run_this.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. import pandas as pd
  2. import numpy as np
  3. import os
  4. from datetime import datetime
  5. from sklearn.linear_model import LinearRegression
  6. # ============================================================
  7. # 粘度模型(温度修正)
  8. # ============================================================
  9. def xishan_viscosity(T):
  10. """
  11. 根据温度计算水的动力粘度(Pa·s)
  12. """
  13. x = (T + 273.15) / 300
  14. factor = 890 / (
  15. 280.68 * x ** -1.9 +
  16. 511.45 * x ** -7.7 +
  17. 61.131 * x ** -19.6 +
  18. 0.45903 * x ** -40
  19. )
  20. return 0.00089 / factor
  21. # ============================================================
  22. # 字段映射(适配不同机组)
  23. # ============================================================
  24. def get_unit_cols(unit, dpt_type="DPT_1"):
  25. dpt_map = {
  26. "DPT_1": "C.M.{}_DB@DPT_1",
  27. "DPT_2": "C.M.{}_DB@DPT_2"
  28. }
  29. return {
  30. "flux": f"{unit}_FluxF",
  31. "dpt": dpt_map[dpt_type].format(unit)
  32. }
  33. # ============================================================
  34. # 膜阻力计算
  35. # ============================================================
  36. def compute_resistance(df, unit, dpt_type="DPT_1"):
  37. cols = get_unit_cols(unit, dpt_type)
  38. flux = df[cols["flux"]]
  39. dpt = df[cols["dpt"]]
  40. mu = xishan_viscosity(df["C.M.RO_TT_ZJS@out"])
  41. df["R1"] = dpt * 3.6e12 / (mu * flux)
  42. return df
  43. # ============================================================
  44. # 累积产水量计算
  45. # ============================================================
  46. def compute_V(df, unit, dpt_type="DPT_1"):
  47. cols = get_unit_cols(unit, dpt_type)
  48. flux = df[cols["flux"]].values
  49. dt = 300
  50. J = flux * (1e-3 / 3600)
  51. V = np.zeros(len(df))
  52. for i in range(1, len(df)):
  53. V[i] = V[i - 1] + J[i - 1] * dt
  54. df["V"] = V
  55. return df
  56. # ============================================================
  57. # 当前周期拟合(幂律模型)
  58. # ============================================================
  59. def fit_model(df, R_col):
  60. """
  61. 拟合 R = a * V^b
  62. """
  63. df = df.dropna(subset=[R_col, "V"])
  64. df = df[(df[R_col] > 0) & (df["V"] > 0)]
  65. if len(df) < 10:
  66. return None
  67. V = df["V"].values
  68. R = df[R_col].values / 1e12
  69. X = np.log(V + 1e-9).reshape(-1, 1)
  70. y = np.log(R + 1e-9)
  71. model = LinearRegression()
  72. model.fit(X, y)
  73. return model
  74. # ============================================================
  75. # 读取历史周期库
  76. # ============================================================
  77. def load_history(dpt_type="DPT_1"):
  78. """
  79. 根据压差类型加载对应历史库
  80. """
  81. return pd.read_csv(HIST_PATHS[dpt_type])
  82. # ============================================================
  83. # 当前周期特征提取
  84. # ============================================================
  85. def extract_features(df, unit, dpt_type="DPT_1"):
  86. cols = get_unit_cols(unit, dpt_type)
  87. return {
  88. "flux_mean": df[cols["flux"]].mean(),
  89. "cond_mean": df["C.M.RO_Cond_ZJS@out"].mean(),
  90. "ph_mean": df["C.M.RO_PH_ZJS@out"].mean(),
  91. "orp_mean": df["C.M.RO_ORP_ZJS@out"].mean(),
  92. "temp_mean": df["C.M.RO_TT_ZJS@out"].mean()
  93. }
  94. # ============================================================
  95. # 历史周期匹配(KNN + 加权)
  96. # ============================================================
  97. def match_history(hist_df, feat, unit, k=5):
  98. """
  99. 在历史周期中寻找最相似的 k 个周期
  100. 并加权得到 a_hist, b_hist
  101. """
  102. df = hist_df[hist_df["unit"] == unit].copy()
  103. feature_cols = ["flux_mean", "cond_mean", "ph_mean", "orp_mean", "temp_mean"]
  104. X_hist = df[feature_cols].values
  105. x_now = np.array([feat[c] for c in feature_cols]).reshape(1, -1)
  106. # 标准化
  107. mean = X_hist.mean(axis=0)
  108. std = X_hist.std(axis=0) + 1e-9
  109. Xn = (X_hist - mean) / std
  110. xn = (x_now - mean) / std
  111. # 欧氏距离
  112. dist = np.linalg.norm(Xn - xn, axis=1)
  113. df["dist"] = dist
  114. df = df.sort_values("dist").head(k)
  115. # 权重
  116. w = 1 / (df["dist"].values + 1e-6)
  117. w = w / w.sum()
  118. a_hist = np.sum(w * df["a"].values)
  119. b_hist = np.sum(w * df["b"].values)
  120. return a_hist, b_hist
  121. # ============================================================
  122. # 根据 a, b 解析求解 V_limit
  123. # ============================================================
  124. def solve_v_limit_ab(a, b, R_limit):
  125. """
  126. 解 R_limit = a * V^b
  127. """
  128. if abs(b) < 1e-12:
  129. return np.inf
  130. V_limit = (R_limit / 1e12 / a) ** (1 / b)
  131. return V_limit
  132. # ============================================================
  133. # 单机组处理(核心函数)
  134. # ============================================================
  135. def process_unit(unit, df_new, TMP_limit=0.23, dpt_type="DPT_1"):
  136. df = df_new.copy()
  137. # =========================
  138. # 计算 R 和 V
  139. # =========================
  140. df = compute_resistance(df, unit, dpt_type)
  141. df = compute_V(df, unit, dpt_type)
  142. # =========================
  143. # 当前周期时长(天)
  144. # =========================
  145. days = max(
  146. (df["time"].max() - df["time"].min()).total_seconds() / 86400,
  147. 1e-6
  148. )
  149. # =========================
  150. # 当前状态
  151. # =========================
  152. mu_avg = xishan_viscosity(df["C.M.RO_TT_ZJS@out"]).mean()
  153. cols = get_unit_cols(unit, dpt_type)
  154. flux_avg = df[cols["flux"]].mean()
  155. V_now = df["V"].iloc[-1]
  156. R_now = df["R1"].iloc[-1]
  157. V_daily = V_now / days
  158. # =========================
  159. # 当前周期拟合
  160. # =========================
  161. model = fit_model(df, "R1")
  162. if model is None:
  163. return None
  164. a_cur = np.exp(model.intercept_)
  165. b_cur = model.coef_[0]
  166. # =========================
  167. # 历史参数(仅用于修正)
  168. # =========================
  169. hist_df = load_history(dpt_type)
  170. feat = extract_features(df, unit, dpt_type)
  171. a_hist, b_hist= match_history(hist_df, feat, unit)
  172. # =========================
  173. # 时间权重(只依赖周期长度)
  174. # 60天后完全关闭历史
  175. # =========================
  176. w = max(0.0, 1.0 - days / 60.0)
  177. w = w ** 2 # 加速衰减(关键)
  178. # =========================
  179. # 数值保护
  180. # =========================
  181. a_cur_safe = max(a_cur, 1e-12)
  182. b_cur_safe = np.sign(b_cur) * max(abs(b_cur), 1e-6)
  183. # =========================
  184. # a 修正(对数空间,更稳定)
  185. # 最大允许 ±20%
  186. # =========================
  187. log_a_cur = np.log(a_cur_safe)
  188. log_a_hist = np.log(max(a_hist, 1e-12))
  189. delta_log_a = w * (log_a_hist - log_a_cur)
  190. delta_log_a = np.clip(delta_log_a, -0.2, 0.2)
  191. a_final = np.exp(log_a_cur + delta_log_a)
  192. # =========================
  193. # b 修正(比例修正)
  194. # 最大允许 ±15%(更严格)
  195. # =========================
  196. delta_b = w * (b_hist - b_cur) / (abs(b_cur) + 1e-6)
  197. delta_b = np.clip(delta_b, -0.15, 0.15)
  198. b_final = b_cur * (1 + delta_b)
  199. # =========================
  200. # 物理约束(防止异常)
  201. # =========================
  202. b_final = np.clip(b_final, 0.01, 3.0)
  203. # =========================
  204. # TMP → R_limit
  205. # =========================
  206. R_limit = TMP_limit * 3.6e12 / (mu_avg * flux_avg)
  207. # =========================
  208. # 求解 V_limit
  209. # =========================
  210. V_limit = solve_v_limit_ab(a_final, b_final, R_limit)
  211. # 数值安全限制
  212. if (not np.isfinite(V_limit)) or (V_limit <= 0):
  213. V_limit = V_now
  214. V_limit = max(V_limit, V_now)
  215. # 剩余时间计算
  216. remaining_days = (V_limit - V_now) / max(V_daily, 1e-9)
  217. # 异常检测
  218. if remaining_days <= 0 or not np.isfinite(remaining_days):
  219. cols = get_unit_cols(unit, dpt_type)
  220. return {
  221. "unit": unit,
  222. "status": "ERROR",
  223. "error_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  224. "error_var": cols["dpt"], # 当前使用的压差变量
  225. "V_now": V_now,
  226. "V_limit": V_limit,
  227. "a_final": a_final,
  228. "b_final": b_final
  229. }
  230. # 正常情况
  231. remaining_days = min(remaining_days, 90 - days)
  232. return {
  233. "unit": unit,
  234. "R_now": R_now,
  235. "V_now": V_now,
  236. "R_limit": R_limit,
  237. "V_limit": V_limit,
  238. "remaining_days": remaining_days,
  239. "a_final": a_final,
  240. "b_final": b_final,
  241. }
  242. # ============================================================
  243. # 主函数
  244. # ============================================================
  245. # ============================================================
  246. # 路径配置
  247. # ============================================================
  248. BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
  249. HIST_PATHS = {
  250. "DPT_1": f"{BASE_DIR}/use_data/cycle_rv_seg1_results.csv",
  251. "DPT_2": f"{BASE_DIR}/use_data/cycle_rv_seg2_results.csv"
  252. }
  253. FEATURE_PATH = f"{BASE_DIR}/use_data/test_cycle_sample.csv" # todo: 需要改,数据从水厂来,当前CIP周期从起始时刻到当前时刻,5min级别,问工厂或代码判断cip起始时间
  254. def main(units, TMP_limit=0.21, dpt_type="DPT_1"):
  255. # TMP_limit 工艺常量
  256. df_new = pd.read_csv(FEATURE_PATH)
  257. df_new["time"] = pd.to_datetime(df_new["time"])
  258. results = []
  259. for u in units:
  260. res = process_unit(u, df_new, TMP_limit, dpt_type)
  261. if res is None:
  262. results.append({
  263. "unit": u,
  264. "status": "ERROR",
  265. "error_time": None,
  266. "error_var": None,
  267. "error_type": "MODEL_FIT_FAILED"
  268. })
  269. continue
  270. if "status" in res and res["status"] == "ERROR":
  271. results.append(res)
  272. else:
  273. res["status"] = "OK"
  274. results.append(res)
  275. return pd.DataFrame(results)
  276. # ============================================================
  277. # 运行入口
  278. # ============================================================
  279. if __name__ == "__main__":
  280. units_to_run = ["RO4"]
  281. out = main(["RO4"], TMP_limit=0.19, dpt_type="DPT_2")
  282. # 防止空结果
  283. if out is None or len(out) == 0:
  284. print("无有效结果")
  285. else:
  286. row = out.iloc[0]
  287. if row["status"] == "ERROR":
  288. print("系统异常:")
  289. print(f"时间: {row['error_time']}")
  290. print(f"变量: {row['error_var']}")
  291. else:
  292. print(f"该机组预计下次CIP为:{row['remaining_days']:.2f} 天后")