egasp 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- egasp/__init__.py +13 -0
- egasp/__main__.py +145 -0
- egasp/check_version.py +197 -0
- egasp/core.py +177 -0
- egasp/data.py +244 -0
- egasp/language.py +43 -0
- egasp/logger_config.py +63 -0
- egasp/validate.py +52 -0
- egasp/version.py +29 -0
- egasp-0.0.1.dist-info/METADATA +73 -0
- egasp-0.0.1.dist-info/RECORD +15 -0
- egasp-0.0.1.dist-info/WHEEL +5 -0
- egasp-0.0.1.dist-info/entry_points.txt +2 -0
- egasp-0.0.1.dist-info/licenses/LICENSE +674 -0
- egasp-0.0.1.dist-info/top_level.txt +1 -0
egasp/__init__.py
ADDED
egasp/__main__.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
'''
|
|
2
|
+
=======================================================================
|
|
3
|
+
····Y88b···d88P················888b·····d888·d8b·······················
|
|
4
|
+
·····Y88b·d88P·················8888b···d8888·Y8P·······················
|
|
5
|
+
······Y88o88P··················88888b·d88888···························
|
|
6
|
+
·······Y888P··8888b···88888b···888Y88888P888·888·88888b·····d88b·······
|
|
7
|
+
········888······"88b·888·"88b·888·Y888P·888·888·888·"88b·d88P"88b·····
|
|
8
|
+
········888···d888888·888··888·888··Y8P··888·888·888··888·888··888·····
|
|
9
|
+
········888··888··888·888··888·888···"···888·888·888··888·Y88b·888·····
|
|
10
|
+
········888··"Y888888·888··888·888·······888·888·888··888··"Y88888·····
|
|
11
|
+
·······························································888·····
|
|
12
|
+
··························································Y8b·d88P·····
|
|
13
|
+
···························································"Y88P"······
|
|
14
|
+
=======================================================================
|
|
15
|
+
|
|
16
|
+
-----------------------------------------------------------------------
|
|
17
|
+
Author : 焱铭
|
|
18
|
+
Date : 2025-04-22 10:43:55 +0800
|
|
19
|
+
LastEditTime : 2025-04-22 20:17:41 +0800
|
|
20
|
+
Github : https://github.com/YanMing-lxb/
|
|
21
|
+
FilePath : /EG-ASP/src/egasp/__main__.py
|
|
22
|
+
Description :
|
|
23
|
+
-----------------------------------------------------------------------
|
|
24
|
+
'''
|
|
25
|
+
|
|
26
|
+
import argparse
|
|
27
|
+
from rich import box
|
|
28
|
+
from rich import print
|
|
29
|
+
from rich.table import Table
|
|
30
|
+
from rich.console import Console
|
|
31
|
+
from rich_argparse import RichHelpFormatter
|
|
32
|
+
|
|
33
|
+
from egasp.validate import Validate
|
|
34
|
+
from egasp.core import EG_ASP_Core
|
|
35
|
+
from egasp.logger_config import setup_logger
|
|
36
|
+
from egasp.check_version import UpdateChecker
|
|
37
|
+
# 版本信息
|
|
38
|
+
from egasp.version import script_name, __version__
|
|
39
|
+
logger = setup_logger(True)
|
|
40
|
+
|
|
41
|
+
def get_egasp(query_temp: float, query_type: str = 'volume', query_value: float = 50) -> tuple:
|
|
42
|
+
"""
|
|
43
|
+
根据输入的查询类型、浓度和温度, 计算乙二醇水溶液的相关属性。
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
query_type : str
|
|
48
|
+
查询浓度的类型, 可选值为 "volume" 或 "mass", 分别表示体积浓度和质量浓度, 默认值为 "volume"。
|
|
49
|
+
query_value : float
|
|
50
|
+
查询的浓度值, 范围为 10% 到 90%, 单位为百分比 (%), 默认值为 50。
|
|
51
|
+
query_temp : float
|
|
52
|
+
查询的温度值, 范围为 -35°C 到 125°C。
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
tuple
|
|
57
|
+
返回一个包含以下属性的元组:
|
|
58
|
+
- mass: 质量浓度 (%)
|
|
59
|
+
- volume: 体积浓度 (%)
|
|
60
|
+
- freezing: 冰点 (°C)
|
|
61
|
+
- boiling: 沸点 (°C)
|
|
62
|
+
- rho: 密度 (kg/m³)
|
|
63
|
+
- cp: 比热容 (J/kg·K)
|
|
64
|
+
- k: 导热率 (W/m·K)
|
|
65
|
+
- mu: 动力粘度 (Pa·s)
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
eg = EG_ASP_Core() # 初始化核心计算类实例
|
|
69
|
+
va = Validate() # 初始化校验类实例
|
|
70
|
+
|
|
71
|
+
# 校验查询类型, 确保其为合法值 ("volume" 或 "mass")
|
|
72
|
+
query_type = va.type_value(query_type)
|
|
73
|
+
|
|
74
|
+
# 校验查询浓度, 确保其在 10% 到 90% 的范围内
|
|
75
|
+
query_value = va.input_value(query_value, min_val=10, max_val=90)
|
|
76
|
+
|
|
77
|
+
# 校验查询温度, 确保其在 -35°C 到 125°C 的范围内
|
|
78
|
+
query_temp = va.input_value(query_temp, min_val=-35, max_val=125)
|
|
79
|
+
|
|
80
|
+
# 打印校验后的查询参数
|
|
81
|
+
print(f"查询类型: {query_type}")
|
|
82
|
+
print(f"查询浓度: {query_value} %")
|
|
83
|
+
print(f"查询温度: {query_temp} °C")
|
|
84
|
+
|
|
85
|
+
# 根据查询类型调用相应的函数, 获取冰点和沸点属性
|
|
86
|
+
mass, volume, freezing, boiling = eg.get_fb_props(query_value, query_type=query_type)
|
|
87
|
+
|
|
88
|
+
# 获取密度 (rho), 单位为 kg/m³
|
|
89
|
+
rho = eg.get_props(temp=query_temp, conc=volume, egp_key='rho')
|
|
90
|
+
|
|
91
|
+
# 获取比热容 (cp), 单位为 J/kg·K
|
|
92
|
+
cp = eg.get_props(temp=query_temp, conc=volume, egp_key='cp')
|
|
93
|
+
|
|
94
|
+
# 获取导热率 (k), 单位为 W/m·K
|
|
95
|
+
k = eg.get_props(temp=query_temp, conc=volume, egp_key='k')
|
|
96
|
+
|
|
97
|
+
# 获取动力粘度 (mu), 单位为 Pa·s, 并将结果从 mPa·s 转换为 Pa·s
|
|
98
|
+
mu = eg.get_props(temp=query_temp, conc=volume, egp_key='mu') / 1000000
|
|
99
|
+
|
|
100
|
+
return mass, volume, freezing, boiling, rho, cp, k, mu
|
|
101
|
+
|
|
102
|
+
def main():
|
|
103
|
+
parser = argparse.ArgumentParser(
|
|
104
|
+
prog='egasp',
|
|
105
|
+
description="[i]乙二醇水溶液属性查询程序 ---- 焱铭[/]",
|
|
106
|
+
formatter_class = RichHelpFormatter,
|
|
107
|
+
)
|
|
108
|
+
parser.add_argument("--query_type", type=str, default="volume", help="浓度类型 (volume/mass or v/m), 默认值为 volume (体积浓度)")
|
|
109
|
+
parser.add_argument("--query_value", type=float, default=50, help="查询浓度 %% (范围: 10 ~ 90), 默认值为 50") # 修改此处
|
|
110
|
+
parser.add_argument("query_temp", type=float, help="查询温度 °C (范围: -35 ~ 125)") # 如果温度单位有%也需要转义
|
|
111
|
+
|
|
112
|
+
args = parser.parse_args()
|
|
113
|
+
|
|
114
|
+
console = Console(width=34)
|
|
115
|
+
console.print(f"\n[bold green]{script_name} v{__version__}[/bold green]", justify="center")
|
|
116
|
+
print('----------------------------------')
|
|
117
|
+
mass, volume, freezing, boiling, rho, cp, k, mu = get_egasp(args.query_temp, args.query_type, args.query_value)
|
|
118
|
+
print('----------------------------------\n')
|
|
119
|
+
|
|
120
|
+
# 创建表格
|
|
121
|
+
table = Table(show_header=True, header_style="bold dark_orange", box=box.ASCII_DOUBLE_HEAD, title="乙二醇水溶液查询结果")
|
|
122
|
+
|
|
123
|
+
# 添加列
|
|
124
|
+
table.add_column("属性", justify="left", style="cyan" , no_wrap=True)
|
|
125
|
+
table.add_column("单位", justify="left", style="magenta", no_wrap=True)
|
|
126
|
+
table.add_column("数值", justify="left", style="green" , no_wrap=True)
|
|
127
|
+
|
|
128
|
+
# 添加行
|
|
129
|
+
table.add_row("质量浓度" , "%" , f"{mass:.2f}")
|
|
130
|
+
table.add_row("体积浓度" , "%" , f"{volume:.2f}")
|
|
131
|
+
table.add_row("冰点" , "°C" , f"{freezing:.2f}")
|
|
132
|
+
table.add_row("沸点" , "°C" , f"{boiling:.2f}")
|
|
133
|
+
table.add_row("密度" , "kg/m³" , f"{rho:.2f}")
|
|
134
|
+
table.add_row("比热容" , "J/kg·K" , f"{cp:.2f}")
|
|
135
|
+
table.add_row("导热率" , "W/m·K" , f"{k:.4f}")
|
|
136
|
+
table.add_row("粘度" , "Pa·s" , f"{mu:.8f}")
|
|
137
|
+
|
|
138
|
+
# 打印表格
|
|
139
|
+
console.print(table)
|
|
140
|
+
|
|
141
|
+
# 检查更新
|
|
142
|
+
uc = UpdateChecker(1, 6) # 访问超时, 单位: 秒;缓存时长, 单位: 小时
|
|
143
|
+
uc.check_for_updates()
|
|
144
|
+
if __name__ == "__main__":
|
|
145
|
+
main()
|
egasp/check_version.py
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
|
|
2
|
+
import re
|
|
3
|
+
import time
|
|
4
|
+
import toml
|
|
5
|
+
import logging
|
|
6
|
+
import urllib.request
|
|
7
|
+
from rich import print
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import importlib.metadata
|
|
10
|
+
import importlib.resources
|
|
11
|
+
from packaging import version
|
|
12
|
+
from datetime import timedelta
|
|
13
|
+
|
|
14
|
+
from egasp.language import set_language
|
|
15
|
+
|
|
16
|
+
_ = set_language('check_version')
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class UpdateChecker():
|
|
20
|
+
|
|
21
|
+
def __init__(self, time_out, cache_time):
|
|
22
|
+
"""
|
|
23
|
+
初始化 CheckVersion 类的实例。
|
|
24
|
+
|
|
25
|
+
参数:
|
|
26
|
+
time_out (int): 超时时间,单位为秒。
|
|
27
|
+
cache_time (int): 缓存时间,单位为小时。
|
|
28
|
+
|
|
29
|
+
行为逻辑:
|
|
30
|
+
1. 创建日志对象。
|
|
31
|
+
2. 初始化版本列表。
|
|
32
|
+
3. 将缓存时间转换为秒并存储。
|
|
33
|
+
4. 存储超时时间。
|
|
34
|
+
5. 获取 egasp 安装路径并拼接 data 子路径,创建 data 目录(如果已存在则不报错)。
|
|
35
|
+
6. 创建缓存文件路径。
|
|
36
|
+
"""
|
|
37
|
+
self.logger = logging.getLogger(__name__) # 创建日志对象
|
|
38
|
+
|
|
39
|
+
self.versions = [] # 初始化版本列表
|
|
40
|
+
|
|
41
|
+
self.cache_time = cache_time * 3600 # 将缓存时间转换为秒并存储
|
|
42
|
+
self.time_out = time_out # 存储超时时间
|
|
43
|
+
|
|
44
|
+
data_path = Path(importlib.resources.files('egasp')) / 'data' # 获取 egasp 安装路径并拼接 data 子路径
|
|
45
|
+
self.cache_file = data_path / "egasp_version_cache.toml" # 创建缓存文件路径
|
|
46
|
+
|
|
47
|
+
# --------------------------------------------------------------------------------
|
|
48
|
+
# 定义 缓存文件读取函数
|
|
49
|
+
# --------------------------------------------------------------------------------
|
|
50
|
+
def _load_cached_version(self):
|
|
51
|
+
"""
|
|
52
|
+
从缓存文件中加载最新版本号.
|
|
53
|
+
|
|
54
|
+
行为逻辑说明:
|
|
55
|
+
- 尝试读取缓存文件路径.
|
|
56
|
+
- 检查缓存文件是否存在且未过期(根据缓存时间判断).
|
|
57
|
+
- 如果缓存文件有效,读取文件内容并返回最新版本号.
|
|
58
|
+
- 如果发生任何异常,记录错误信息并返回 None.
|
|
59
|
+
|
|
60
|
+
返回说明:
|
|
61
|
+
- 返回缓存文件中的最新版本号,如果加载失败或缓存文件无效,则返回 None.
|
|
62
|
+
"""
|
|
63
|
+
try:
|
|
64
|
+
cache_path = Path(self.cache_file) # 获取缓存文件路径
|
|
65
|
+
if not cache_path.exists():
|
|
66
|
+
return None # 如果缓存文件不存在,返回 None
|
|
67
|
+
|
|
68
|
+
# 使用timedelta来转换秒数
|
|
69
|
+
cache_time_remaining = round(self.cache_time - (time.time() - cache_path.stat().st_mtime), 4) # 计算缓存剩余时间
|
|
70
|
+
delta = timedelta(seconds=cache_time_remaining)
|
|
71
|
+
# 计算小时、分钟和秒
|
|
72
|
+
total_seconds = delta.total_seconds() # 将timedelta转换为总秒数
|
|
73
|
+
hours, remainder = divmod(int(total_seconds), 3600) # 计算小时数
|
|
74
|
+
minutes, seconds = divmod(remainder, 60) # 计算分钟数和秒数
|
|
75
|
+
|
|
76
|
+
if cache_path.exists() and (cache_time_remaining > 0): # 检查缓存文件是否存在且未过期
|
|
77
|
+
with cache_path.open('r') as f: # 打开缓存文件
|
|
78
|
+
data = toml.load(f) # 加载缓存文件内容
|
|
79
|
+
self.logger.info(_("读取 egasp 版本缓存文件中的版本号,缓存有效期: ") + f'{int(hours):02} h {int(minutes):02} min {int(seconds):02} s') # 记录日志信息
|
|
80
|
+
self.logger.info(_("egasp 版本缓存文件路径: ") + str(self.cache_file)) # 记录日志信息
|
|
81
|
+
return data.get("latest_version") # 返回最新版本号
|
|
82
|
+
except Exception as e:
|
|
83
|
+
self.logger.error(_("加载缓存版本时出错: ") + str(e)) # 记录错误信息
|
|
84
|
+
return None # 如果加载失败或缓存文件无效,返回 None
|
|
85
|
+
|
|
86
|
+
# --------------------------------------------------------------------------------
|
|
87
|
+
# 定义 缓存文件写入函数
|
|
88
|
+
# --------------------------------------------------------------------------------
|
|
89
|
+
def _update_egasp_version_cache(self, latest_version):
|
|
90
|
+
"""
|
|
91
|
+
更新egasp版本缓存文件.
|
|
92
|
+
|
|
93
|
+
参数:
|
|
94
|
+
latest_version (str): 最新的egasp版本号.
|
|
95
|
+
|
|
96
|
+
行为逻辑:
|
|
97
|
+
尝试打开缓存文件并以写模式写入最新的版本号.如果操作失败,记录错误日志.
|
|
98
|
+
"""
|
|
99
|
+
try:
|
|
100
|
+
# 尝试以写模式打开缓存文件
|
|
101
|
+
with open(self.cache_file, 'w') as f:
|
|
102
|
+
# 使用toml库将最新的版本号写入文件
|
|
103
|
+
toml.dump({"latest_version": latest_version}, f)
|
|
104
|
+
except Exception as e:
|
|
105
|
+
# 如果更新缓存时出错,记录错误日志
|
|
106
|
+
self.logger.error(_("更新版本缓存时出错: ") + str(e))
|
|
107
|
+
|
|
108
|
+
# --------------------------------------------------------------------------------
|
|
109
|
+
# 定义 网络获取版本信息函数
|
|
110
|
+
# --------------------------------------------------------------------------------
|
|
111
|
+
def _get_latest_version(self, package_name): # TODO 将该函数放到线程中执行,获取线程信息,保存,并且在下次运行时检查这个显示是否已经执行结束,如果结束则不再执行,否则执行
|
|
112
|
+
"""
|
|
113
|
+
获取指定包的最新版本号.
|
|
114
|
+
|
|
115
|
+
参数:
|
|
116
|
+
package_name (str): 包的名称.
|
|
117
|
+
|
|
118
|
+
返回:
|
|
119
|
+
str: 最新版本号,如果获取失败则返回 None.
|
|
120
|
+
|
|
121
|
+
行为逻辑:
|
|
122
|
+
1. 记录开始时间.
|
|
123
|
+
2. 构建包的镜像URL.
|
|
124
|
+
3. 尝试打开URL并读取HTML内容.
|
|
125
|
+
4. 使用正则表达式匹配HTML内容中的版本号.
|
|
126
|
+
5. 将匹配到的版本号添加到版本列表中.
|
|
127
|
+
6. 如果版本列表不为空,返回最大版本号;否则记录错误并返回 None.
|
|
128
|
+
7. 捕获所有异常并记录错误信息,返回 None.
|
|
129
|
+
8. 记录获取版本号的耗时.
|
|
130
|
+
"""
|
|
131
|
+
start_time = time.time() # 记录开始时间
|
|
132
|
+
mirror_url = "https://mirrors.aliyun.com/pypi/simple/" # 设置镜像URL #TODO 添加多个镜像源,进行循环尝试
|
|
133
|
+
url = f"{mirror_url}{package_name}" # 构建包的URL
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
response = urllib.request.urlopen(url) # 尝试打开URL
|
|
137
|
+
html_content = response.read().decode('utf-8') # 读取并解码HTML内容
|
|
138
|
+
pattern = re.compile(r'<a\s+href="[^"]+/egasp-([\d\.]+)-py3-none-any\.whl[^"]*"\s+data-requires-python="[^"]+">.*?</a>', re.DOTALL) # 定义正则表达式模式
|
|
139
|
+
matches = pattern.findall(html_content) # 使用正则表达式匹配版本号
|
|
140
|
+
|
|
141
|
+
for ver in matches: # 遍历匹配到的版本号
|
|
142
|
+
self.versions.append(ver) # 将版本号添加到版本列表中
|
|
143
|
+
|
|
144
|
+
if self.versions: # 如果版本列表不为空
|
|
145
|
+
# 使用列表推导式和parse函数将字符串转换为版本对象
|
|
146
|
+
version_objects = [version.parse(ver) for ver in self.versions]
|
|
147
|
+
|
|
148
|
+
# 使用max函数和key参数找到最新版本
|
|
149
|
+
latest_version = max(version_objects)
|
|
150
|
+
|
|
151
|
+
self.logger.info(_("获取 %(args)s 最新版本号成功: ") % {"args": package_name} + f"[bold green]{latest_version}[/bold green]") # 记录成功信息
|
|
152
|
+
return latest_version # 返回最新版本号
|
|
153
|
+
else:
|
|
154
|
+
self.logger.error(_("获取 %(args)s 最新版本号失败: 未找到版本号") % {"args": package_name}) # 记录错误信息
|
|
155
|
+
return None # 返回 None
|
|
156
|
+
|
|
157
|
+
except Exception as e: # 捕获所有异常
|
|
158
|
+
self.logger.error(_("获取 %(args)s 最新版本号出错: ") % {"args": package_name} + str(e)) # 记录错误信息
|
|
159
|
+
return None # 返回 None
|
|
160
|
+
finally:
|
|
161
|
+
end_time = time.time() # 记录结束时间
|
|
162
|
+
elapsed_time = round(end_time - start_time, 4) # 计算耗时
|
|
163
|
+
self.logger.info(_("获取最新版本号耗时: ") + f"{elapsed_time} s") # 记录耗时信息
|
|
164
|
+
|
|
165
|
+
# --------------------------------------------------------------------------------
|
|
166
|
+
# 定义 更新检查主函数
|
|
167
|
+
# --------------------------------------------------------------------------------
|
|
168
|
+
def check_for_updates(self):
|
|
169
|
+
"""
|
|
170
|
+
检查是否有新版本可用,并提示用户更新.
|
|
171
|
+
|
|
172
|
+
行为逻辑说明:
|
|
173
|
+
1. 从缓存中加载最新版本信息.
|
|
174
|
+
2. 如果缓存中没有最新版本信息,则从远程获取最新版本信息并更新缓存.
|
|
175
|
+
3. 获取当前安装的版本信息.
|
|
176
|
+
4. 比较当前版本和最新版本,如果当前版本较旧,则提示用户更新.
|
|
177
|
+
5. 如果当前版本是最新的,则提示当前版本信息.
|
|
178
|
+
"""
|
|
179
|
+
latest_version = self._load_cached_version() # 从缓存中加载最新版本信息
|
|
180
|
+
|
|
181
|
+
if latest_version:
|
|
182
|
+
latest_version = version.parse(latest_version) # 将字符串转换为版本对象
|
|
183
|
+
else:
|
|
184
|
+
latest_version = self._get_latest_version("egasp")
|
|
185
|
+
if latest_version:
|
|
186
|
+
self._update_egasp_version_cache(str(latest_version))
|
|
187
|
+
else:
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
# 获取当前安装的版本信息
|
|
191
|
+
current_version = version.parse(importlib.metadata.version("egasp"))
|
|
192
|
+
|
|
193
|
+
if current_version < latest_version:
|
|
194
|
+
print(_("有新版本可用: ") + f"[bold green]{latest_version}[/bold green] " + _("当前版本: ") + f"[bold red]{current_version}[/bold red]")
|
|
195
|
+
print(_("请运行 [bold green]'pip install --upgrade egasp'[/bold green] 进行更新"))
|
|
196
|
+
else:
|
|
197
|
+
print(_("当前版本: ") + f"[bold green]{current_version}[/bold green]")
|
egasp/core.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from egasp.data import EGP
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class EG_ASP_Core:
|
|
8
|
+
def __init__(self):
|
|
9
|
+
self.logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
def _interpolate_linear(self, x1: float, y1: float, x2: float, y2: float, x: float) -> float:
|
|
12
|
+
"""线性插值函数
|
|
13
|
+
|
|
14
|
+
根据两点 (x1,y1) 和 (x2,y2) 计算x处的插值y
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
x1 : float
|
|
19
|
+
第一个点的x坐标
|
|
20
|
+
y1 : float
|
|
21
|
+
第一个点的y坐标
|
|
22
|
+
x2 : float
|
|
23
|
+
第二个点的x坐标
|
|
24
|
+
y2 : float
|
|
25
|
+
第二个点的y坐标
|
|
26
|
+
x : float
|
|
27
|
+
插值点的x坐标
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
float
|
|
32
|
+
插值结果y
|
|
33
|
+
"""
|
|
34
|
+
return y1 + (y2 - y1) * (x - x1) / (x2 - x1)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_props(self, temp: float, conc: float, egp_key: str,
|
|
38
|
+
temp_range: tuple = (-35, 125), conc_range: tuple = (10, 90),
|
|
39
|
+
temp_step: int = 5, conc_step: float = 10) -> float | None:
|
|
40
|
+
"""根据温度和浓度获取乙二醇水溶液的物性参数
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
temp : float
|
|
45
|
+
温度, 单位摄氏度
|
|
46
|
+
conc : float
|
|
47
|
+
浓度, 质量分数 (10%~90%)
|
|
48
|
+
egp_key : str
|
|
49
|
+
物性关键词: rho cp k mu 分别对应密度、比热容、导热率和动力粘度
|
|
50
|
+
temp_range : tuple, optional
|
|
51
|
+
温度范围, 默认(-35, 125)
|
|
52
|
+
conc_range : tuple, optional
|
|
53
|
+
浓度范围, 默认(10, 90)
|
|
54
|
+
temp_step : int, optional
|
|
55
|
+
温度步长, 默认5
|
|
56
|
+
conc_step : float, optional
|
|
57
|
+
浓度步长, 默认10
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
float | None
|
|
62
|
+
对应的物性参数值, 如果输入超出范围或数据中存在None, 返回None
|
|
63
|
+
"""
|
|
64
|
+
# data_matrix: 二维列表, 行对应温度, 列对应浓度, 存储物性参数值
|
|
65
|
+
data_matrix = EGP.get(egp_key)
|
|
66
|
+
# 生成温度节点列表
|
|
67
|
+
temp_nodes = list(range(temp_range[0], temp_range[1] + 1, temp_step))
|
|
68
|
+
# 生成浓度节点列表, 避免浮点精度问题
|
|
69
|
+
conc_nodes = [round(conc_range[0] + i * conc_step, 1)
|
|
70
|
+
for i in range(int((conc_range[1] - conc_range[0]) / conc_step) + 1)]
|
|
71
|
+
|
|
72
|
+
# 检查输入参数是否在有效范围内
|
|
73
|
+
if (temp < temp_nodes[0] or temp > temp_nodes[-1] or
|
|
74
|
+
conc < conc_nodes[0] or conc > conc_nodes[-1]):
|
|
75
|
+
self.logger.error(f"查询温度 {temp} °C 浓度 {conc} % 在数据库范围之外")
|
|
76
|
+
|
|
77
|
+
# 找到相邻温度节点
|
|
78
|
+
t_lower = max(t for t in temp_nodes if t <= temp)
|
|
79
|
+
t_upper = min(t for t in temp_nodes if t >= temp)
|
|
80
|
+
|
|
81
|
+
# 找到相邻浓度节点
|
|
82
|
+
c_lower = max(c for c in conc_nodes if c <= conc)
|
|
83
|
+
c_upper = min(c for c in conc_nodes if c >= conc)
|
|
84
|
+
|
|
85
|
+
# 获取索引位置
|
|
86
|
+
t_lower_idx = temp_nodes.index(t_lower)
|
|
87
|
+
t_upper_idx = temp_nodes.index(t_upper)
|
|
88
|
+
c_lower_idx = conc_nodes.index(c_lower)
|
|
89
|
+
c_upper_idx = conc_nodes.index(c_upper)
|
|
90
|
+
|
|
91
|
+
# 提取四个角点的值
|
|
92
|
+
v11 = data_matrix[t_lower_idx][c_lower_idx]
|
|
93
|
+
v12 = data_matrix[t_lower_idx][c_upper_idx]
|
|
94
|
+
v21 = data_matrix[t_upper_idx][c_lower_idx]
|
|
95
|
+
v22 = data_matrix[t_upper_idx][c_upper_idx]
|
|
96
|
+
|
|
97
|
+
# 检查数据有效性
|
|
98
|
+
if any(v is None for v in [v11, v12, v21, v22]):
|
|
99
|
+
self.logger.error(f"查询温度 {temp} °C 浓度 {conc} % 在数据库缺失数据范围之内, 请调整输入值。")
|
|
100
|
+
exit()
|
|
101
|
+
|
|
102
|
+
# 执行双线性插值
|
|
103
|
+
if t_lower == t_upper and c_lower == c_upper:
|
|
104
|
+
return v11
|
|
105
|
+
elif t_lower == t_upper:
|
|
106
|
+
return self._interpolate_linear(c_lower, v11, c_upper, v12, conc)
|
|
107
|
+
elif c_lower == c_upper:
|
|
108
|
+
return self._interpolate_linear(t_lower, v11, t_upper, v21, temp)
|
|
109
|
+
else:
|
|
110
|
+
v1 = self._interpolate_linear(c_lower, v11, c_upper, v12, conc)
|
|
111
|
+
v2 = self._interpolate_linear(c_lower, v21, c_upper, v22, conc)
|
|
112
|
+
return self._interpolate_linear(t_lower, v1, t_upper, v2, temp)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def get_fb_props(self, query: float, query_type: str = 'volume') -> tuple[float, float, float, float] | None:
|
|
116
|
+
"""根据质量浓度或体积浓度查询乙二醇溶液的属性
|
|
117
|
+
|
|
118
|
+
Parameters
|
|
119
|
+
----------
|
|
120
|
+
query : float
|
|
121
|
+
查询的浓度值
|
|
122
|
+
query_type : str, optional
|
|
123
|
+
查询类型, 'mass' 或 'volume', 默认为 'volume'
|
|
124
|
+
|
|
125
|
+
Returns
|
|
126
|
+
-------
|
|
127
|
+
tuple[float, float, float, float] | None
|
|
128
|
+
返回对应的质量浓度、体积浓度、冰点、沸点, 如果查询值超出范围返回None
|
|
129
|
+
|
|
130
|
+
Raises
|
|
131
|
+
------
|
|
132
|
+
ValueError
|
|
133
|
+
当query_type不是'mass'或'volume'时抛出
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
# 每个元素为 (mass_conc, volume_conc, freezing, boiling)
|
|
137
|
+
data = EGP.get('fb')
|
|
138
|
+
|
|
139
|
+
# 根据查询类型排序数据
|
|
140
|
+
sort_key = 1 if query_type == 'volume' else 0
|
|
141
|
+
sorted_data = sorted(data, key=lambda x: x[sort_key])
|
|
142
|
+
|
|
143
|
+
# 遍历寻找相邻数据点
|
|
144
|
+
for i in range(len(sorted_data) - 1):
|
|
145
|
+
current = sorted_data[i]
|
|
146
|
+
next_item = sorted_data[i + 1]
|
|
147
|
+
|
|
148
|
+
current_val = current[sort_key]
|
|
149
|
+
next_val = next_item[sort_key]
|
|
150
|
+
|
|
151
|
+
if current_val <= query <= next_val:
|
|
152
|
+
# 解包数据点
|
|
153
|
+
m1, v1, f1, b1 = current
|
|
154
|
+
m2, v2, f2, b2 = next_item
|
|
155
|
+
|
|
156
|
+
# 检查数据有效性
|
|
157
|
+
if any(v is None for v in [m1, v1, f1, b1, m2, v2, f2, b2]):
|
|
158
|
+
self.logger.error(f"查询浓度 {query} % 在数据库缺失数据范围之内, 请调整输入值。")
|
|
159
|
+
exit()
|
|
160
|
+
|
|
161
|
+
# 执行插值计算
|
|
162
|
+
if query_type == 'volume':
|
|
163
|
+
mass = self._interpolate_linear(v1, m1, v2, m2, query)
|
|
164
|
+
volume = query
|
|
165
|
+
freezing = self._interpolate_linear(v1, f1, v2, f2, query)
|
|
166
|
+
boiling = self._interpolate_linear(v1, b1, v2, b2, query)
|
|
167
|
+
else:
|
|
168
|
+
volume = self._interpolate_linear(m1, v1, m2, v2, query)
|
|
169
|
+
mass = query
|
|
170
|
+
freezing = self._interpolate_linear(m1, f1, m2, f2, query)
|
|
171
|
+
boiling = self._interpolate_linear(m1, b1, m2, b2, query)
|
|
172
|
+
|
|
173
|
+
return (mass, volume, freezing, boiling)
|
|
174
|
+
|
|
175
|
+
# 查询值超出数据范围
|
|
176
|
+
self.logger.error(f"查询浓度 {query} % 超出数据")
|
|
177
|
+
exit()
|