Spaces:
Sleeping
Sleeping
import copy | |
import json | |
import math | |
import re | |
import numpy as np | |
import pandas as pd | |
import matplotlib.pyplot as plt | |
#import matplotlib.colors as mcolors | |
from matplotlib.patches import Patch | |
## the synthesis methods: Mass Spectroscopy, Radioactive Decay, Light Particles, Fission, Fusion, Spallation, Projectile Fragmentation, and Transfer/Deep Inelastic Scattering | |
colors_synthesisMethods = { | |
"MS" : (0, 0, 0), | |
"RD" : (0, 255, 255), | |
"LP" : (255, 165, 0), | |
"FI" : (255, 255, 0), | |
"FU" : (255, 0, 0), | |
"SP" : (0, 0, 255), | |
"PF" : (0, 127, 0), | |
"UN" : (127, 0, 127) | |
} | |
names_synthesisMethods = { | |
"MS" : "Mass Spectroscopy", | |
"RD" : "Radioactive Decay", | |
"LP" : "Light Particles", | |
"FI" : "Fission", | |
"FU" : "Fusion", | |
"SP" : "Spallation", | |
"PF" : "Projectile Fragmentation", | |
"UN" : "Transfer/Deep Inelastic" | |
} | |
colors_halflife = { | |
"l100ns": (247, 189, 222), | |
"100ns" : (255, 198, 165), | |
"1us" : (255, 231, 198), | |
"10us" : (255, 255, 156), | |
"100us" : (255, 255, 16), | |
"1ms" : (231, 247, 132), | |
"10ms" : (214, 239, 57), | |
"100ms" : (173, 222, 99), | |
"1s" : (82, 181, 82), | |
"10s" : (99, 189, 181), | |
"100s" : (99, 198, 222), | |
"1ks" : (0, 165, 198), | |
"10ks" : (8, 154, 148), | |
"100ks" : (0, 132, 165), | |
"10Ms" : (49, 82, 165), | |
"1e10s" : (41, 0, 107), | |
"1e15s" : (0, 0, 0), | |
"ST" : (0, 0, 0), | |
"SU" : (255, 148, 115), | |
"UN" : (224, 224, 224) | |
} | |
legends_halflife = { | |
"l100ns": "<100ns", | |
"100ns" : "100ns ~ 1us", | |
"1us" : "1us ~ 10us", | |
"10us" : "10us ~ 100us", | |
"100us" : "100us ~ 1ms", | |
"1ms" : "1ms ~ 10ms", | |
"10ms" : "10ms ~ 100ms", | |
"100ms" : "100ms ~ 1s", | |
"1s" : "1s ~ 10s", | |
"10s" : "10s ~ 100s", | |
"100s" : "100s ~ 1ks", | |
"1ks" : "1ks ~ 10ks", | |
"10ks" : "10ks ~ 100ks", | |
"100ks" : "100ks ~ 10Ms", | |
"10Ms" : "10Ms ~ 1e10s", | |
"1e10s" : "1e10s ~ 1e15s", | |
"1e15s" : ">1e15s", | |
"ST" : "STABLE", | |
"SU" : "SpecialUnit", | |
"UN" : "UNKNOWN" | |
} | |
colors_DecayModes = { | |
"P" : (255, 148, 115), | |
"N" : (156, 123, 189), | |
"A" : (255, 255, 66), | |
"B-" : (231, 140, 198), | |
"EC+B+" : (99, 198, 222), | |
"EC" : (0, 132, 165), | |
"SF" : (82, 181, 82), | |
"STABLE": (0, 0, 0), | |
"UNKNOWN":(224, 224, 224) | |
} | |
legends_DecayModes = { | |
"P" : "Proton", | |
"N" : "Neutron", | |
"A" : "Alaph", | |
"B-" : "Beta-", | |
"EC+B+" : "Beta+ ElectronCapture", | |
"EC" : "ElectronCapture", | |
"SF" : "SpontaneousFission", | |
"STABLE": "STABLE", | |
"UNKNOWN":"UNKNOWN" | |
} | |
nuclides_data_path = "data/nndc_nudat_data_export.json" | |
data_synthesisMethods_path = "data/Nuclides_synthesisMethods.json" | |
ElementsList_path = "data/ElementsList.json" | |
data_NuclidesClassifiedHalflife_path = "data/NuclidesClassifiedHalflife.json" | |
data_NuclidesClassifiedDecayModes_path = "data/NuclidesClassifiedDecayModes.json" | |
## 使用matplotlib绘图 | |
## 入参为核素分类模式、所绘制核素区域、显示信息、有无图例 | |
## area部分待完善 | |
def nucildesChartPlotPLT(plot_mode=0, area=((0,0),(118,177)), text_mode=0, have_legend=True): | |
## 确定绘制核素区域 | |
z_min, n_min = area[0] | |
z_max, n_max = area[1] | |
area_ysize = z_max - z_min + 1 | |
area_xsize = n_max - n_min + 1 | |
## 计算坐标时使用的单位为像素,而matplotlib的方法使用的单位为英寸,执行前须进行单位转换 | |
nbwidth = 40 | |
nbheight = 40 | |
dpi = 100 | |
## 根据显示信息确定绘图大小 | |
if text_mode == 0: | |
resize_ratio = 5 | |
ticks_step = 10 | |
fontsize_label = 75 | |
fontsize_ticks = 50 | |
elif text_mode == 1: | |
resize_ratio = 5 | |
ticks_step = 10 | |
fontsize_label = 75 | |
fontsize_ticks = 50 | |
elif text_mode == 2: | |
resize_ratio = 10 | |
ticks_step = 10 | |
fontsize_label = 150 | |
fontsize_ticks = 100 | |
elif text_mode == 3: | |
resize_ratio = 10 | |
ticks_step = 10 | |
fontsize_label = 150 | |
fontsize_ticks = 100 | |
else: | |
resize_ratio = 1 | |
ticks_step = 10 | |
fontsize_label = 15 | |
fontsize_ticks = 10 | |
## 确定绘制范围 | |
## 据质子数、中子数取整十 | |
## 对于默认的(118,177),绘制范围为(130, 200) | |
x_min = math.floor(n_min / 10) * 10 | |
x_max = math.ceil(n_max / 10) * 10 | |
y_min = math.floor(z_min / 10) * 10 | |
y_max = math.ceil(z_max / 10) * 10 | |
fig = plt.figure(figsize=(10*resize_ratio*(x_max-x_min)/200, 6*resize_ratio*(y_max-y_min)/130), dpi=dpi) | |
ax = plt.gca() | |
## 核素区域绘制,默认白色背景 | |
bg_color = (255, 255, 255) | |
color_data = np.full((area_ysize, area_xsize, 3), bg_color) | |
dx = nbwidth * resize_ratio | |
dy = nbheight * resize_ratio | |
xposg = np.arange(0, area_xsize*dx + dx, dx) | |
yposg = np.arange(0, area_ysize*dy + dy, dy) | |
xgrid = np.tile(xposg.reshape(1, -1), (area_ysize + 1, 1)) | |
ygrid = np.tile(yposg.reshape(-1, 1), (1, area_xsize + 1)) | |
xgridI = xgrid / dpi | |
ygridI = ygrid / dpi | |
## 根据核素分类模式上色 | |
color_data = nucildesChartPlotPLTColor(copy.deepcopy(color_data), plot_mode, z_min, n_min ,z_max, n_max) | |
## 停用imshow转用pcolormesh以便控制各网格大小以及绘制边框 | |
#ax.imshow(color_data, origin="lower", extent=[n_min-0.5, n_max+0.5, z_min-0.5, z_max+0.5]) | |
ax.pcolormesh(xgridI, ygridI, color_data.astype(np.uint8), edgecolors="white", linewidth=4*resize_ratio/dpi) | |
## 绘制坐标轴 | |
ax.set_xlim(((x_min-5)*dx/dpi, (x_max+20)*dx/dpi)) | |
ax.set_ylim(((y_min-5)*dy/dpi, (y_max+10)*dy/dpi)) | |
ax.set_xlabel("Neutron number", fontsize=fontsize_label, font='Times New Roman') | |
ax.set_ylabel("Proton number" , fontsize=fontsize_label, font='Times New Roman') | |
## 刻度绘制,同样取整十 (ticks_step = 10) | |
xticks = np.arange(x_min, x_max + 20 + ticks_step, ticks_step) | |
yticks = np.arange(y_min, y_max + 10 + ticks_step, ticks_step) | |
xtickspos = (xticks * dx + np.ones(len(xticks)) * 0.5 * dx) / dpi | |
ytickspos = (yticks * dy + np.ones(len(yticks)) * 0.5 * dy) / dpi | |
ax.set_xticks(xtickspos, xticks, fontsize=fontsize_ticks) | |
ax.set_yticks(ytickspos, yticks, fontsize=fontsize_ticks) | |
## 图例添加 | |
## 默认大小匹配全图绘制 | |
if have_legend: | |
legend_handles = legendHandlesGet(plot_mode) | |
if len(legend_handles) < 13: | |
fontsize_legend = fontsize_ticks | |
else: | |
fontsize_legend = fontsize_ticks * 0.6 | |
plt.legend(handles=legend_handles, loc="lower right", fontsize=fontsize_legend) | |
## 添加显示信息 | |
if not text_mode == 0: | |
color_weight = np.array((0.299, 0.587, 0.114)) | |
text_data = nucildesChartPlotPLTText(plot_mode, z_min, n_min ,z_max, n_max) | |
## 显示元素名称 | |
if text_mode == 1: | |
for tdata in text_data: | |
## 根据方块颜色选择文本颜色(黑或白) | |
base_color = color_data[tdata["pos"][1]][tdata["pos"][0]] | |
if np.dot(color_weight, base_color) > 128: | |
text_color = "black" | |
else: | |
text_color = "white" | |
txposi = (tdata["pos"][0] + 0.5) * dx / dpi | |
typosi = (tdata["pos"][1] + 0.5) * dy / dpi | |
ax.text(txposi, typosi, tdata["text01"], ha="center", va="center", color=text_color, fontsize=6) | |
## 显示核素名称 | |
elif text_mode == 2: | |
for tdata in text_data: | |
## 根据方块颜色选择文本颜色(黑或白) | |
base_color = color_data[tdata["pos"][1]][tdata["pos"][0]] | |
if np.dot(color_weight, base_color) > 128: | |
text_color = "black" | |
else: | |
text_color = "white" | |
txposi = (tdata["pos"][0] + 0.5) * dx / dpi | |
typosi = (tdata["pos"][1] + 0.5) * dy / dpi | |
ax.text(txposi, typosi, tdata["text02"], ha="center", va="center", color=text_color, fontsize=6) | |
## 显示详细信息 | |
elif text_mode == 3: | |
for tdata in text_data: | |
## 根据方块颜色选择文本颜色(黑或白) | |
base_color = color_data[tdata["pos"][1]][tdata["pos"][0]] | |
if np.dot(color_weight, base_color) > 128: | |
text_color = "black" | |
else: | |
text_color = "white" | |
txposi = (tdata["pos"][0] + 0.5) * dx / dpi | |
typosi = (tdata["pos"][1] + 0.85) * dy / dpi | |
text = tdata["text02"] + "\n" + tdata["text03"] | |
ax.text(txposi, typosi, text, ha="center", va="top", ma="center", color=text_color, fontsize=2.4) | |
#fig.savefig("test.svg", format="svg") | |
#fig.savefig("test.png", format="png") | |
#plt.show() | |
return fig | |
def nucildesChartPlotPLTColor(color_data, mode, z_min, n_min ,z_max, n_max): | |
## 据半衰期上色 | |
## 默认使用基态数据 | |
## nndc上的nudat3绘制时若基态无数据则会使用激发态的数据,此处与其不同 比如:137Pm、154Lu、161Ta 等 | |
if mode == 0: | |
## 自NDfilter.nuclidesClassifyHalflife() | |
with open(data_NuclidesClassifiedHalflife_path, "r", encoding="utf8") as file: | |
data = json.load(file) | |
for row in data: | |
if (row["z"] >= z_min and row["z"] <= z_max) and (row["n"] >= n_min and row["n"] <= n_max): | |
ypos = row["z"] - z_min | |
xpos = row["n"] - n_min | |
if row["type"] in colors_halflife: | |
color_data[ypos][xpos] = np.array(colors_halflife[row["type"]]) | |
## 据衰变模式上色 | |
elif mode == 1: | |
## 自NDfilter.nuclidesClassifyDecayMode() | |
with open(data_NuclidesClassifiedDecayModes_path, "r", encoding="utf8") as file: | |
data = json.load(file) | |
for row in data: | |
if (row["z"] >= z_min and row["z"] <= z_max) and (row["n"] >= n_min and row["n"] <= n_max): | |
ypos = row["z"] - z_min | |
xpos = row["n"] - n_min | |
if row["type"] in colors_DecayModes: | |
color_data[ypos][xpos] = np.array(colors_DecayModes[row["type"]]) | |
elif row["type"] in ("2B-", "β⁻"): | |
color_data[ypos][xpos] = np.array(colors_DecayModes["B-"]) | |
elif row["type"] in ("2P", "3P"): | |
color_data[ypos][xpos] = np.array(colors_DecayModes["P"]) | |
elif row["type"] in ("2N"): | |
color_data[ypos][xpos] = np.array(colors_DecayModes["N"]) | |
else: | |
color_data[ypos][xpos] = np.array(colors_DecayModes["UNKNOWN"]) | |
## 据合成方法上色 | |
elif mode == 2: | |
with open(data_synthesisMethods_path, "r", encoding="utf8") as file: | |
data = json.load(file) | |
for row in data: | |
if (row["z"] >= z_min and row["z"] <= z_max) and (row["n"] >= n_min and row["n"] <= n_max): | |
ypos = row["z"] - z_min | |
xpos = row["n"] - n_min | |
if row["type"] in colors_synthesisMethods: | |
color_data[ypos][xpos] = np.array(colors_synthesisMethods[row["type"]]) | |
return color_data | |
def legendHandlesGet(plot_mode): | |
handles = [] | |
if plot_mode == 0: | |
for hl_tag in colors_halflife: | |
if hl_tag == "ST": | |
pass | |
elif hl_tag == "1e15s": | |
handles.append(Patch(facecolor=np.array(colors_halflife[hl_tag])/255., label=">1e15s or Stable")) | |
else: | |
handles.append(Patch(facecolor=np.array(colors_halflife[hl_tag])/255., label=legends_halflife[hl_tag])) | |
elif plot_mode == 1: | |
for decay_mode in colors_DecayModes: | |
handles.append(Patch(facecolor=np.array(colors_DecayModes[decay_mode])/255., label=legends_DecayModes[decay_mode])) | |
elif plot_mode == 2: | |
for synthesis_method in colors_synthesisMethods: | |
handles.append(Patch(facecolor=np.array(colors_synthesisMethods[synthesis_method])/255., label=names_synthesisMethods[synthesis_method])) | |
return handles | |
def nucildesChartPlotPLTText(plot_mode, z_min, n_min ,z_max, n_max): | |
with open(ElementsList_path, "r", encoding="utf8") as file: | |
elements_list = json.load(file) | |
text_data = [] | |
if plot_mode == 0 or plot_mode == 1: | |
with open(nuclides_data_path, "r", encoding="utf8") as file: | |
data = json.load(file) | |
## 填充半衰期及衰变模式信息 | |
## 对于衰变模式多于三种的,只显示其前三种(仿nndc) | |
## 对于过长的数字,将会对其进行截断 | |
for nom, ndata in data.items(): | |
if (ndata["z"] >= z_min and ndata["z"] <= z_max) and (ndata["n"] >= n_min and ndata["n"] <= n_max): | |
ypos = ndata["z"] - z_min | |
xpos = ndata["n"] - n_min | |
text01 = elements_list[str(ndata["z"])] | |
text02 = str(ndata["z"]+ndata["n"]) + text01 | |
hlt = "" | |
dmst = [] | |
if len(ndata["levels"]) == 0: | |
hlt = "" | |
dmst = [] | |
else: | |
if not "halflife" in ndata["levels"][0]: | |
hlt = "" | |
elif not "value" in ndata["levels"][0]["halflife"]: | |
hlt = "" | |
elif ndata["levels"][0]["halflife"]["value"] == "STABLE": | |
hlt = "STABLE" | |
else: | |
hlv = ndata["levels"][0]["halflife"]["value"] | |
if hlv > 1e4: | |
hlt = f"{hlv:.2e}" | |
else: | |
hlt = str(hlv) | |
if ndata["levels"][0]["halflife"]["unit"] == "m": | |
hlt = hlt + " min" | |
else: | |
hlt = hlt + " " + ndata["levels"][0]["halflife"]["unit"] | |
if not "decayModes" in ndata["levels"][0]: | |
dmst = [] | |
else: | |
decay_modes = ndata["levels"][0]["decayModes"]["observed"] + ndata["levels"][0]["decayModes"]["predicted"] | |
if len(decay_modes) == 0: | |
dmst = [] | |
else: | |
## 据分支比排序 | |
dms1 = [] | |
dms2 = [] | |
dmsd = {} | |
dmsd1 = [] | |
for decay_mode in decay_modes: | |
if not "value" in decay_mode: | |
dms2.append(decay_mode) | |
else: | |
dmsd[decay_mode["mode"]] = decay_mode | |
dmsd1.append((decay_mode["value"], decay_mode["mode"])) | |
dmsdf = pd.DataFrame(dmsd1, columns=["value", "mode"]) | |
dmsdf.sort_values(by="value", ascending=False, inplace=True) | |
for mode in dmsdf["mode"]: | |
dms1.append(dmsd[mode]) | |
dms = dms1 + dms2 | |
### | |
for decay_mode in dms: | |
text = decay_mode["mode"] | |
if not "value" in decay_mode: | |
text = text + " ?" | |
else: | |
dmv = decay_mode["value"] | |
dmt = str(dmv) | |
if len(dmt) > 7: | |
if re.search(rf"([eE])", dmt) == None: | |
if dmv > 1e-3: | |
dmt = dmt[:7] | |
else: | |
match00 = re.fullmatch(rf"(0+)(\.)(0+)([1-9]+)", dmt) | |
if not match00 == None: | |
ne = match00.group(4) | |
if len(ne) < 3: | |
dmt = f"{dmv:e}" | |
else: | |
dmt = f"{dmv:.2e}" | |
if decay_mode["uncertainty"]["type"] == "limit": | |
if decay_mode["uncertainty"]["limitType"] == "lower": | |
if decay_mode["uncertainty"]["isInclusive"] == True: | |
text = text + " ≥ " + dmt + "%" | |
else: | |
text = text + " > " + dmt + "%" | |
elif decay_mode["uncertainty"]["limitType"] == "upper": | |
if decay_mode["uncertainty"]["isInclusive"] == True: | |
text = text + " ≤ " + dmt + "%" | |
else: | |
text = text + " < " + dmt + "%" | |
else: | |
text = text + " = " + dmt + "%" | |
dmst.append(text) | |
text03 = hlt + "\n" | |
if not len(dmst) > 0: | |
pass | |
elif len(dmst) <= 3: | |
for dmt in dmst: | |
text03 = text03 + "\n" + dmt | |
else: | |
for idx in range(0, 3): | |
text03 = text03 + "\n" + dmst[idx] | |
text_data.append({"pos":(xpos, ypos), "text01":text01, "text02":text02, "text03":text03}) | |
## 填充合成方法信息 | |
elif plot_mode == 2: | |
with open(data_synthesisMethods_path, "r", encoding="utf8") as file: | |
data = json.load(file) | |
for row in data: | |
if (row["z"] >= z_min and row["z"] <= z_max) and (row["n"] >= n_min and row["n"] <= n_max): | |
ypos = row["z"] - z_min | |
xpos = row["n"] - n_min | |
text01 = elements_list[str(row["z"])] | |
text02 = str(row["z"]+row["n"]) + text01 | |
text03 = row["type"] | |
text_data.append({"pos":(xpos, ypos), "text01":text01, "text02":text02, "text03":text03}) | |
return text_data | |
## test | |