Files
Uni-Lab-OS/unilabos/compile/evaporate_protocol.py
KCFeng425 4c6e437eb1 修复了部分的protocol因为XDL更新导致的问题
但是pumptransfer,add,dissolve,separate还没修,后续还需要写virtual固体加料器
2025-07-06 19:21:53 +08:00

388 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from typing import List, Dict, Any, Optional
import networkx as nx
import logging
from .pump_protocol import generate_pump_protocol
logger = logging.getLogger(__name__)
def debug_print(message):
"""调试输出"""
print(f"[EVAPORATE] {message}", flush=True)
logger.info(f"[EVAPORATE] {message}")
def get_vessel_liquid_volume(G: nx.DiGraph, vessel: str) -> float:
"""获取容器中的液体体积"""
debug_print(f"检查容器 '{vessel}' 的液体体积...")
if vessel not in G.nodes():
debug_print(f"容器 '{vessel}' 不存在")
return 0.0
vessel_data = G.nodes[vessel].get('data', {})
debug_print(f"容器数据: {vessel_data}")
# 检查多种体积字段
volume_keys = ['total_volume', 'volume', 'liquid_volume', 'current_volume']
for key in volume_keys:
if key in vessel_data:
try:
volume = float(vessel_data[key])
debug_print(f"'{key}' 读取到体积: {volume}mL")
return volume
except (ValueError, TypeError):
continue
# 检查liquid数组
liquids = vessel_data.get('liquid', [])
if isinstance(liquids, list):
total_volume = 0.0
for liquid in liquids:
if isinstance(liquid, dict):
for vol_key in ['liquid_volume', 'volume', 'amount']:
if vol_key in liquid:
try:
vol = float(liquid[vol_key])
total_volume += vol
debug_print(f"从液体数据 '{vol_key}' 读取: {vol}mL")
except (ValueError, TypeError):
continue
if total_volume > 0:
return total_volume
debug_print(f"未检测到液体体积,返回 0.0")
return 0.0
def find_rotavap_device(G: nx.DiGraph) -> Optional[str]:
"""查找旋转蒸发仪设备"""
debug_print("查找旋转蒸发仪设备...")
# 查找各种可能的旋转蒸发仪设备
possible_devices = []
for node in G.nodes():
node_data = G.nodes[node]
node_class = node_data.get('class', '')
if any(keyword in node_class.lower() for keyword in ['rotavap', 'evaporator']):
possible_devices.append(node)
debug_print(f"找到旋转蒸发仪设备: {node}")
if possible_devices:
return possible_devices[0]
debug_print("未找到旋转蒸发仪设备")
return None
def find_rotavap_vessel(G: nx.DiGraph) -> Optional[str]:
"""查找旋转蒸发仪样品容器"""
debug_print("查找旋转蒸发仪样品容器...")
possible_vessels = [
"rotavap", "rotavap_flask", "flask_rotavap",
"evaporation_flask", "evaporator", "rotary_evaporator"
]
for vessel in possible_vessels:
if vessel in G.nodes():
debug_print(f"找到旋转蒸发仪样品容器: {vessel}")
return vessel
debug_print("未找到旋转蒸发仪样品容器")
return None
def find_recovery_vessel(G: nx.DiGraph) -> Optional[str]:
"""查找溶剂回收容器"""
debug_print("查找溶剂回收容器...")
possible_vessels = [
"flask_distillate", "distillate", "solvent_recovery",
"rotavap_condenser", "condenser", "waste_workup", "waste"
]
for vessel in possible_vessels:
if vessel in G.nodes():
debug_print(f"找到回收容器: {vessel}")
return vessel
debug_print("未找到回收容器")
return None
def generate_evaporate_protocol(
G: nx.DiGraph,
vessel: str,
pressure: float = 0.1,
temp: float = 60.0,
time: float = 1800.0,
stir_speed: float = 100.0,
solvent: str = "",
**kwargs # 接受任意额外参数,增强兼容性
) -> List[Dict[str, Any]]:
"""
生成蒸发操作的协议序列 - 增强兼容性版本
Args:
G: 设备图
vessel: 蒸发容器名称(必需)
pressure: 真空度 (bar)默认0.1
temp: 加热温度 (°C)默认60
time: 蒸发时间 (秒)默认1800
stir_speed: 旋转速度 (RPM)默认100
solvent: 溶剂名称(可选,用于参数优化)
**kwargs: 其他参数(兼容性)
Returns:
List[Dict[str, Any]]: 动作序列
"""
debug_print("=" * 50)
debug_print("开始生成蒸发协议")
debug_print(f"输入参数:")
debug_print(f" - vessel: {vessel}")
debug_print(f" - pressure: {pressure} bar")
debug_print(f" - temp: {temp}°C")
debug_print(f" - time: {time}s ({time/60:.1f}分钟)")
debug_print(f" - stir_speed: {stir_speed} RPM")
debug_print(f" - solvent: '{solvent}'")
debug_print(f" - 其他参数: {kwargs}")
debug_print("=" * 50)
action_sequence = []
# === 参数验证和修正 ===
debug_print("步骤1: 参数验证和修正...")
# 验证必需参数
if not vessel:
raise ValueError("vessel 参数不能为空")
if vessel not in G.nodes():
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
# 修正参数范围
if pressure <= 0 or pressure > 1.0:
debug_print(f"真空度 {pressure} bar 超出范围,修正为 0.1 bar")
pressure = 0.1
if temp < 10.0 or temp > 200.0:
debug_print(f"温度 {temp}°C 超出范围,修正为 60°C")
temp = 60.0
if time <= 0:
debug_print(f"时间 {time}s 无效,修正为 1800s")
time = 1800.0
if stir_speed < 10.0 or stir_speed > 300.0:
debug_print(f"旋转速度 {stir_speed} RPM 超出范围,修正为 100 RPM")
stir_speed = 100.0
# 根据溶剂优化参数
if solvent:
debug_print(f"根据溶剂 '{solvent}' 优化参数...")
solvent_lower = solvent.lower()
if any(s in solvent_lower for s in ['water', 'aqueous', 'h2o']):
temp = max(temp, 80.0)
pressure = max(pressure, 0.2)
debug_print("水系溶剂:提高温度和真空度")
elif any(s in solvent_lower for s in ['ethanol', 'methanol', 'acetone']):
temp = min(temp, 50.0)
pressure = min(pressure, 0.05)
debug_print("易挥发溶剂:降低温度和真空度")
elif any(s in solvent_lower for s in ['dmso', 'dmi', 'toluene']):
temp = max(temp, 100.0)
pressure = min(pressure, 0.01)
debug_print("高沸点溶剂:提高温度,降低真空度")
debug_print(f"最终参数: pressure={pressure}, temp={temp}, time={time}, stir_speed={stir_speed}")
# === 查找设备 ===
debug_print("步骤2: 查找设备...")
# 查找旋转蒸发仪设备
rotavap_device = find_rotavap_device(G)
if not rotavap_device:
debug_print("未找到旋转蒸发仪设备,使用通用设备")
rotavap_device = "rotavap_1" # 默认设备ID
# 查找旋转蒸发仪样品容器
rotavap_vessel = find_rotavap_vessel(G)
if not rotavap_vessel:
debug_print("未找到旋转蒸发仪样品容器,使用默认容器")
rotavap_vessel = "rotavap" # 默认容器
# 查找回收容器
recovery_vessel = find_recovery_vessel(G)
debug_print(f"设备配置:")
debug_print(f" - 旋转蒸发仪设备: {rotavap_device}")
debug_print(f" - 样品容器: {rotavap_vessel}")
debug_print(f" - 回收容器: {recovery_vessel}")
# === 体积计算 ===
debug_print("步骤3: 体积计算...")
source_volume = get_vessel_liquid_volume(G, vessel)
if source_volume > 0:
transfer_volume = min(source_volume * 0.9, 250.0) # 90%或最多250mL
debug_print(f"检测到液体体积 {source_volume}mL转移 {transfer_volume}mL")
else:
transfer_volume = 50.0 # 默认小体积,更安全
debug_print(f"未检测到液体体积,使用默认转移体积 {transfer_volume}mL")
# === 生成动作序列 ===
debug_print("步骤4: 生成动作序列...")
# 动作1: 转移溶液到旋转蒸发仪
if vessel != rotavap_vessel:
debug_print(f"转移 {transfer_volume}mL 从 {vessel}{rotavap_vessel}")
try:
transfer_actions = generate_pump_protocol(
G=G,
from_vessel=vessel,
to_vessel=rotavap_vessel,
volume=transfer_volume,
flowrate=2.0,
transfer_flowrate=2.0
)
action_sequence.extend(transfer_actions)
debug_print(f"添加了 {len(transfer_actions)} 个转移动作")
except Exception as e:
debug_print(f"转移失败: {str(e)}")
# 继续执行,不中断整个流程
# 等待稳定
action_sequence.append({
"action_name": "wait",
"action_kwargs": {"time": 10}
})
# 动作2: 执行蒸发
debug_print(f"执行蒸发: {rotavap_device}")
evaporate_action = {
"device_id": rotavap_device,
"action_name": "evaporate",
"action_kwargs": {
"vessel": rotavap_vessel,
"pressure": pressure,
"temp": temp,
"time": time,
"stir_speed": stir_speed,
"solvent": solvent
}
}
action_sequence.append(evaporate_action)
# 蒸发后等待
action_sequence.append({
"action_name": "wait",
"action_kwargs": {"time": 30}
})
# 动作3: 回收溶剂(如果有回收容器)
if recovery_vessel:
debug_print(f"回收溶剂到 {recovery_vessel}")
try:
recovery_volume = transfer_volume * 0.7 # 估算回收70%
recovery_actions = generate_pump_protocol(
G=G,
from_vessel="rotavap_condenser", # 假设的冷凝器
to_vessel=recovery_vessel,
volume=recovery_volume,
flowrate=3.0,
transfer_flowrate=3.0
)
action_sequence.extend(recovery_actions)
debug_print(f"添加了 {len(recovery_actions)} 个回收动作")
except Exception as e:
debug_print(f"溶剂回收失败: {str(e)}")
# 动作4: 转移浓缩物回原容器
if vessel != rotavap_vessel:
debug_print(f"转移浓缩物从 {rotavap_vessel}{vessel}")
try:
concentrate_volume = transfer_volume * 0.2 # 估算浓缩物20%
transfer_back_actions = generate_pump_protocol(
G=G,
from_vessel=rotavap_vessel,
to_vessel=vessel,
volume=concentrate_volume,
flowrate=1.0, # 浓缩物可能粘稠
transfer_flowrate=1.0
)
action_sequence.extend(transfer_back_actions)
debug_print(f"添加了 {len(transfer_back_actions)} 个转移回收动作")
except Exception as e:
debug_print(f"浓缩物转移失败: {str(e)}")
# === 总结 ===
debug_print("=" * 50)
debug_print(f"蒸发协议生成完成")
debug_print(f"总动作数: {len(action_sequence)}")
debug_print(f"处理体积: {transfer_volume}mL")
debug_print(f"蒸发参数: {pressure} bar, {temp}°C, {time}s, {stir_speed} RPM")
debug_print("=" * 50)
return action_sequence
# === 便捷函数 ===
def generate_quick_evaporate_protocol(
G: nx.DiGraph,
vessel: str,
**kwargs
) -> List[Dict[str, Any]]:
"""快速蒸发:低温短时间"""
return generate_evaporate_protocol(
G, vessel,
pressure=0.2,
temp=40.0,
time=900.0,
stir_speed=80.0,
**kwargs
)
def generate_gentle_evaporate_protocol(
G: nx.DiGraph,
vessel: str,
**kwargs
) -> List[Dict[str, Any]]:
"""温和蒸发:中等条件"""
return generate_evaporate_protocol(
G, vessel,
pressure=0.1,
temp=50.0,
time=2700.0,
stir_speed=60.0,
**kwargs
)
def generate_high_vacuum_evaporate_protocol(
G: nx.DiGraph,
vessel: str,
**kwargs
) -> List[Dict[str, Any]]:
"""高真空蒸发:低温高真空"""
return generate_evaporate_protocol(
G, vessel,
pressure=0.01,
temp=35.0,
time=3600.0,
stir_speed=120.0,
**kwargs
)
def generate_standard_evaporate_protocol(
G: nx.DiGraph,
vessel: str,
**kwargs
) -> List[Dict[str, Any]]:
"""标准蒸发:常用参数"""
return generate_evaporate_protocol(
G, vessel,
pressure=0.1,
temp=60.0,
time=1800.0,
stir_speed=100.0,
**kwargs
)