Pārlūkot izejas kodu

```
feat(DHU): 增加对无后表冷设备的控制逻辑

在配置读取中增加对“有后表冷”字段的判断,若设备不存在后表冷,则将 coil_3_Val 设置为 0,
以避免无效控制输出。

feat(DHU): 引入 numpy 并优化实时优化流程注释

引入 numpy 模块以支持数值计算;同时清理 optimize 函数中的过程注释,使代码更清晰。
此外,移除异常处理中不必要的 raise 语句,提升程序健壮性。

feat(DHU): 实现动态约束条件构建机制

根据不同设备类型(DHU_A/B 或 SDHU_A/B)生成对应的优化约束条件,包括送风露点、前后再生温
差上下限等,并传递给优化器进行求解。

feat(DHU): 调整模型训练样本数量并增强异常日志输出

将房间模型训练的样本数从 1000 改为 24*60,提高训练数据代表性。同时在训练异常时打印具体
设备名称和异常信息,便于调试追踪。

feat(DHU): 修正 DHU_B 类型转轮组件与数据清洗逻辑

修复 DHU_B 型设备中 wheel_2 默认值错误的问题,并根据 exist_Fa_B 标志动态添加
mixed_2_ToutA 列。优化数据清洗流程,支持按 input/observed 类型过滤列并去除异常值。

feat(SDHU): 修正模型输入列及实现 SDHU_B 型设备建模逻辑

更新 SDHU 设备 val_rw_adj_target 字段为 coil_2 相关参数;补全 SDHU_B 型设备的 model_B
函数定义,并修复训练数据中 fan_2_Hz 输入错误的问题。增加 AirFlow_SDHU_B 类用于风量建模。

refactor(DHU): 统一 waste 计算中 numpy 和 pymc 变量判断逻辑

在 cal_Q_waste 函数中统一使用 WHERE 变量处理 numpy 数组或 Theano 张量,提升兼容性和可读性。
```

zhangshenhao 4 mēneši atpakaļ
vecāks
revīzija
412af18ce8
5 mainītis faili ar 127 papildinājumiem un 26 dzēšanām
  1. 3 0
      apps/DHU/config_reader.py
  2. 18 14
      apps/DHU/optimize.py
  3. 2 1
      apps/DHU/train.py
  4. 10 4
      model/DHU/DHU_AB.py
  5. 94 7
      model/DHU/SDHU_AB.py

+ 3 - 0
apps/DHU/config_reader.py

@@ -74,6 +74,9 @@ class ConfigReader:
                     if room_id > num_room:
                         point_map.pop(point_name)
         
+        if not self.get_equp_info(equp_name,'有后表冷','bool'):
+            if 'coil_3_Val' in point_map:
+                point_map['coil_3_Val'] = 0
         return point_map
     
     def get_point_info(self,equp_name,point_name) -> dict:

+ 18 - 14
apps/DHU/optimize.py

@@ -4,6 +4,7 @@ from pathlib import Path
 from pprint import pprint
 from typing import Union
 
+import numpy as np
 import pandas as pd
 
 from ...model.DHU.DHU_AB import DHU_AB
@@ -45,15 +46,6 @@ def optimize(*inputs,config=None):
         config_reader.all_equp_names,
         config_reader.all_equp_names_short
     ):
-        # 加载模型
-        # 加载数据
-        # 运行判断
-        #   稳态判断:房间露点设定值与反馈值是否接近
-        #   模型调整
-        #   模型判断:模型精度是否满足要求
-        #   模式判断:
-        #       1. 基于当前露点优化模式:基于房间露点设定值减偏差
-        #       2. 快速提升送风露点模式:约束送风露点保持不变
         
         if not config_reader.get_app_info(each_eaup_name,'实时优化','开始实时优化','bool'):
             continue
@@ -78,7 +70,6 @@ def optimize(*inputs,config=None):
                 continue
         except Exception as e:
             ALL_RESULT['EXCEPTION']['State'][each_eaup_name] = e
-            raise e
             continue
         
         # 加载模型
@@ -118,7 +109,6 @@ def optimize(*inputs,config=None):
             ALL_OPT_RES.append(opt_result['opt_summary'][1].assign(equp_name=each_eaup_name))
         except Exception as e:
             ALL_RESULT['EXCEPTION']['Opt'][each_eaup_name] = e
-            raise e
             continue
             
         try:
@@ -294,8 +284,22 @@ def optimize_dhu(
     )
     print(TVP_data)
     
-    # 实时优化
+    # 约束条件
+    constrains = []
+    if each_equp_type in ['DHU_A','DHU_B']:
+        constrains.append('#送风露点约束# coil_3_DoutA-[coil_3_DoutA]<0')
+        Tdup = config_reader.get_equp_info(each_eaup_name,key='前后再生差值上限',info_type='float')
+        Tddw = config_reader.get_equp_info(each_eaup_name,key='前后再生差值下限',info_type='float')
+        if not np.isnan(Tdup):
+            constrains.append(f'#前后再生差值上限# (wheel_1_TinR-wheel_2_TinR)-{Tdup}<0')
+        if not np.isnan(Tddw):
+            constrains.append(f'#前后再生差值下限# {Tddw}-(wheel_1_TinR-wheel_2_TinR)<0')
+    elif each_equp_type in ['SDHU_A','SDHU_B']:
+        constrains.append('#送风露点约束# wheel_1_DoutP-[wheel_1_DoutP]<0')
+    print('约束条件')
+    pprint(constrains)
     
+    # 实时优化
     if each_equp_type in ['DHU_A','DHU_B']:
         opt_res = MODEL.optimize(
             cur_input_data=data_cur,
@@ -308,7 +312,7 @@ def optimize_dhu(
                 config_reader.get_equp_info(each_eaup_name,key='后再生温度下限',info_type='float')
             ),
             fan_2_Hz   = None,
-            constrains = ['coil_3_DoutA-[coil_3_DoutA]<0'],
+            constrains = constrains,
             logging    = False,
             target     = 'summary_waste',
             target_min = True
@@ -321,7 +325,7 @@ def optimize_dhu(
                 config_reader.get_equp_info(each_eaup_name,key='前再生温度下限',info_type='float')
             ),
             fan_2_Hz   = (30,45),
-            constrains = ['wheel_1_DoutP-[wheel_1_DoutP]<0'],
+            constrains = constrains,
             logging    = False,
             target     = 'summary_waste',
             target_min = True

+ 2 - 1
apps/DHU/train.py

@@ -62,6 +62,7 @@ def train(*inputs,config=None):
             )
         except Exception as E:
             ALL_RESULT['EXCEPTION']['Fit'][each_eaup_name] = E
+            print(f'{each_eaup_name}模型训练异常 {E}')
             continue
         
         # 保存可视化结果
@@ -199,7 +200,7 @@ def train_room_model(each_eaup_name,each_equp_type,equp_data,config_reader:Confi
     if not config_reader.get_app_info(each_eaup_name,'模型训练','训练房间模型','bool'):
         return None
     
-    N_fit = 1000
+    N_fit = 24 * 60
     equp_model_path = f'{config_reader_path}/model/{each_eaup_name}.pkl'
     if each_equp_type in ['DHU_A','DHU_B']:
         equp_model = DHU_AB.load(equp_model_path)

+ 10 - 4
model/DHU/DHU_AB.py

@@ -43,7 +43,7 @@ class DHU_AB(BaseDevice):
             wheel_2 = wheel_2 if wheel_2 is not None else 'WheelS3V2'
         elif self.DHU_type == 'B':
             wheel_1 = wheel_1 if wheel_1 is not None else 'WheelS2V2'
-            wheel_2 = wheel_2 if wheel_2 is not None else 'WheelS3V3'
+            wheel_2 = wheel_2 if wheel_2 is not None else 'WheelS3V2'
         else:
             raise Exception('DHU_type must be A or B')
         
@@ -120,11 +120,12 @@ class DHU_AB(BaseDevice):
             'coil_2_DoutA' : 'coil_2_DoutA',
             'wheel_2_ToutP': 'wheel_2_ToutP',
             'wheel_2_DoutP': 'wheel_2_DoutP',
-            'mixed_2_ToutA': 'mixed_2_ToutA',   # 涉及前再生加热盘管的热量
             'wheel_2_ToutC': 'wheel_2_ToutC',   # 涉及后再生加热盘管的热量
         }
         if self.DHU_type == 'A':
             columns['wheel_1_ToutC'] = 'wheel_1_ToutC' # A类除湿机前转轮是三分转轮
+        if self.exist_Fa_B:
+            columns['mixed_2_ToutA'] = 'mixed_2_ToutA'
         
         for idx in [1,2]:
             heatingcoil_idx = f'heatingcoil_{idx}'
@@ -196,7 +197,7 @@ class DHU_AB(BaseDevice):
                         'heatingcoil_2_Fs': 20,
                         'heatingcoil_1_FP': 10000,
                         'heatingcoil_2_FP': 10000,
-                    }.get(std_name,1)
+                    }.get(std_name,10)
                 
                 observe(
                     name     = std_name,
@@ -242,15 +243,19 @@ class DHU_AB(BaseDevice):
         fill_zero    : bool = False,
         save_log     : Union[str,None]  = None
     ) -> pd.DataFrame:
+        
         data = data.replace(-9999,np.nan)
         clean_data = DataCleaner(data,print_process=print_process)
         
+        filter_columns = []
         if 'input' in data_type:
+            filter_columns += list(self.model_input_data_columns.values())
             clean_data = (
                 clean_data
                 .rm_rolling_fluct(window=60,fun='ptp',thre=0.1,include_cols=['State'])
                 .rm_rule('State != 1')
                 .rm_outrange(method='raw',upper=140,lower=20,include_cols=['wheel_1_TinR','wheel_2_TinR'])
+                .rm_outrange(method='quantile',upper=0.95,lower=0.05,include_cols=filter_columns)
             )
             if self.DHU_type == 'A':
                 clean_data = (
@@ -258,11 +263,12 @@ class DHU_AB(BaseDevice):
                     .rm_rule('wheel_1_ToutC<=coil_1_ToutA')
                 )
         if 'observed' in data_type:
-            pass
+            filter_columns += list(self.model_observe_data_columns.values())
         clean_data = clean_data.get_data(
             fill     = 0 if fill_zero else None,
             save_log = save_log
         )
+        clean_data = clean_data.loc[:,filter_columns]
         return clean_data
 
     

+ 94 - 7
model/DHU/SDHU_AB.py

@@ -19,7 +19,7 @@ from ...tools.data_cleaner import DataCleaner
 
 class SDHU_AB(BaseDevice):
     
-    val_rw_adj_target = ('coil_3_ToutA','coil_3_DoutA')
+    val_rw_adj_target = ('coil_2_ToutA','coil_2_DoutA')
     
     def __init__(
         self,
@@ -116,8 +116,7 @@ class SDHU_AB(BaseDevice):
         if self.DHU_type == 'A':
             return model_A(*args,**kwargs)
         elif self.DHU_type == 'B':
-            # return model_B(*args,**kwargs)
-            pass
+            return model_B(*args,**kwargs)
         else:
             raise ValueError('DHU_type must be A or B')
     
@@ -162,7 +161,7 @@ class SDHU_AB(BaseDevice):
             model        = reorder_posterior(param_prior,self.param_posterior),
             train_data   = {
                 'wheel_1_TinR': observed_data.loc[:,'wheel_1_TinR'].values,
-                'fan_2_Hz'    : observed_data.loc[:,'wheel_2_TinR'].values,
+                'fan_2_Hz'    : observed_data.loc[:,'fan_2_Hz'].values,
                 'coil_2_DoutA': observed_data.loc[:,'coil_2_DoutA'].values,
             },
             train_metric = {'R2':1,'MAE':1,'MAPE':1}
@@ -191,7 +190,9 @@ class SDHU_AB(BaseDevice):
         data = data.replace(-9999,np.nan)
         clean_data = DataCleaner(data,print_process=print_process)
         
+        filter_columns = []
         if 'input' in data_type:
+            filter_columns += list(self.model_input_data_columns.values())
             clean_data = (
                 clean_data
                 .rm_rolling_fluct(window=60,fun='ptp',thre=0.1,include_cols=['State'])
@@ -200,7 +201,7 @@ class SDHU_AB(BaseDevice):
                 .rm_outrange(method='raw',upper=140,lower=20,include_cols=['wheel_1_TinR'])
             )
         if 'observed' in data_type:
-            pass
+            filter_columns += list(self.model_observe_data_columns.values())
         clean_data = clean_data.get_data(
             fill     = 0 if fill_zero else None,
             save_log = save_log
@@ -418,7 +419,83 @@ def model_A(
         }
     }
         
+def model_B(
+    Tin_Fr,        # 再生侧入口温度
+    Hin_Fr,        # 再生侧入口湿度
+    coil_1_ToutA,
+    coil_1_HoutA,
+    fan_1_Hz,     # 处理侧风机频率 
+    fan_2_Hz,     # 再生侧风机频率
+    coil_2_TinW,  # 中表冷进水温度
+    coil_2_Val,   # 中表冷阀门开度
+    wheel_1_TinR, # 前转轮再生侧温度
+    engine    : str,
+    components: dict,
+    param     : dict,
+    mixed_1_TinM = 0, # 回风温度(处理侧)
+    mixed_1_HinM = 0, # 回风湿度(处理侧)
+) ->  dict:
+    # 水的质量流量
+    coil_2_FW = coil_2_Val / 100
+    # 空气的质量流量
+    air_flow = AirFlow_SDHU_B.model(fan_1_Hz=fan_1_Hz,fan_2_Hz=fan_2_Hz,param=param)
     
+    # 前转轮
+    wheel_1_res = components['wheel_1'].model(
+        TinP   = coil_1_ToutA,
+        HinP   = coil_1_HoutA,
+        FP     = air_flow['wheel_1_FaP'],
+        TinR   = wheel_1_TinR,
+        HinR   = Hin_Fr,
+        FR     = air_flow['wheel_1_FaR'],
+        engine = engine,
+        param  = param['wheel_1']
+    )
+    # 处理侧混风(回风)
+    mixed_1_res = components['mixed_1'].model(
+        TinA   = wheel_1_res['ToutP'],
+        HinA   = wheel_1_res['HoutP'],
+        FA     = air_flow['mixed_1_FaA'],
+        TinM   = mixed_1_TinM,
+        HinM   = mixed_1_HinM,
+        FM     = air_flow['mixed_1_FaM'],
+        engine = engine
+    )
+    # 中表冷
+    coil_2_res = components['coil_2'].model(
+        TinA   = mixed_1_res['ToutA'],
+        HinA   = mixed_1_res['HoutA'],
+        FA     = air_flow['coil_2_FaA'],
+        TinW   = coil_2_TinW,
+        FW     = coil_2_FW,
+        engine = engine,
+        param  = param['coil_2']
+    )
+    # 前再生加热盘管
+    heatingcoil_1_res = components['heatingcoil_1'].model(
+        TinA   = Tin_Fr,
+        ToutA  = wheel_1_TinR,
+        FA     = air_flow['heatingcoil_1_Fa'],
+        param  = param['heatingcoil_1'],
+        engine = engine
+    )
+    waste = cal_Q_waste(
+        wheel_1_res       = wheel_1_res,
+        heatingcoil_1_res = heatingcoil_1_res,
+        wheel_1_TinR      = wheel_1_TinR,
+        fan_2_Hz          = fan_2_Hz
+    )
+    return {
+        'coil_2'       : coil_2_res,
+        'wheel_1'      : wheel_1_res,
+        'mixed_1'      : mixed_1_res,
+        'heatingcoil_1': heatingcoil_1_res,
+        'Fa'           : air_flow,
+        'summary'      : {
+            'Fs'  : heatingcoil_1_res['Fs'],
+            **waste,
+        }
+    }
 
 class AirFlow_SDHU_A:
     
@@ -454,6 +531,11 @@ class AirFlow_SDHU_A:
             param['HzP_H']  = pm.HalfNormal('F_air_HzP_H',sigma=1,initval=0.5)
         return param
         
+class AirFlow_SDHU_B:
+    @classmethod
+    def model(cls,fan_1_Hz,fan_2_Hz,param):
+        ...
+
         
 
 def cal_Q_waste(
@@ -469,8 +551,13 @@ def cal_Q_waste(
         waste = 0.25 * (1 - np.exp(-0.04 * (TinR - 70)))
         return np.where(waste>0,waste,0)
     
+    if isinstance(wheel_1_res['Qsen'],np.ndarray):
+        WHERE = np.where
+    else:
+        WHERE = pm.math.switch
+    
     heatingcoil_1_Q = heatingcoil_1_res['Q']
-    heatingcoil_1_Q = np.where(heatingcoil_1_Q>0,heatingcoil_1_Q,0)
+    heatingcoil_1_Q = WHERE(heatingcoil_1_Q>0,heatingcoil_1_Q,0)
     
     waste_Qsen1 = wheel_1_res['Qsen']
     waste_cond1 = heatingcoil_1_Q * waste_cond_func1(wheel_1_TinR)
@@ -481,7 +568,7 @@ def cal_Q_waste(
     }
     
     waste_out = heatingcoil_1_Q - wheel_1_res['Qsen'] - wheel_1_res['Qlat']
-    waste_out = np.where(waste_out>0,waste_out,0)
+    waste_out = WHERE(waste_out>0,waste_out,0)
     waste = waste_Qsen1 + waste_cond1 + waste_out + fan_2_Hz/100
     res['waste_out']   = waste_out
     res['waste'] = waste