# 面向 BESIII 用户的 TF-PWA 使用手册
* 本手册仅面向使用 BESIII 集群的用户
* 原作者——蒋艺的 **Github** 地址:[https://github.com/jiangyi15/tf-pwa](https://github.com/jiangyi15/tf-pwa)
* 我在 **Gitee** 克隆的地址:[https://gitee.com/wwdws1/tf-pwa](https://gitee.com/wwdws1/tf-pwa)
* 我在所内 **GitLab** 克隆的地址(仅 **dev** 分支):[https://code.ihep.ac.cn/wangshi/tf-pwa](https://code.ihep.ac.cn/wangshi/tf-pwa)
* 原作者——蒋艺的完整的用户手册:[https://tf-pwa.readthedocs.io/en/latest/index.html](https://tf-pwa.readthedocs.io/en/latest/index.html)
* 有任何问题和意见,欢迎给我发邮件:[wangshi@ihep.ac.cn](mailto:wangshi@ihep.ac.cn)
* TF-PWA 作者蒋艺的邮箱:[jiangyi15@mails.ucas.ac.cn](mailto:jiangyi15@mails.ucas.ac.cn)
# 目录
[TOC]
# 1. 使用前的准备工作
## 1.1 申请 GPU 账号
在已有集群账号的前提下,申请 Slum GPU 集群账号,详情见 Offline Software 组用户手册的3.2.2节:[链接](http://afsapply.ihep.ac.cn/cchelp/zh/local-cluster/jobs/slurm/#3222-slum-gpu%E9%9B%86%E7%BE%A4%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95)。分波分析申请的是 GPUPWA 组。
## 1.2 准备末态粒子的四动量
* 数据文件每一行都是一个粒子的四动量 `E px py pz`,每m行为一组,表示m个最终粒子。
* 支持纯文本和numpy的数据文件,即 `.txt` 和 `.npy`。纯文本文件推荐精度为12位小数以上。
* 至少需要 data 和带效率的相空间 MC 的四动量文件。
# 2. 安装
## 2.1 无安装使用方法
原作者已经将装好环境的 python 进行了打包,路径为:
```
/hpcfs/others/ucas/jiangy/python3.10.8-cp310-cp310-manylinux2014_x86_64.AppImage
```
直接将其作为安装好的 python3 使用即可, 可以使用环境变量PYTHONPATH指定包的路径。
## 2.2 使用 miniconda 和 pip 安装
### 2.2.0 安装前的准备
把 `$HOME` 路径下的 `.cache` 和 `.local` 两个目录移动到其他的盘再[软链接](https://www.runoob.com/note/29134)回来,以免 `$HOME` 目录超出限额。
### 2.2.1 在集群上安装 miniconda
* ~~mambaforge 已被弃用~~。[miniconda 下载链接](https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh)
* 下载完成之后,使用 bash 运行(如果你是 tcsh,那就换 bash),即:
```
bash Miniconda3-latest-Linux-x86_64.sh
```
最开始是同意协议,yes 就行了。然后是指定安装位置,需要在 /hpcfs 盘,因为 gpu 作业节点没有接其他的盘。例(这里的 `<user>` 是让你填自己的用户名的):
```
/hpcfs/bes/gpupwa/<user>/miniconda3
```
再往后就是安装过程,然后最后会问你是否初始化(`init`),选择初始化(`yes`),然后把初始化的那部分从 `.bashrc` 里拿出来单独放一个文件 `tf.sh`,每次需要这个环境的时候再 `source tf.sh`。因为这一步会比较卡(仅限在集群上,因为磁盘性能差),如果写在 `.bashrc` 里每次登录都会卡一会儿。然后新建一个新的环境:
```
conda create -n tf-pwa
```
进入新环境:
```
conda activate tf-pwa
```
### 2.2.2 下载源代码
找一个~~风水好的~~地方,然后
```
git clone https://github.com/jiangyi15/tf-pwa.git
```
~~然后这时候会有大概率失败,因为集群和 GitHub 的连接时有时无~~。现在集群已经稳定地完全无法连接 GitHub, 请务必使用在最开始写的几个克隆到国内的地址
```
git clone https://code.ihep.ac.cn/wangshi/tf-pwa.git
```
或者
```
git clone https://gitee.com/wwdws1/tf-pwa.git
```
更新方法:在没有作业正在跑的情况下,到克隆下来的 tf-pwa 的路径下,运行
```
git pull
```
### 2.2.3 安装 TensorFlow
你已经建好新的环境并且进了。官方教程在[这里](https://tensorflow.google.cn/install/pip#linux)。
**注:此节教程编写时间为2023年12月12日。**
1. 安装 python3.10
```
conda install python=3.10 -c conda-forge
```
2. 更新 pip(注:这里 `pip` 使用了清华的镜像加速下载)
```
python -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip
```
3. 安装 TensorFlow 和 CUDA
```
python -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple tensorflow[and-cuda]==2.14.1
```
> tensorflow 2.15.0开始依赖cu12,但是集群的显卡驱动太老。最后一个使用cu11的是2.14.1
### 2.2.4 安装其他依赖
然后安装 `uproot`,其作用为:
> Uproot 是一个 Python 库,用于读取和处理 ROOT 文件格式的数据。
安装的命令为:
```
python -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple uproot
```
`cd` 到你克隆下来的 `tf-pwa` 里,运行如下命令安装依赖:
```
python -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -e .
```
对于此句的解释,原作者的解释为:
> (Using `-e`, so it can be updated by `git pull` directly.)
到这里,所有的安装都已经结束,可以进行分波分析了。
# 3. 修改配置文件
配置文件的样板为 `tf-pwa` 路径下的 `config.sample.yml`。下面将分区域进行解释。特别注意,`yaml` 文件中的从属关系是通过缩进确定的。
## 3.1 data
```
## data entry is the data file name list used for fit
data:
## 4-momentum order of final particles in dat files [option]
## the `finals` in `particle` will be used by default.
## it is necessary when dat files has momentum out of final particles
dat_order: [B, C, D]
## basic data files, every line is `E px py pz` as 4-momentum of a particle,
## and every m lines group as m final particls
data: ["data/data4600_new.dat"]
## data weight file, each line for a weight
# data_weight: ["data/data_weight.dat"]
## phase space data for normalize amplitude
phsp: ["data/PHSP4600_new.dat"]
## phase space weight file, each line for a weight
# phsp_weight: ["data/phsp_weight.dat"]
## phase space without efficiencies, used for calculating fit fraction.
# phsp_noeff: ["data/PHSP4600_noeff.dat"]
## much more phase space, used for plotting
# phsp_plot: ["data/PHSP4600_plot.dat"]
## optional weight background data, need bg_weight
bg: ["data/bg4600_new.dat"]
## background weight
bg_weight: 0.731
## inject MC in data
# inmc: ["data/inMC.dat"]
# inject_ratio: 0.01
# float_inmc_ratio_in_pdf: False
## using total monumtum direction as initial axis (or labrary axis)
# random_z: True
## whether boost data to center mass frame first
# center_mass: True
# cached data file
# cached_data: "data/all_data.npy"
# data_charge: ["data/data4600_cc.dat"] # charge conjugation condition same as weight
# cp_trans: True # when used charge conjugation as above, this do p -> -p for charge conjugation process.
```
我们对该部分进行逐行讲解
### 3.1.1 `dat_order: [B, C, D]`
这里指定的是你输入文件中对应的的粒子的顺序,有几个写几个,可以用更明显的名字代替,需要和decay和particle部分的配置一致。比如 $J/\psi\to\pi^+\pi^-\pi^0$,这里你可以写成 `dat_order: [pip, pim, piz]` 用以明显区分。
### 3.1.2 `data: ["data/data4600_new.dat"]`
这里指定的是输入的 data 的四动量文件的路径,`.dat` 文件是纯文本文件,同样支持输入 `.npy` 文件。可以多个 config 文件使用同一个四动量文件用以节省磁盘。建议写成绝对路径避免出错。
### 3.1.3 `phsp: ["data/PHSP4600_new.dat"]`
这里指定的是输入的相空间 MC 的四动量文件的路径。使用 truth 或 reconstructed 均可,但是需要带效率。
### 3.1.4 `bg: ["data/bg4600_new.dat"]` (optional)
这里指定的是输入的本底的四动量文件的路径。目前只支持输入一个文件,所以有多个本底的需合成一个文件。**可以没有本底。**
### 3.1.5 `bg_weight: 0.731` (optional)
这里指定的是输入的本底的权重。如果输入本底的每个事例的 weight 一样,这里使用浮点数即可。如果每个事例有不同的 weight,则需要准备一个文件存入 weight,并与本底事例一一对应,并且输入的是一个文件。纯文本文件和 `.npy` 文件均可。
### 3.1.6 `random_z: True`
该选项默认为 `True` 且注释,但是对于 BESIII 探测器的数据需要将该项取消注释并且修改为 `False`。(这是因为初态粒子直接从正负电子出来,极化的方向固定,如果不是正负电子直接产生的,不需要`False`)
### 3.1.7 `center_mass: True`
分波分析需要输入的四动量在母粒子静止系下,但是对于我们正常取得的数据则为实验室系下,存在一个沿着 $x$ 轴的分量。你可以选择输出四动量的时候就进行 boost,也可以输出实验室系下的四动量,在这里打开这个选项。建议取消注释。
### 3.1.8 其他额外选项
TBD
## 3.2 decay
```
## `decay` describe the decay structure, each node can be a list of particle name
decay:
## each entry is a list of direct decay particle, or a list or the list.
## `dec_file` entry for loading `*.dec` files will be supported in the feature.
## `-` is often used for list in yaml
A:
## for example A -> R_CD + B, R_CD -> C + D
## the optional `key: value` mean option parameters of the decay
## note there, R1 is overrated by `particle` entry as R1 and R11
- [R_CD, B, model: default]
- - R_BD
- C
- model: "default"
- - R_BC
- D
## set l_list to allow only l=0
# - [ NR(BC)SP, D, curve_style: "g-", {has_barrier_factor: False, l_list: [0]}]
# - [R_BC2, D, p_break: True] # allow P parity violate
# R_BC2: [B, C, c_break: False] # enable C parity select C=(-1)^(l+s)
R_CD:
- C
- D
R_BD: [B, D]
R_BC: [B, C]
## set l_list to allow only l=1
# NR(BC)SP: [B, C, {has_barrier_factor: False, l_list: [1]}]
```
### 3.2.1 综述
在这一节内,你需要指定从母粒子经过多级衰变到你的输入末态的衰变链。并且这里仅支持两体衰变,相空间过程需要通过共振态模拟来实现。
### 3.2.2 第一级衰变
以这里的写法,衰变为 $A\to B+C+D$,这里有三种可能的情况:
1. $A\to R\_BC+D, R\_BC\to B+C$
2. $A\to R\_BD+C, R\_BD\to B+D$
3. $A\to R\_CD+B, R\_CD\to C+D$
这里的 $R\_BC$,$R\_BD$ 和 $R\_CD$ 指的是衰变到 $B+C$,$B+D$ 和 $C+D$的共振态(Resonance)。$R\_BC$,$R\_BD$ 和 $R\_CD$ 可以为各种粒子,这里只是每个衰变链上的总称,每个共振态的具体粒子在下面指定。
两种写法均可:
```
- [R_CD, B, model: default]
```
和
```
- - R_CD
- B
- model: "default"
```
两种写法是一个意思。这里的 `default` 带不带双引号都可以,使用默认 model 的时候 `model: default` 可以不写。
### 3.2.3 第二级衰变
由于这里的例子是三体末态,最多只有两级衰变。这里以 $R\_BC$ 的衰变举例:$R\_BC\to B+C$ 对应的写法为:
```
R_BC: [B, C]
```
或
```
R_BC:
- B
- C
```
均可。同样的,使用默认的 model 的时候可以不写。
### 3.2.4 其他选项
TBD
## 3.3 particle
```
## `particle` describe the particle included
## top, finals, include are used as keywords
particle:
## top decay particle
$top:
A:
J: 1 # spin, use 1/2 or 0.5 for half spin
P: -1 # parity, alias: `Par`
mass: 4.6
# spins: [-1, 1] # list of possible of spin-projected quantum numbers
# polarization: "vector" # possible polarization option
## final particles
$finals:
B:
J: 1
P: -1
mass: 2.1
C:
J: 1
P: -1
mass: 1.8
D:
J: 0
P: -1
mass: 0.1
## map for possible resonances in the same position
## each entry is a list of resonances id [option]
## include detailed parameters defined in `Resonances.yml`
$include: Resonances.sample.yml
R_BD:
- D2_2460p
- D1_2430p
- D1_2420p
R_BC: [Zc_4025, Zc_4160]
## set `[]` for particle which is not in real decays,
## just used for calculating angle data.
R_CD: []
## particle can be defined in here or included by `Resonances.yml`
## if both have the defination, defination in here will overtite defination in `Resonances.yml`
# NR(BC)SP: {J: 1, P: -1, model: one}
```
### 3.3.1 初、末态粒子定义
这里可以看到两类:`$top` 对应的是母粒子,`$final` 对应的是末态粒子。其中几个重要的参数为:
1. `J`:对应粒子的自旋,半整数可以使用分数(`1/2`)或者小数(`0.5`)来表示。
2. `P`:对应粒子的 P 宇称,正的就是 `+1`,负的就是 `-1`。
3. `mass`:对应粒子的质量,单位为 $\mathrm{GeV}/c^2$。
4. `spins` (optional):对应粒子自旋可用的投影,对于 BESIII 实验,$J/\psi$ 粒子是通过虚光子而来,所以自旋的投影不能为 $0$。因此需要在这里指定其自旋的投影只有 $-1$ 和 $1$。
### 3.3.2 中间共振态粒子选择
对于中间共振态,即 $R\_BC$,$R\_BD$ 和 $R\_CD$,他们所对应的粒子的定义在 `$include` 这一部分。这些中间共振态的具体定义可以写在这里,也可以引用写在 `Resonance.sample.yml` 中的粒子。这里有几个注意事项:
1. 如果在某一个质量谱上你没有放共振态,但是又想画它的质量分布,这里就需要写成 `[]`。例如:
```
R_BC: []
```
2. 对于在这里和 `Resonance.sample.yml` 中都定义过的粒子,这里的优先级是高于 `Resonance.sample.yml` 的。
### 3.3.3 中间共振态粒子的定义
这里以 `Resonance.sample.yml` 中的粒子举例:
```
## Resonance configure list
## yaml format version , see more: https://yaml.org/
## required pyyaml module
## head resonance
## resonance name as id
## quotation marks are option
## head resonance will has head fixed to 1.0
D2_2460:
## spin [required]
J: 2
## parity (alias `Par`) [required]
P: 1
## resonance mass (alias `mass`) : m_0 [required]
m0: 2.4607
m_max: 2.4611
m_min: 2.4603
## resonance width (alias `width`): \Gamma_0 [required]
g0: 0.0475
g_max: 0.0486
g_min: 0.0464
## set mass and width (gamma0) as variables, and float in fit
# float: mg
## add gaussian constraint term (e.g. -ln L -> -lm [L * Gauss(m0; mean, sigma)]
# gauss_constr:
## the sigma of mass, mean is m0
# mass: 0.001
# width: 0.002
## Breit Wigner formula [option]
model: default
## whether used in decay
disable: False
Ds1_2700_2860:
J: 1
P: -1
params:
mass1: 2.7083
mass2: 2.859
width1: 0.12
width2: 0.159
mass1_range: [2.7, 2.72]
mass1_sigma: 0.008
mass1_free: True
mass1_constr: False
m0: 2.83
model: Kmatrix
## other resonances
D2_2460p:
J: 2
P: 1
## coef_head make D2_2460p to use the params of D2_2460 by the same name, (only works in simple cases)
## only the total phase angle different
coef_head: "D2_2460"
g0: 0.0467
g_max: 0.0479
g_min: 0.0455
m0: 2.4654
m_max: 2.4667
m_min: 2.4644
## or you can used `{}` to avoid indent, use `,` to distinguish items.
D1_2420:
{
J: 1,
Par: 1,
g0: 0.0317,
g_max: 0.0342,
g_min: 0.0292,
m0: 2.4208,
m_max: 2.4213,
m_min: 2.4203,
}
D1_2420p:
{
J: 1,
Par: 1,
coef_head: "D1_2420",
g0: 0.025,
g_max: 0.021,
g_min: 0.019,
m0: 2.4232,
m_max: 2.4256,
m_min: 2.4208,
}
D1_2430:
{
J: 1,
Par: 1,
g0: 0.384,
g_max: 0.514,
g_min: 0.274,
m0: 2.427,
m_max: 2.467,
m_min: 2.387,
}
D1_2430p:
{
J: 1,
Par: 1,
coef_head: "D1_2430",
g0: 0.384,
g_max: 0.514,
g_min: 0.274,
m0: 2.427,
m_max: 2.467,
m_min: 2.387,
}
# D0_2550: {
# J: 0, Par: -1,
# m0: 2.518,
# g0: 0.199,
# }
# D0_2550p: {
# J: 0, Par: -1,
# coef_head: "D0_2550",
# m0: 2.518,
# g0: 0.199,
# }
# D1_2600: {
# J: 1, Par: -1,
# m0: 2.6419,
# g0: 0.149,
# }
# D1_2600p: {
# J: 1, Par: -1,
# coef_head: "D1_2600",
# m0: 2.6419,
# g0: 0.149,
# }
Zc_4025: { J: 1, Par: 1, g0: 0.0248, m0: 4.0263 }
Zc_4160: { J: 1, Par: 1, g0: 0.0921485, m0: 4.17329 }
Zc_4210:
J: 1
P: 1
m0: 4.21
g0: 0.11
## trans model examples
model:
expr: a * BWR
where:
a: -1.0
Kmatrix_S:
J: 1
P: 1
## most model use m0 to calulate `q0` in barrier factor,
m0: 4.21
## KmatrixSingleChannel paramters
model: KMatrixSingleChannel
mass_list: [4.21, 4.31]
width_list: [0.03, 0.06]
```
这里我们分条讲解:
1. `J`:自旋。
2. `P`:P 宇称。
3. `m0`:质量初值,亦可写作 `mass`,单位 $\mathrm{GeV}/c^2$。
4. `m_max` 和 `m_min` (optional):质量变化范围的上限和下限。
5. `g0`:宽度初值,亦可写作 `width`,单位 $\mathrm{GeV}$。
6. `g_max` 和 `g_min` (optional):宽度变化范围的上限和下限。
7. `float: mg` (optional):放开质量和宽度作为参数拟合,可以只写 `m` 或 `g`,用以只放开质量或宽度。
8. `gauss_constr` (optional):高斯约束,指定的是质量或宽度的误差。
9. `coef_head` (optional):固定参数与另一个共振态相同,让这两个共振态只有相角不同。
## 3.4 约束
```
## constrains for params [WIP]
constrains:
# particle:
# equal:
# mass: [[D1_2430p, D1_2430]]
## fix the first decay chain total to 1.0
decay: { fix_chain_idx: 0, fix_chain_val: 1 }
# fix_var:
# "A->Zc_4025.DZc_4025->B.C_total_0r": 1.0
# var_range:
# "A->Zc_4025.DZc_4025->B.C_total_0r": [1.0, null]
# var_equal:
# - ["A->D2_2460p.CD2_2460p->B.D_total_0r", "A->D2_2460.BD2_2460->C.D_total_0r"]
```
1. 指定两个共振态的质量相同。
2. 指定第 0 个衰变链的 `total` 为 $1+0i$,这里是为了方便其他衰变链向其归一。
3. 固定某个参数为定值。
4. 指定某个参数的范围。
5. 指定某两个参数相同。
## 3.5 画图
```
## plot describe the configuration of plotting 1-d data distribution
plot:
## plot configuration TODO
config:
bins: 50
# legend_outside: True
## Make the legend outside the plot, default is False (inside)
## invariant mass
mass:
R_CD:
## label of display
## use `$$` for latex math
display: "$M_{CD}$"
# upper_ylim: 200
# trans: x*x
R_BD:
display: "$M_{BD}$"
R_BC:
display: "$M_{BC}$"
## helicity angle
angle:
## If it named as `[.../]A/R_BC`, it refer to the decay A -> R_BC ... in the decay chain has A and R_BC,
## the angle is determined by the last 2 particles, the other order is random.
## for example, D->E+F in [A->R_BC+D, D->E+F, R_BC->B+C] is `R_BC/D/E` or `A/R_BC/D/E` or `R_BC/A/D/E`
A/R_CD:
## include "alpha, cos(beta), gamma", # add `cos` for cos(beta)
alpha:
display: "$\\phi_{1}$"
cos(beta):
display: "$\\cos(\\theta_1)$"
bins: 50 # number of bins is 50
legend: False # do not plot the legend
range: [-1, 1] # range for plot
A/R_BD:
alpha:
display: "$\\phi_{2}$"
cos(beta):
display: "$\\cos(\\theta_2)$"
A/R_BC:
alpha:
display: "$\\phi_{3}$"
cos(beta):
display: "$\\cos(\\theta_3)$"
R_BC/B:
alpha:
display: "$\\phi_{11}$"
cos(beta):
display: "$\\cos(\\theta_{11})$"
R_CD/C:
alpha:
display: "$\\phi_{21}$"
cos(beta):
display: "$\\cos(\\theta_{21})$"
R_BD/B:
alpha:
display: "$\\phi_{31}$"
cos(beta):
display: "$\\cos(\\theta_{31})$"
## 2D plot
2Dplot:
m_R_CD & m_R_BC:
display: "$M_{CD}$ vs $M_{BC}$"
plot_figs: ["data", "sideband", "fitted"]
dalitz_12:
x: m_R_CD**2
y: m_R_BC**2
display: "$M_{CD}^2$ vs $M_{BC}^2$"
plot_figs: ["data", "sideband", "fitted"]
```
### 3.5.1 config
这里指定的参数适用于各个一维分布。优先级为:每个图单独的设置 > 总的设置 > 默认。常用参数如下:
* `bins` (type=`int`):设置分 bin 数,默认是 50。
* `legend` (type=`bool`):是否画图例,质量谱默认画,角分布默认不画。
* `legend_outside` (type=`bool`):画图例的话,是在图内还是在图外,默认是图内。示意图如下:
| <center> ![legend_outside=False](https://note.ihep.ac.cn/uploads/a67cdd2e-cb3f-4a5d-8206-13abcf77beab.png) </center> | <center> ![legend_outside=True](https://note.ihep.ac.cn/uploads/494a7a08-0fa4-4590-bd46-3102b68c194b.png) </center> |
| -------- | -------- |
| <center>`legend_outside=False` </center> | <center> `legend_outside=True` </center> |
### 3.5.2 mass
这部分是画各个质量谱的,即你前面定义个各种 `R_*`。选项有:
* `display` (type=`"str"`):图和 x 轴的 title。支持 latex 语法,需要用两个英文的 `$`,并且其中的反斜线命令需要两个反斜线。比如 $\Lambda$ 在这里需要写成 `$\\Lambda$`。
* `upper_ylim` (type=`int`):y 轴的上限。
* `range` (type=`[double, double]`):指定画图的范围,如果不指定则自动获取。
### 3.5.3 angle
画角分布的时候比质量谱多了一点就是指定参考系。例如 `A/R_BD` 指在 $A$ 的静止系下画 $R\_BD$ 的角分布,这里的 alpha 和 beta 对应的是球坐标系下的 $\phi$ 角和 $\theta$ 角。还支持画角分布的余弦 `cos()`。角分布默认不画 legend。
### 3.5.4 2Dplot
支持两种画图方式:
* `x & y`:此种方法画的是散点图。x 和 y 是一维图的文件名,对于质量谱来说就是 `m_XXX`,角分布是 `XX_YY_ZZ`。如 `m_R_BC` 和 `A_R_BC_cos(beta)`。
* `XXXXX`:这里 XXXXX 是保存的文件名,下面的 x 和 y 是指定每一维画的什么。图的名字的规则和前面的一样,但是支持平方,即可以画 dalitz 图。
这里的 `plot_figs` 支持的如下:
* `"data"`, `"sideband"`:对应 root 中二维图的 `SCAT`。
* `"data_hist"`, `"sideband_hist"`, `"fitted"`:对应 root 中二维图的 `COLZ`。
| <center> ![SCAT](https://note.ihep.ac.cn/uploads/4a40afe5-0c9e-4070-a74f-ae2c7e6c1655.png) </center> | <center> ![COLZ](https://note.ihep.ac.cn/uploads/e4b40ce5-dc4b-40a1-ad20-a92c38c590e7.png) </center> |
| -------- | -------- |
| <center> **data** </center> | <center> **data_hist** </center> |
# 4. 提交作业
## 4.1 准备作业运行脚本
这里使用计算中心提供的模板修改而来:
```#! /bin/bash
######## Part 1 #########
# Script parameters #
#########################
# Specify the partition name from which resources will be allocated, mandatory option
#SBATCH --partition=gpu
# Specify the QOS, mandatory option
#SBATCH --qos=pwanormal
# Specify which group you belong to, mandatory option
# This is for the accounting, so if you belong to many group,
# write the experiment which will pay for your resource consumption
#SBATCH --account=gpupwa
# Specify your job name, optional option, but strongly recommand to specify some name
#SBATCH --job-name=<name>
# Specify how many cores you will need, default is one if not specified
#SBATCH --ntasks=1
# Specify the output file path of your job
# Attention!! Your afs account must have write access to the path
# Or the job will be FAILED!
#SBATCH --output=/hpcfs/bes/gpupwa/<user>/<dir>/<script_name>-%j.out
# Specify memory to use, or slurm will allocate all available memory in MB
#SBATCH --mem-per-cpu=16384
# Specify how many GPU cards to use
#SBATCH --gres=gpu:v100:1
######## Part 2 ######
# Script workload #
######################
# Replace the following lines with your real workload
# list the allocated hosts
srun -l hostname
# list the GPU cards of the host
/usr/bin/nvidia-smi -L
python fit.py -c config.yml
```
第一部分为作业配置,每一个选项分别对应:
1. `#SBATCH --partition=gpu`:资源分区。gpupwa 组可用的资源区有两个,分别为 `gpu` 和 `gpupwa`。
2. `#SBATCH --qos=pwanormal`:队列。gpupwa 组 gpu 分区可用的 QOS 有 `pwanormal` 和 `debug`,gpupwa 组 gpupwa 分区可用的 QOS 有 `pwadedicate` 和 `pwadebug`。具体区别详见[集群资源、队列和组别说明](http://afsapply.ihep.ac.cn/cchelp/zh/local-cluster/jobs/slurm/#%E9%9B%86%E7%BE%A4%E8%B5%84%E6%BA%90%E3%80%81%E9%98%9F%E5%88%97%E5%92%8C%E7%BB%84%E5%88%AB%E8%AF%B4%E6%98%8E)。
3. `#SBATCH --account=gpupwa`:组别。你就是 gpupwa 组的,这个不改。
4. `#SBATCH --job-name=<name>`:作业名。自己起个名改了就行了。
5. `#SBATCH --ntasks=1`:申请的 CPU 核心数。1 个够了,并且建议用 1 个。
6. `#SBATCH --output=/hpcfs/bes/gpupwa/<user>/<dir>/<script_name>-%j.out`:输出 log 文件绝对路径。这里的路径一定要是已经存在的,否则运行失败。其中的 `%j` 会被替换成作业的号。
7. `#SBATCH --mem-per-cpu=16384`:每个 CPU 核心数对应的内存大小,单位 MB。
8. `#SBATCH --gres=gpu:v100:1`:申请的 GPU 名称和数量。每个资源分区对应的 GPU 名称和数量详见[集群资源、队列和组别说明](http://afsapply.ihep.ac.cn/cchelp/zh/local-cluster/jobs/slurm/#%E9%9B%86%E7%BE%A4%E8%B5%84%E6%BA%90%E3%80%81%E9%98%9F%E5%88%97%E5%92%8C%E7%BB%84%E5%88%AB%E8%AF%B4%E6%98%8E)。
第二部分是运行区域,可以理解为 `bash` 脚本。
## 4.2 提交作业命令
```
# command to submit a job
$ sbatch <job_script.sh>
# <job_script.sh> is the name of the script,e.g: v100_test.sh, then the command is:
$ sbatch v100_test.sh
# There will be a jobid returned as a message if the job is submitted successfully
```
## 4.3 查询作业
```
# command to check the job queue
$ squeue
# command to check the jobs submitted by user
$ sacct
```
## 4.4 取消作业
```
# command to cancel the job
$ scancel <jobid>
# <jobid> can be found using the command sacct
```
# 5. 查看拟合结果
## 5.1 log
很多的信息都会保存在 log 中,可以自行查看。
## 5.2 图
程序会根据 `config.yml` 里的设置进行画图,保存在提交作业所在目录下的 `figure` 目录内。
## 5.3 参数
拟合中的各种参数,保存在 `final_params.json` 文件中。如果你放开了共振态的质量宽度,则结果也在里面。第一部分为中心值,第二部分为误差,第三部分为拟合状态,NLL(negative log likelihood) 和自由度。
## 5.4 共振态占比 (fit fraction)
中心值保存在 `fit_frac.csv` 中。其中对角元为某个共振态自己的占比,非对角元为行列对应共振态之间的干涉。误差则保存在 `fit_frac_err.csv` 中。
# 6. 进阶
本节将按照不同的目标进行讲解。
## 6.1 产生无效率的 PHSP 样本
```
import tensorflow as tf
from tf_pwa.config_loader import ConfigLoader
def main():
config = ConfigLoader("/hpcfs/bes/mlgpu/wangshi/tfpwa/pksp/pwa/all/config.yml")
N = 2000000
with tf.device("/device:GPU:0"):
phsp = config.generate_phsp(N)
config.data.savetxt("./data/phsp_noeff.npy", phsp)
if __name__ == "__main__":
main()
```
其中 `config.yml`、事例数和 `.npy` 文件的名字和位置可以修改。
## 6.2 基于分波结果产生 toy MC 样本
下面以 $\psi(3686)\to pK^0_S\bar{\Sigma}^-$ 为例说明如何产生 toy MC 样本
```
import csv
import json
import time
from pprint import pprint
import matplotlib
# avoid using Xwindow
matplotlib.use("agg")
import numpy as np
import tensorflow as tf
# examples of custom particle model
from tf_pwa.amp import simple_resonance
from tf_pwa.config_loader import ConfigLoader, MultiConfig
from tf_pwa.experimental import extra_amp, extra_data
from tf_pwa.utils import error_print, tuple_table
devices = "/device:GPU:0"
num_data = 10000000
final_paricle_num = 3
lb = ["pp", "pm"]
resons = ["all", "R1", "R2", "R3", "R4", "R5"]
particles_num = np.array([2212, 310, 3222], dtype=int) # p, K_S0, Sigma+
anti_params = np.array([1, 0, -1], dtype=int) # p, K_S0, anti-Sigma-
config_file = "config.yml"
def main():
for reson in resons:
for i in range(len(lb)):
final_paras = "final_params_" + str(reson) + ".json"
data_dat = "data/data_" + str(lb[i]) + "_" + str(reson) + ".npy"
pdata_dat = "data/pdata_" + str(lb[i]) + "_" + str(reson) + ".dat"
config = ConfigLoader(config_file)
print("Loaded parameter file: " + final_paras)
config.set_params(final_paras)
with tf.device(devices):
data = config.generate_toy(num_data)
config.data.savetxt(data_dat, data)
print("Saved dat file: " + data_dat)
p4 = np.load(data_dat)
print("Loaded dat file: " + data_dat)
f1 = open(pdata_dat, "w")
for j in range(num_data):
f1.write("%d\n" % final_paricle_num)
for k in range(final_paricle_num):
if anti_params[k] == 0:
particle_num = int(particles_num[k])
else:
particle_num = int(
(-1) ** (i + 2) * anti_params[k] * particles_num[k]
)
f1.write(
str(particle_num)
+ " "
+ str("%.12f" % p4[j][k][1])
+ " "
+ str("%.12f" % p4[j][k][2])
+ " "
+ str("%.12f" % p4[j][k][3])
+ " "
+ str("%.12f" % p4[j][k][0])
+ "\n"
)
f1.close()
print("Wirted pdat file: " + pdata_dat)
if __name__ == "__main__":
main()
```
这里是基于你拟合的 `config.yml` 文件,以及拟合结果的 `final_params.json` 文件,产生无效率的 toy MC truth,`num_data` 是你要产生的事例数。
修改好了之后交 GPU 作业,会产生 `.npy` 和 `.dat` 文件。其中 `.npy` 文件是纯四动量,三维矩阵:事例数,粒子数,$E,p_x,p_y,p_z$。注意这里产生的四动量是母粒子静止系下的,BesEvtGen 在模拟的时候会自动 boost。`.dat` 文件是用于 BesEvtGen 模拟是使用的,文件内的格式为:
```
······
3
2212 -0.657680440696 0.252044740774 0.505645335494 1.277537408514
-321 0.143385615443 -0.327185941150 0.480461257571 0.775996182438
-3212 0.514294825253 0.075141200376 -0.986106593065 1.632466391790
······
```
这里具体的格式详见 `$BESEVTGENROOT/src/EvtGen/EvtGenModels/EvtTrackGen.cc` 文件开头的注释部分。对于如何使用四动量产生 toy MC,BesEvtGen 会在提供的四动量中抽样。boss 作业的修改方法如下:
* 修改 decay card
产生相空间 MC 的是这样
```
Decay psi(2S)
1.0000 p+ K- anti-Sigma0 PHSP;
Enddecay
······
```
修改后是这样
```
Decay psi(2S)
1.0000 p+ K- anti-Sigma0 TrackGen 0;
Enddecay
······
```
* 修改模拟的 jobOption
默认的里面关于 EvtGen 的是这样
```
//*************job options for EvtGen***************
#include "$BESEVTGENROOT/share/BesEvtGen.txt"
EvtDecay.userDecayTableName = "decay.dec";
```
修改之后是这样
```
//*************job options for EvtGen***************
#include "$BESEVTGENROOT/share/BesEvtGen.txt"
EvtDecay.userDecayTableName = "decay.dec";
EvtDecay.FileForTrackGen={"pdata.dat"};
```
**注意:HTCondor 节点没有挂载 `/hpcfs`,所以四动量文件需要复制到非 `/hpcfs` 盘后方可调用。**
## 6.3 质量分辨
关于考虑质量分辨的原理,详见蒋艺的手册:[Resolution in Partial Wave Analysis](https://tf-pwa.readthedocs.io/en/latest/resolution.html)。
关于具体实现,这里分为几个部分:
1. 将 data 扩大 n 倍
2. 将 background 同样扩大 n 倍
3. 画图的时候,MC 需要带分辨的样本,即重建值
这里用到三个新的脚本产生上述 1 和 2 步的扩大后的样本:
1. `detector.yml`:这里面写的是你的分辨和偏移,单位 $\mathrm{GeV}/c^2$
```
sigma: 0.001951045
bias: 0.0
```
2. `detector.py`:这个是产生新样本的时候调用的。这里需要修改三个 `Define` 的部分,分别是共振态的名字,相空间上限和下限的约束以及 `config.yml` 文件的路径。**注意**,其中trans_function(m1,m2)需要自己定义,这里是简单高斯加上一个线性效率以及范围截断。只考虑高斯分辨可以把eff处的改为
`eff=np.where((m2<mass_max_cut)&(m2>mass_min_cut),1.,0.)*phsp_factor(m1)`. 对于一般情况trans_function返回值是相空间MC中truth(m1)和rec(m2的二维分布概率密度。一般的做法是按照m2分bin,每个bin里得到一个m1的分布,这样总的分布就是m2本身的分布乘以每个bin里的m1的分布。
```
"""
Example of detector model
"""
import numpy as np
import tensorflow as tf
import yaml
from tf_pwa.config_loader import ConfigLoader
from tf_pwa.data import data_mask
from tf_pwa.data_trans.helicity_angle import HelicityAngle
# Define decay chain to be smeared
resonance = "R_CD"
# Define the mass of each particle, in GeV, order by decay chain
mass_mother = 3.097
mass_one = 1.192642
mass_two = 0.134977
mass_another = 1.115683
mass_min_cut = mass_one + mass_two
mass_max_cut = mass_mother - mass_another
# Define the config.yml file
config_file = "/hpcfs/bes/mlgpu/wangshi/tfpwa/jlsp/pwa/all/config.yml"
config = ConfigLoader(config_file)
decay_chain = config.get_decay(False).get_decay_chain(resonance)
ha = HelicityAngle(decay_chain)
# load detector model parameters
with open("detector.yml") as f:
detector_config = yaml.safe_load(f)
def detector(data):
"""
run detector for data
"""
ms, costheta, phi = ha.find_variable(data)
p_map = {str(i): i for i in ms.keys()}
m = ms[p_map[resonance]]
# smear mass
new_m = (
m
+ tf.random.normal(shape=m.shape, dtype=m.dtype) * detector_config["sigma"]
+ detector_config["bias"]
)
# selected with effecency, eff(m) = m
# weight = m
# max_w = 2.0
# cut = max_w * tf.random.uniform(shape=m.shape, dtype=m.dtype) < weight
# selected in reconstructed value, a mass cut
# cut = (cut) & (new_m < mass_max_cut) & (new_m > mass_min_cut)
cut = (new_m < mass_max_cut) & (new_m > mass_min_cut)
toy_truth = data_mask(data, cut)
ms[p_map[resonance]] = new_m
toy_rec = ha.build_data(*data_mask((ms, costheta, phi), cut))
toy_rec = config.data.cal_angle(toy_rec)
return toy_truth, toy_rec
def relative_p(m0, m1, m2):
"""breakup monmentum"""
s2 = (m0**2 - (m1 + m2) ** 2) * (m0**2 - (m1 - m2) ** 2)
s2 = np.where(s2 < 0, 0, s2)
return np.sqrt(s2) / 2 / m0
def phsp_factor(m):
"""phase space factor"""
p1 = relative_p(mass_mother, m, mass_another) # A -> R_CD + B
p2 = relative_p(m, mass_one, mass_two) # R_CD -> C + D
return p1 * p2
def gauss(delta, sigma):
"""simple gauss function without normalisation"""
return np.exp(-(delta**2) / 2 / sigma**2)
def trans_function(m1, m2):
"""
transisation function
"""
delta = m2 - m1 - detector_config["bias"]
r1 = gauss(delta, sigma=detector_config["sigma"])
eff = np.where((m2 > mass_min_cut) & (m2 < mass_max_cut), 1.0, 0.0) * phsp_factor(
m1
)
return r1 * eff
def rec_function(m2):
"""pdf of reconstructed value"""
ret = 0
N = 10000
m1 = np.linspace(mass_min_cut, mass_max_cut, N)
ret = trans_function(m1[:, None], m2)
return np.mean(ret, axis=0)
def truth_function(m1):
"""pdf of truth value"""
ret = 0
N = 10000
m2 = np.linspace(mass_min_cut, mass_max_cut, N)
ret = trans_function(m1[:, None], m2)
return np.mean(ret, axis=1)
def delta_function(delta):
"""pdf of reconstructed value - truth value"""
N = 10000
ms_min = mass_min_cut * 2 + np.abs(delta) # np.where(delta<0, 0.2+delta, 0.2-delta)
ms_max = mass_max_cut * 2 - np.abs(delta) # np.where(delta<0, 1.9+delta, 1.9-delta)
# print(delta, ms_min, ms_max)
ms = (
np.linspace(0.0 + 1 / 2 / N, 1.0 - 1 / 2 / N, N)[:, None] * (ms_max - ms_min)
+ ms_min
)
# print(ms, (ms-delta)/2, (ms+delta)/2)
ret = trans_function((ms - delta) / 2, (ms + delta) / 2)
return np.mean(ret, axis=0) * (ms_max - ms_min)
def run(name):
"""run detector for data sample"""
toy = config.data.load_data("data/" + name + ".dat")
toy_truth, toy_rec = detector(toy)
toy_rec.savetxt("data/" + name + "_rec.dat", config.get_dat_order())
toy_truth.savetxt("data/" + name + "_truth.dat", config.get_dat_order())
if __name__ == "__main__":
# run("toy")
run("phsp")
# run("phsp_plot")
```
3. `sample.py`:产生扩大后样本的脚本。其中需要修改的部分仍为三个 `Define` 的部分,分别为本底的 weight,共振态的名字(和 `detector.py` 中一致)以及 `config.yml` 文件的路径(和 `detector.py` 中一致)。这里是通过trans_function均匀取点,也可以改成不均匀的取点,提高精度。
```
import numpy as np
import tensorflow as tf
import yaml
from detector import trans_function
from scipy.stats import norm
from tf_pwa.config_loader import ConfigLoader
from tf_pwa.data_trans.helicity_angle import (
HelicityAngle,
HelicityAngle1,
generate_p,
)
# loda detector model parameters
with open("detector.yml") as f:
detector_config = yaml.safe_load(f)
# Define the total weight of background
bg_weight_total = 1.0
# Define decay chain to be smeared
resonance = "R_CD"
# Define the config.yml file
config_file = "/hpcfs/bes/mlgpu/wangshi/tfpwa/jlsp/pwa/all/config.yml"
def smear_function(m, m_min, m_max, i, N):
"""generate mass of truth sample and weight"""
delta_min = m_min - m
delta_max = m_max - m
sigma = detector_config["sigma"]
bias = detector_config["bias"]
delta_min = np.max([delta_min, -5 * sigma * np.ones_like(delta_min) - bias], axis=0)
delta_max = np.min([delta_max, 5 * sigma * np.ones_like(delta_max) - bias], axis=0)
delta = (
np.linspace(1 / N / 2, 1 - 1 / N / 2, N)[i] * (delta_max - delta_min)
+ delta_min
)
w = trans_function(m + delta, m)
return m + delta, w
def smear(toy, decay_chain, name, function, idx, N):
"""generate full truth sample and weight"""
ha = HelicityAngle(decay_chain)
ms, costheta, phi = ha.find_variable(toy)
m_min = 0
m_max = np.inf
par = None
for i in decay_chain:
if str(i.core) == str(name):
m_min = ms[i.outs[0]] + ms[i.outs[1]]
par = i.core
if str(name) in [str(j) for j in i.outs]:
m_max = ms[i.core]
for j in i.outs:
if str(j) == str(name):
continue
m_max = m_max - ms[j]
m_smear, w = function(ms[par], m_min, m_max, idx, N)
# print("smear", idx, m_smear, ms[par], m_smear-ms[par], w)
# exit()
ms[par] = m_smear
return ha, ha.build_data(ms, costheta, phi), w
def random_sample(config, decay_chain, toy):
"""generate total truth sample and weight"""
all_p4 = []
ws = []
for i in range(config.resolution_size):
# decay_chain = [i for i in toy["decay"].keys() if "(p+, p-, pi+, pi-)" in str(i)][0] # config.get_decay(False).get_decay_chain("(B, C)")
ha, toy_smear, w = smear(
toy, decay_chain, resonance, smear_function, i, config.resolution_size
)
ws.append(w)
# print(toy_smear)
# toy_smear = {i: j for i,j in zip(ha.par, toy_smear)}
# print(toy_smear)
p4 = [
toy_smear[i].numpy() for i in config.get_dat_order()
] # (particle, idx, mu)
all_p4.append(np.stack(p4)) # (resolution, particle, idx, mu)
ws = np.stack(ws)
ws = ws / np.sum(ws, axis=0)
return np.stack(all_p4).transpose((2, 0, 1, 3)), ws
def random_sample_bg(config, decay_chain, toy):
"""generate total truth sample and weight"""
all_p4 = []
ws = []
for i in range(config.resolution_size):
# decay_chain = [i for i in toy["decay"].keys() if "(p+, p-, pi+, pi-)" in str(i)][0] # config.get_decay(False).get_decay_chain("(B, C)")
ha, toy_smear, w = smear(
toy, decay_chain, resonance, smear_function, i, config.resolution_size
)
ws.append(w)
# print(toy_smear)
# toy_smear = {i: j for i,j in zip(ha.par, toy_smear)}
# print(toy_smear)
p4 = [
toy_smear[i].numpy() for i in config.get_dat_order()
] # (particle, idx, mu)
all_p4.append(np.stack(p4)) # (resolution, particle, idx, mu)
ws = np.stack(ws)
ws = ws / np.sum(ws, axis=0) * bg_weight_total
return np.stack(all_p4).transpose((2, 0, 1, 3)), ws
def main():
config = ConfigLoader(config_file)
decay_chain = config.get_decay(False).get_decay_chain(resonance)
toy = config.get_data("data_rec")[0]
# print(toy)
# exit()
# ha = HelicityAngle(decay_chain)
# ms, costheta, phi = ha.find_variable(toy)
# dat = ha.build_data(ms, costheta, phi)
# print(var)
# print(ha.build_data(*var))
p4, w = random_sample(config, decay_chain, toy)
# exit()
np.save("data/data.npy", np.stack(p4).reshape((-1, 4)))
np.savetxt(
"data/data_w.dat", np.transpose(w).reshape((-1,))
) # np.ones(p4.reshape((-1,4)).shape[0]))
toy = config.get_data("bg_rec")[0]
p4, w = random_sample_bg(config, decay_chain, toy)
np.save("data/bg.npy", np.stack(p4).reshape((-1, 4)))
np.savetxt("data/bg_w.dat", np.transpose(w).reshape((-1,)))
# toy = config.get_data("phsp_rec")[0]
# p4, w = random_sample(config, decay_chain, toy)
# np.savetxt("data/phsp_re.dat", p4.reshape((-1,4)))
# np.savetxt("data/phsp_re_w.dat", np.transpose(w * toy.get_weight()).reshape((-1,))) # np.repeat(toy.get_weight(), config.resolution_size))
if __name__ == "__main__":
main()
```
然后需要修改 `config.yml` 文件中的 `data` 部分:
```
······
# data entry is the data file name list used for fit
data:
dat_order: [B, C, D]
resolution_size: 25
data: ["resolution/data/data.npy"]
data_weight: ["resolution/data/data_w.dat"]
data_rec: ["data.dat"]
phsp: ["phsp_truth.dat"]
phsp_rec: ["phsp_rec.dat"]
phsp_noeff: ["resolution/data/phsp_noeff.npy"]
bg: ["resolution/data/bg.npy"]
bg_weight: ["resolution/data/bg_w.dat"]
bg_rec: ["bg.dat"]
bg_rec_weight: 1.0
······
```
这里**多余的部分和注释就不写了**,只需要注意我下面列出的几条:
* `resolution_size`:扩大的倍数,请量力而为。
* `data` 和 `data_weight`:这里放的是扩大后的 data 和其一一对应的 weight。把 data 存成 `.npy` 是为了节省空间,下同。
* `data_rec`:这是扩大前,原本的 data,这里用来画图。
* `phsp`,`phsp_rec` 和 `phsp_noeff`:第一个是在拟合中做 MC 积分的,第二个是画图的时候为了和有质量分辨的 data 对应的有分辨的 MC 样本,第三个是用来计算 fit ftaction 时用的无效率的 PHSP MC 样本。
* `bg` 和 `bg_weight`:扩大后的本底和其一一对应的 weight。
* `bg_rec` 和 `bg_rec_weight`:这是扩大前的本底事例,以及其归一化因子。注意这里的归一化因子应该和 `sample.py` 中要修改的权重一致。
搞好这四个文件之后,把 `sample.py` 交到 GPU 作业上,等跑完就将数据扩完了。但是这里需要检查两个 `weight` 文件中是否有 nan。如果有,说明这个事例由于分辨原因超出了相空间允许的范围,需要从原文件中删除。这里有一个写好的检查并删除对应事例的脚本 `re_cal_data.py`:
```
# Read the .txt file to get the line number, then print the line number per 10 lines
import numpy as np
line_number_file = "resolution/data/nan_check.txt"
four_momentum_file = "data.dat"
save_four_momentum_file = "resolution/data/data_new.dat"
def read_line_number(file_name):
line_number = np.loadtxt(file_name)
len_line_number = line_number.shape[0]
return line_number, len_line_number
def read_four_momentum(file_name):
four_momentum = np.loadtxt(file_name)
return four_momentum
def save_four_momentum(file_name, four_momentum):
np.savetxt(file_name, four_momentum, delimiter=" ", fmt="%.12f")
def main():
line_number, len_line_number = read_line_number(line_number_file)
four_momentum = read_four_momentum(four_momentum_file)
for i in range(int(len_line_number / 10) - 1, -1, -1):
print(int(line_number[i * 10 + 9] / 10))
four_momentum = np.delete(
four_momentum, (int(line_number[i * 10 + 9] / 10) * 3), 0
)
four_momentum = np.delete(
four_momentum, (int(line_number[i * 10 + 9] / 10) * 3 - 1), 0
)
four_momentum = np.delete(
four_momentum, (int(line_number[i * 10 + 9] / 10) * 3 - 2), 0
)
save_four_momentum(save_four_momentum_file, four_momentum)
if __name__ == "__main__":
main()
```
用法为:
* 先产生10倍扩大的样本
* 使用 `grep -n "nan" resolution/data/data_w.dat >resolution/data/nan_check.txt` 将 nan 对应的行数输出到 `nan_check.txt` 中,然后将该文件中的除数字外的所有字符删除
* 运行 `re_cal_data.py`,将新产生的 data 覆盖原有的文件
* 再跑一遍 `sample.py` 确保其正常工作。
* bg 和 data 一样。
当所有的都确定没有问题之后,就可以基于改好的 `config.yml` 跑拟合了。
## 6.4 大样本优化选项
### 6.4.1 第一组
```
······
# data entry is the data file name list used for fit
data:
······
use_tf_function: True
jit_compile: True
······
```
TensorFlow提供的编译选项,将动态图转化为静态图。
不支持TensorFlow以外的运算(会固定到第一次的值)。会造成第一步编译耗费大量时间,在不拟合只是计算一下结果时(或者迭代次数较少时)建议关闭。
* `use_tf_function`:tensorflow 优化项
* `jit_compile`:实时(just in time)编译,,tensorflow 优化项。会需要额外的内存。
* **注意**:
1. 如果是使用 `conda` 安装的,会有大概率启用此选项后无法正常拟合,则只需注释这一行即可。
2. 目前集群上的显卡的驱动所对应的 CUDA 版本低于要求,所以使用最新方法安装的 TensorFlow 会有问题,请勿使用该选项。
### 6.4.2 第二组
```
······
# data entry is the data file name list used for fit
data:
······
lazy_call: True
no_id_cached: True
cached_lazy_call: "cached_data/"
······
```
这一组一般是一起使用的,其中每一项为:
* `lazy_call`:启用惰性运行。主要针对角度计算,启用后会在每次计算时计算角度,避免角度占用大量内存。
* `no_id_cached`:在使用`lazy_call: True`时使用`use_tf_function: True`需要设为`True`。
* `cached_lazy_call`:指定缓存文件存储位置,如果指定位置为空(`""`),则缓存文件会存储在内存中。不指定则不会缓存,而是每次都重新计算。**注意**,每次修改 `config.yml` 文件(以及更新代码)后需要删除旧的缓存文件重新生成,未改变则无需重新生成。
### 6.4.3 第三组
```
······
# data entry is the data file name list used for fit
data:
······
amp_model: cached_amp
preprocessor: cached_amp
······
```
新的 `amp_model` 和 `preprocessor`。改变amplitude不同部分的计算顺序,以及需要缓存的数据内容。支持的选项有`default`,`cached_amp`,`cached_shape`,`p4_directly`. 不同选项的具体表现取决于具体的过程。一般当初末态粒子的自旋皆为0时,cached_amp或cached_shape更快。对于初末态自旋较大时,p4_directly更快。
| | preprocessor | amp_model |
| ---------- | ------------------ | --------------- |
| default | p4 -> m + angle | $a_i f_i(m) D_i(angle)$ |
|cached_amp | p4 -> m + angle + $D_i$ | $a_i f_i(m) D_i$ |
|cached_shape| p4 -> m + angle + $f_i D_i + D_j$ | $a_i f_i D_i + a_j f_j(m) D_j$ |
|p4_directly | p4 | $a_i f_i(m(p4))D_i(angle(p4))$ |
$a_i$ 是拟合系数, $f_i(m)$ 是质量依赖部分, $D_i(angle)$ 是角度部分。
存储需求如下表(参考值:$10^7 \times 16\approx1.2$G)
| | |
| -- | -- |
| p4 | 4 N(event) N(particles) |
| m | N(event) N(particles) |
| angle | N(event) N(topo chains)(9 N(final particles)-6) |
| $f_iD_i, D_i$ | N(event) N(helicity combination) N(partial waves) |
`N(topo chains)`取决与`config.yml`的`decay`里定义的数目,去掉没用到的decay chain,可以减少角度计算和缓存需求。
**注意** 画图是用的preprocessor 的结果,没有的将不能画图(比如p4_directly只有p4,没法画m和angle)。
### 6.4.4 第四组
```
······
# data entry is the data file name list used for fit
data:
······
no_left_angle: True
no_angle: True
no_p4: True
······
```
可以去掉部分不需要的角度,缓解存储压力。
* `no_left_angle`: 去掉反向粒子的helicity角。
* `no_angle`: 不画角分布。
* `no_p4`: 去掉原始的四动量。
### 6.4.5 第五组
```
······
# data entry is the data file name list used for fit
data:
······
lazy_call: True
lazy_file: True # bool
lazy_prefetch: 2 # int , -1 for auto, 0 for no, >0 for exact value
······
```
这些选项是为了超级大的 PHSP 样本,例如一亿的 PHSP。其中:
* `lazy_file`:读取 `.npy` 文件会与内存对应,但是非常大的 `.npy` 会占用大量内存,使用memmap直接把读取内存映射到文件,减小内存使用。并且其需要与 `lazy_call` 一起使用。
* `lazy_prefetch`:prefetch会在GPU计算的同时,预先读取下面几步需要的数据,会消耗额外的内存。可以手动设置读取次数。
上述选项可以组合使用。
## 6.5 其他脚本
### 6.5.1 提前计算缓存文件
在使用 `cached_data` 时,可以基于 `config.yml` 文件提前计算缓存文件,提前释放内存,避免拟合时超限。路径:[create_lazy_cached_data.py](https://github.com/jiangyi15/tf-pwa/blob/dev/tutorials/examples/create_lazy_cached_data.py)