0%

Tafel曲线的绘制相关内容

本代码用于物理化学实验中Tafel曲线的绘制,包含了曲线、切线和交点绘制等功能,效果如图。

第一部分:从实验软件生成的txt中批量提取内容到csv表格中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import pandas as pd
import os

def extract_data_and_save_to_csv(txt_file_path):
# 加载数据
try:
data = pd.read_csv(txt_file_path, skiprows=17, header=None, names=['Potential/V', 'Current/A', 'log(i/A)'])
except Exception as e:
print(f"Failed to load data from {txt_file_path}: {str(e)}")
return

# 构建输出CSV文件的路径和名称
output_file_path = os.path.splitext(txt_file_path)[0] + '.csv'

# 保存到CSV文件
try:
data.to_csv(output_file_path, index=False)
print(f"Data from {txt_file_path} has been successfully saved to {output_file_path}")
except Exception as e:
print(f"Failed to save data to {output_file_path}: {str(e)}")

def main(directory_path):
# 获取目录下所有的TXT文件
txt_files = [f for f in os.listdir(directory_path) if os.path.isfile(os.path.join(directory_path, f)) and f.lower().endswith('.txt')]

# 对每个TXT文件执行数据提取和保存操作
for txt_file in txt_files:
txt_file_path = os.path.join(directory_path, txt_file)
extract_data_and_save_to_csv(txt_file_path)

if __name__ == "__main__":
directory_path = input("Please enter the directory path: ")
main(directory_path)

第一步输出和第二步输入的csv格式如图:

第二部分演示程序见:

http://47.122.18.217:8501/

(结构化学课程服务器)

阿尔图罗Arturia
arturia.fun:8501(备案已完成)

2023.11.17起因为负载问题,此端口关闭。欢迎查看其他端口的程序。

(个人服务器,长期)

第二部分:选择csv表格进行绘图操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import streamlit as st
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import linregress
from scipy.interpolate import interp1d
import tempfile
import shutil
import os

# 定义颜色方案
colors = {
"color1": np.array([99, 178, 238]) / 255,
"color2": np.array([118, 218, 145]) / 255,
"color3": np.array([248, 203, 127]) / 255,
"color4": np.array([248, 149, 136]) / 255,
"color5": np.array([124, 214, 207]) / 255,
"color6": np.array([145, 146, 171]) / 255,
"color7": np.array([120, 152, 225]) / 255,
"color8": np.array([239, 166, 102]) / 255,
"color9": np.array([237, 221, 134]) / 255,
"color10": np.array([153, 135, 206]) / 255
}


def plot_tafel(data, A_1, A_2):
data['abs_Current/A'] = np.abs(data['Current/A'])

# 1. 找到电流最接近0的点
min_current_row = data.loc[data['abs_Current/A'].idxmin()]

# 2. 在这个点的正负A_1, A_2范围内,找到对应的两个点
mask = (data['Potential/V'] >= min_current_row['Potential/V'] - A_1) & (data['Potential/V'] <= min_current_row['Potential/V'] + A_2)
range_data = data[mask]

# 找到正负A_1, A_2对应的点
point1 = range_data.loc[range_data['Potential/V'].idxmin()]
point2 = range_data.loc[range_data['Potential/V'].idxmax()]

# 3. 在这两个位置的三个点上分别计算切线
def calculate_tangent_line(data, center_index):
subset = data.loc[center_index-1:center_index+1]
slope, intercept, _, _, _ = linregress(subset['Potential/V'], subset['log(i/A)'])
return slope, intercept

slope1, intercept1 = calculate_tangent_line(data, point1.name)
slope2, intercept2 = calculate_tangent_line(data, point2.name)

# 4. 求出这两条切线的交点
x_intersect = (intercept2 - intercept1) / (slope1 - slope2)
y_intersect = slope1 * x_intersect + intercept1

# 计算切线交点到切点的距离
distance_1 = x_intersect - point1['Potential/V']
distance_2 = x_intersect - point2['Potential/V']

# 对数据进行插值
interp_function = interp1d(data['Potential/V'], data['log(i/A)'], kind='cubic')
potential_interpolated = np.linspace(data['Potential/V'].min(), data['Potential/V'].max(), 1000000)
current_interpolated = interp_function(potential_interpolated)

# 5. 绘制塔菲尔曲线(使用插值后的数据)、切线和交点
plt.figure(figsize=(10, 5))
plt.plot(potential_interpolated, current_interpolated, label='Tafel Plot', color=colors["color1"])
# 在x坐标轴上标注电流最接近0的点
plt.annotate(f'Min Current\n({min_current_row["Potential/V"]:.3f}, {min_current_row["log(i/A)"]:.3f})',
xy=(min_current_row['Potential/V'], min_current_row['log(i/A)']),
xytext=(min_current_row['Potential/V']-0.15, min_current_row['log(i/A)']+0.05),
arrowprops=dict(facecolor=colors["color4"], shrink=0.1),
color=colors["color4"])
# 标记切线的交点
plt.scatter(x_intersect, y_intersect, color=colors["color2"])
# 确定切线最小范围
mm=0.03
# 绘制切线,确保切线可见
x_values_1 = np.linspace(point1['Potential/V'] - max(abs(distance_1),mm), point1['Potential/V'] + max(abs(distance_1),mm), 1000)
y_values_1 = slope1 * x_values_1 + intercept1
x_values_2 = np.linspace(point2['Potential/V'] - max(abs(distance_2),mm), point2['Potential/V'] + max(abs(distance_2),mm), 1000)
y_values_2 = slope2 * x_values_2 + intercept2

plt.plot(x_values_1, y_values_1, label='Tangent Line 1', linestyle='--', color=colors["color3"])
plt.plot(x_values_2, y_values_2, label='Tangent Line 2', linestyle='--', color=colors["color4"])
# 标记切点
plt.scatter([point1['Potential/V'], point2['Potential/V']], [point1['log(i/A)'], point2['log(i/A)']], color=colors["color5"], alpha=0.5)
# 在图的右下角显示交点坐标
plt.text(0.78, 0.05, f'Intersect\n({x_intersect:.3f}, {y_intersect:.3f})', color=colors["color2"], ha='left', va='bottom', transform=plt.gca().transAxes)

# 设置图表标题和坐标轴标签
plt.title('Tafel Plot with Tangent Lines and Intersection')
plt.xlabel('Potential (V)')
plt.ylabel('log(i/A)')

# 显示图例
plt.legend(loc='upper left')

# 显示图表
plt.grid(True)
st.pyplot(plt) # 使用st.pyplot()来显示图表

# Streamlit 应用
st.title("Tafel Plot Analysis")

#文件上传
uploaded_file = st.file_uploader("Choose a CSV file", type="csv")
if uploaded_file is not None:
# 获取上传文件的文件名(不包含扩展名)
file_name, _ = os.path.splitext(uploaded_file.name)

data = pd.read_csv(uploaded_file)

# 自定义参数
A_1 = st.number_input("输入左切点偏离值", value=0.06)
A_2 = st.number_input("输入右切点偏离值", value=0.06)

# 绘制图表
plot_button = st.button("Plot Tafel Curve")
if plot_button:
plot_tafel(data, A_1, A_2)
# 保存到临时文件
temp_filename = tempfile.mktemp(suffix=".png")
plt.savefig(temp_filename)
st.session_state.temp_filename = temp_filename

# 添加保存图片的按钮
if "temp_filename" in st.session_state:
save_button = st.button("Save Plot")
if save_button:
# 将临时文件移动到目标位置
shutil.move(st.session_state.temp_filename, f"{file_name}.png")
st.success("Plot saved successfully!")
# 清除session状态中的临时文件名
del st.session_state.temp_filename
else:
st.write("Please upload a CSV file to proceed.")