Przeglądaj źródła

refactor(model): 重构模型加载和保存功能

- 修改了 BaseModel 类的 load 和 save 方法,实现了更灵活的模型加载和保存机制
- 优化了 DHU_AB 模型的结构,提高了代码可读性和可维护性
- 新增 Room 模型的基础结构,为后续开发做好准备
- 调整了 main.py 中的模型训练逻辑,支持不同类型的设备
zhangshenhao 6 miesięcy temu
rodzic
commit
0c6fc84e94

+ 0 - 0
tools/config_reader.py → apps/DHU/config_reader.py


+ 0 - 0
apps/monitor/monitor.py → apps/DHU/monitor.py


+ 1 - 1
apps/optimize/optimize.py → apps/DHU/optimize.py

@@ -6,7 +6,7 @@ from pprint import pprint
 import pandas as pd
 
 from ...model.DHU.DHU_AB import DHU_AB
-from ...tools.config_reader import ConfigReader
+from .config_reader import ConfigReader
 from ...tools.data_loader import DataLoader
 
 NOW = datetime.now().replace(second=0,microsecond=0)

+ 2 - 2
apps/train/train.py → apps/DHU/train.py

@@ -6,7 +6,7 @@ from pprint import pprint
 import pandas as pd
 
 from ...model.DHU.DHU_AB import DHU_AB
-from ...tools.config_reader import ConfigReader
+from .config_reader import ConfigReader
 from ...tools.data_loader import DataLoader
 
 NOW             = datetime.now().replace(second=0,microsecond=0)
@@ -94,7 +94,7 @@ def train(*inputs,config=None):
                 input_data    = equp_data,
                 observed_data = equp_data,
                 plot_TVP      = False,
-                rw_FA_val     = True, #TODO
+                rw_FA_val     = config_reader.get_app_info(each_eaup_name,'模型训练','新风阀门开度参数','bool')
             )
             Path(f'{config_reader_path}/model').mkdir(parents=True, exist_ok=True)
             equp_model.save(f'{config_reader_path}/model/{each_eaup_name}.pkl')

+ 80 - 0
apps/Room/config_reader.py

@@ -0,0 +1,80 @@
+from typing import Union
+from datetime import datetime
+
+import numpy as np
+import pandas as pd
+
+from ..DHU.config_reader import ConfigReader as ConfigReader_DHU
+
+class ConfigReader:
+    
+    def __init__(
+        self,
+        path,
+        DHU_AB_config:ConfigReader_DHU = None
+    ):
+        self.path  = path
+        self.room = pd.read_excel(self.path,sheet_name='房间')
+        self.app  = pd.read_excel(self.path,sheet_name='程序')
+        
+        self.all_room_dew = set(self.room.loc[:,'房间露点温度'].to_list())
+        self.DHU_AB_config:ConfigReader_DHU = DHU_AB_config
+    
+    def get_room_info(self,room_dew,info_name) -> str:
+        info = self.room.loc[lambda dt:dt.房间露点温度==room_dew,info_name].to_list()[0]
+        return info
+    
+    def get_point(self,room_dew) -> dict:
+        if room_dew not in self.all_room_dew:
+            raise ValueError('房间露点温度不存在')
+        point = {'Droom':room_dew}
+        if self.DHU_AB_config is not None:
+            equp_name = self.get_room_info(room_dew,'设备编号')
+            equp_point = self.DHU_AB_config.get_equp_point(
+                equp_name  = equp_name,
+                equp_type  = self.get_room_info(room_dew,'设备类型'),
+                equp_class = 'B'
+            )
+            point['Dout'] = equp_name + '_' + equp_point['wheel_2_DoutP']
+        return point
+    
+    def get_app_info(
+        self,
+        equp_name: str,
+        app_type : str,
+        key      : str,
+        info_type: str = None
+    ) -> str:
+        value = self.app.loc[lambda dt:(dt.程序类型==app_type) & (dt.项目==key)]
+        if value.shape[0] != 1:
+            raise Exception(f'程序类型({app_type})下不存在唯一的的项目({key})')
+        info_value = value.loc[:,'全局配置'].iat[0]
+        # 更新全局配置
+        if equp_name in value.columns:
+            equp_value = value.loc[:,equp_name].iat[0]
+            if isinstance(equp_value,(str,float)) and not np.isnan(equp_value):
+                info_value = equp_value
+        # 调整数据类型
+        info_value = convert_info_type(info_value,info_type)
+        
+        return info_value
+    
+def convert_info_type(info_value,info_type):
+    if info_type == 'int':
+        info_value = int(info_value)
+    elif info_type == 'float':
+        info_value = float(info_value)
+    elif info_type == 'bool':
+        info_value = bool(info_value)
+    elif info_type == 'datetime':
+        if info_value == 'NOW':
+            info_value = datetime.now()
+        elif not isinstance(info_value,datetime):
+            info_value = datetime.strptime(info_value,'%Y/%m/%d %H:%M:%S')
+    elif info_type == 'str':
+        info_value = str(info_value)
+    elif info_type is None:
+        pass
+    else:
+        raise ValueError(f"{info_type} type error!")
+    return info_value

+ 10 - 0
apps/Room/model.py

@@ -0,0 +1,10 @@
+import numpy as np
+import pandas as pd
+import statsmodels.api as sm
+
+def smooth(y:pd.Series,frac):
+    y             = y.dropna()
+    index         = y.index
+    x             = np.arange(len(y))
+    lowess_result = sm.nonparametric.lowess(y, x, frac=frac) 
+    return pd.Series(lowess_result[:, 1], index=index)

+ 61 - 0
apps/Room/train.py

@@ -0,0 +1,61 @@
+import os
+from datetime import datetime
+from pathlib import Path
+from pprint import pprint
+
+import pandas as pd
+
+from .config_reader import ConfigReader
+from ..DHU.config_reader import ConfigReader as ConfigReader_DHU
+from ..._data.main import get_data
+
+NOW             = datetime.now().replace(second=0,microsecond=0)
+PATH            = os.path.dirname(os.path.realpath(__file__)).replace('\\','/')
+MODEL_FUNC_PATH = f'{PATH}/model_func.py'
+MODEL_FILE_PATH = f'./model.pkl'
+
+def train(*inputs,config=None):
+    config = {} if config is None else config
+    
+    if '__LOCAL' in config.keys():
+        config_reader_path = config['__LOCAL']
+        data_URL           = config['__URL']
+        plot_metric        = True
+    else:
+        config_reader_path = '/mnt/workflow_data'
+        data_URL           = 'http://basedataportal-svc:8080/data/getpointsdata'
+        plot_metric        = False
+    
+    config_reader = ConfigReader(
+        path          = f'{config_reader_path}/Room.xlsx',
+        DHU_AB_config = ConfigReader_DHU(path=f'{config_reader_path}/DHU_AB.xlsx')
+    )
+    
+    # 历史数据
+    data_raw_folder = os.path.join(config_reader_path,f'data/train/data_his_raw/')
+    if not os.path.exists(data_raw_folder): 
+        os.makedirs(data_raw_folder)
+    for each_room_dew in config_reader.all_room_dew:
+        all_data = []
+        data_his_folder = os.path.join(config_reader_path,f'data/train/data_his/{each_room_dew}')
+        if not os.path.exists(data_his_folder): 
+            os.makedirs(data_his_folder)
+        
+        points = config_reader.get_point(each_room_dew)
+        for each_point_name,each_point_id in points.items():
+            file_path = os.path.join(data_his_folder,f'{each_point_name}.pkl')
+            get_data(
+                points_id  = [each_point_id],
+                time_start = config_reader.get_app_info(each_room_dew,'模型训练','开始时间','datetime'),
+                time_end   = config_reader.get_app_info(each_room_dew,'模型训练','结束时间','datetime'),
+                int_time   = 'M',
+                url        = data_URL,
+                from_cache = True,
+                PATH       = Path(file_path),
+            )
+            all_data.append(pd.read_pickle(file_path).set_axis([each_point_name],axis=1))
+        
+        all_data = pd.concat(all_data,axis=1).to_pickle(f'{data_raw_folder}/{each_room_dew}.pkl')
+        
+        
+    

+ 33 - 124
components/wheel3.py

@@ -35,6 +35,7 @@ class WheelS3(BaseComponents):
         beta_P5 = param['beta_P5']
         beta_P6 = param['beta_P6']
         beta_P7 = param['beta_P7']
+        beta_P8 = param['beta_P8']
         beta_C1 = param['beta_C1']
         beta_C2 = param['beta_C2']
         beta_C3 = param['beta_C3']
@@ -49,6 +50,7 @@ class WheelS3(BaseComponents):
             * HinP 
             * (TinR + beta_P6 * TinC + beta_P7 * TinP) 
             * EXP(-beta_P5 * FP) 
+            + beta_P8 * FR
             + beta_P2 
         ) / 1000
         WdiffP  = HdiffP * FP
@@ -62,8 +64,7 @@ class WheelS3(BaseComponents):
         # 冷却侧
         TdiffC    = beta_C1 * EXP(-beta_C2 * EXP(-beta_C3 * (TinR - TinC))) * EXP(-beta_C4 * FC)
         ToutC     = TinC + TdiffC
-        # HdiffC    = (beta_P1 * RinC**beta_P4 * HinC * TinR * EXP(-beta_P5 * FC) + beta_P2)/1000
-        HdiffC = 0
+        HdiffC    = 0
         WdiffC    = HdiffC * FC
         HoutC     = HinC - HdiffC
         DoutC     = get_Dew_from_HumRatio(HoutC,engine)
@@ -92,6 +93,7 @@ class WheelS3(BaseComponents):
             'beta_P5': pm.TruncatedNormal(f'{self.name}_beta_P5',mu=5,sigma=2,initval=5,lower=0),
             'beta_P6': pm.Normal(f'{self.name}_beta_P6',mu=0,sigma=1,initval=0),
             'beta_P7': pm.Normal(f'{self.name}_beta_P7',mu=0,sigma=1,initval=0),
+            'beta_P8': pm.Normal(f'{self.name}_beta_P8',mu=1,sigma=10,initval=1),
             'beta_C1': pm.TruncatedNormal(f'{self.name}_beta_C1',mu=60,sigma=10,initval=60,lower=10),
             'beta_C2': pm.TruncatedNormal(f'{self.name}_beta_C2',mu=30,sigma=10,initval=30,lower=1),
             'beta_C3': pm.TruncatedNormal(f'{self.name}_beta_C3',mu=0.05,sigma=0.1,initval=0.05,lower=0),
@@ -99,7 +101,8 @@ class WheelS3(BaseComponents):
         }
         return param
 
-class WheelS3V2(BaseComponents):
+
+class WheelS3V3(BaseComponents):
     def __init__(self, name):
         super().__init__(name)
     
@@ -115,40 +118,38 @@ class WheelS3V2(BaseComponents):
         FUNC = cls.get_func_by_engine(engine)
         EXP  = FUNC['EXP']
         
-        K0 = param['K0']
-        eta = param['eta']
         beta_P1 = param['beta_P1']
         beta_P2 = param['beta_P2']
         beta_P3 = param['beta_P3']
-        beta_P1a = param['beta_P1a']
-        beta_P2a = param['beta_P2a']
-        beta_P3a = param['beta_P3a']
         beta_P4 = param['beta_P4']
+        beta_P5 = param['beta_P5']
+        beta_P6 = param['beta_P6']
+        beta_P7 = param['beta_P7']
         beta_C1 = param['beta_C1']
         beta_C2 = param['beta_C2']
         beta_C3 = param['beta_C3']
-        beta_C4 = param['beta_C4']
         
         RinP = get_RH_from_Tdb_and_Hr(TinP,HinP,engine)
-        T_wheel_P = (
-            beta_P1a * TinP * FP ** beta_P1 
-            + beta_P2a * TinC * FC ** beta_P2
-            + beta_P3a * TinR * FR ** beta_P3
-        ) 
-        E = -45
-        R = 8.314
-        N = 0.45
-        H_eq    = K0 * EXP(-E / (R * (T_wheel_P + 273.15))) * RinP ** N # 处理侧平衡状态下转轮能够吸附的水
-        HoutP   = HinP - eta * (H_eq - HinP) * (1 - EXP(-beta_P4 * FP))
+        RinC = get_RH_from_Tdb_and_Hr(TinC,HinC,engine)
+        
+        # 处理侧
+        HdiffP  = (
+            beta_P1 * RinP ** beta_P4  
+            * HinP 
+            * (TinR + beta_P6 * TinC + beta_P7 * TinP) 
+            * EXP(-beta_P5 * FP) 
+            + beta_P2 
+        ) / 1000
+        WdiffP  = HdiffP * FP
+        HoutP   = HinP - HdiffP
         DoutP   = get_Dew_from_HumRatio(HoutP,engine)
-        WdiffP  = (HinP - HoutP) * FP
         Q_lat_P = WdiffP * cls.CONSTANT['h_ads']
-        Q_sen_P = beta_P3 * (TinR - TinP) * FP 
+        Q_sen_P = beta_P3 * (TinR - TinP) * FP
         TdiffP  = (Q_lat_P + Q_sen_P) / (FP * cls.CONSTANT['c_p_air'])
         ToutP   = TinP + TdiffP
         
         # 冷却侧
-        TdiffC    = beta_C1 * EXP(-beta_C2 * EXP(-beta_C3 * (TinR - TinC))) * EXP(-beta_C4 * FC)
+        TdiffC    = beta_C1 * RinC ** beta_C2 * TinR + beta_C3
         ToutC     = TinC + TdiffC
         HdiffC    = 0
         WdiffC    = HdiffC * FC
@@ -172,108 +173,16 @@ class WheelS3V2(BaseComponents):
         
     def prior(self):
         param = {
-            'beta_P1': pm.Normal(f'{self.name}_beta_P1',mu=0,sigma=1,initval=0),
-            'beta_P2': pm.Normal(f'{self.name}_beta_P2',mu=0,sigma=1,initval=0),
-            'beta_P3': pm.Normal(f'{self.name}_beta_P3',mu=0,sigma=1,initval=0),
-            'beta_P1a': pm.Normal(f'{self.name}_beta_P1a',mu=1,sigma=1,initval=1),
-            'beta_P2a': pm.Normal(f'{self.name}_beta_P2a',mu=1,sigma=1,initval=1),
-            'beta_P3a': pm.Normal(f'{self.name}_beta_P3a',mu=1,sigma=1,initval=1),
-            'beta_P4': pm.TruncatedNormal(f'{self.name}_beta_P4',mu=5,sigma=3,initval=5,lower=0),
-            'beta_C1': pm.TruncatedNormal(f'{self.name}_beta_C1',mu=60,sigma=10,initval=60,lower=10),
-            'beta_C2': pm.TruncatedNormal(f'{self.name}_beta_C2',mu=30,sigma=10,initval=30,lower=1),
-            'beta_C3': pm.TruncatedNormal(f'{self.name}_beta_C3',mu=0.05,sigma=0.1,initval=0.05,lower=0),
-            'beta_C4': pm.TruncatedNormal(f'{self.name}_beta_C4',mu=1,sigma=1,initval=1,lower=0),
-            'K0'     : pm.TruncatedNormal(f'{self.name}_k0',mu=0.4,sigma=0.1,initval=0.4,lower=0),
-            'eta'    : pm.HalfNormal(f'{self.name}_eta',sigma=0.1,initval=0.1)
+            'beta_P1': pm.TruncatedNormal(f'{self.name}_beta_P1',mu=5,sigma=10,initval=5,lower=0),
+            'beta_P2': pm.Normal(f'{self.name}_beta_P2',sigma=1,initval=0),
+            'beta_P3': pm.TruncatedNormal(f'{self.name}_beta_P3',mu=1,sigma=2,initval=1.5,lower=0),
+            'beta_P4': pm.HalfNormal(f'{self.name}_beta_P4',sigma=1,initval=0.1),
+            'beta_P5': pm.TruncatedNormal(f'{self.name}_beta_P5',mu=5,sigma=2,initval=5,lower=0),
+            'beta_P6': pm.Normal(f'{self.name}_beta_P6',mu=0,sigma=1,initval=0),
+            'beta_P7': pm.Normal(f'{self.name}_beta_P7',mu=0,sigma=1,initval=0),
+            'beta_C1': pm.TruncatedNormal(f'{self.name}_beta_C1',mu=0.5,sigma=1,initval=0.5,lower=0),
+            'beta_C2': pm.TruncatedNormal(f'{self.name}_beta_C2',mu=0.3,sigma=1,initval=0.3,lower=0),
+            'beta_C3': pm.Normal(f'{self.name}_beta_C3',mu=0,sigma=10,initval=0),
         }
         return param
 
-
-class WheelS3V3(BaseComponents):
-    
-    def __init__(self, name):
-        super().__init__(name)
-    
-    @classmethod
-    def model(
-        cls,
-        TinP,HinP,FP,
-        TinR,HinR,FR,
-        TinC,HinC,FC,
-        engine  : str,
-        param   : dict,
-    ):
-        FUNC = cls.get_func_by_engine(engine)
-        EXP  = FUNC['EXP']
-        
-        beta_P1 = param['beta_P1'] # km
-        beta_P2 = param['beta_P2'] # eta_max
-        beta_P4 = param['beta_P4']
-        beta_P5 = param['beta_P5']
-        beta_C1 = param['beta_C1']
-        beta_C2 = param['beta_C2']
-        beta_C3 = param['beta_C3']
-        beta_C4 = param['beta_C4']
-        beta_R1 = param['beta_R1']
-        beta_R2 = param['beta_R2']
-        
-        # FP_w = EXP(FP)
-        # FR_w = EXP(FR)
-        # FC_w = EXP(FC)
-        T_wheel = (
-            TinP * (FP / (FP + FR + FC))
-            + TinR * (FR / (FP + FR + FC))
-            + TinC * (FC / (FP + FR + FC))
-        )
-        Hr_eq  = get_HumRatio_from_Tdb_and_RH(T_wheel,1,engine)
-        
-        # 处理侧
-        # 湿度
-        eta    = beta_P2 * (1-EXP(-beta_P1 * (HinP-Hr_eq)/Hr_eq))
-        HdiffP = eta * (HinP - Hr_eq)
-        HoutP  = HinP - HdiffP
-        DoutP  = get_Dew_from_HumRatio(HoutP,engine)
-        # 温度
-        TdiffP_sen = beta_P4 * (T_wheel - TinP)
-        TdiffP_lat = cls.CONSTANT['h_ads'] / cls.CONSTANT['c_p_air'] * HdiffP * beta_P5
-        ToutP      = TinP + TdiffP_sen + TdiffP_lat
-        
-        # 预冷侧
-        # 湿度
-        HdiffC = eta * (HinC - Hr_eq)
-        HoutC  = HinC - HdiffC
-        DoutC  = get_Dew_from_HumRatio(HoutC,engine)
-        # 温度
-        TdiffC = beta_C1 * EXP(-beta_C2 * EXP(-beta_C3 * (T_wheel - TinC))) * EXP(-beta_C4 * FC)
-        ToutC  = TinC + TdiffC
-        
-        # 再生侧
-        # 湿度
-        HdiffR = (HdiffP * FP + HdiffC * FC) / FR
-        HoutR  = HinR + HdiffR
-        DoutR  = get_Dew_from_HumRatio(HoutR,engine)
-        # 温度
-        TdiffR_lat = cls.CONSTANT['h_ads'] / cls.CONSTANT['c_p_air'] * HdiffR * beta_R1
-        TdiffR_sen = beta_R2 * (TinR - T_wheel)
-        ToutR      = TinR - TdiffR_sen - TdiffR_lat
-        
-        return {
-            'ToutP':ToutP,'HoutP':HoutP,'DoutP':DoutP,'FP':FP,
-            'ToutR':ToutR,'HoutR':HoutR,'DoutR':DoutR,'FR':FR,
-            'ToutC':ToutC,'HoutC':HoutC,'DoutC':DoutC,'FC':FC,
-        } 
-        
-    def prior(self):
-        param = {
-            'beta_P1': pm.HalfNormal(f'{self.name}_beta_P1',sigma=10,initval=0.01),
-            'beta_P2': pm.Uniform(f'{self.name}_beta_P2',lower=0,upper=1,initval=0.5),
-            'beta_P4': pm.HalfNormal(f'{self.name}_beta_P4', sigma=0.5,initval=0.2),
-            'beta_P5': pm.TruncatedNormal(f'{self.name}_beta_P5', mu=1, sigma=1,initval=1,lower=0),
-            'beta_C1': pm.TruncatedNormal(f'{self.name}_beta_C1',mu=60,sigma=10,initval=60,lower=10),
-            'beta_C2': pm.TruncatedNormal(f'{self.name}_beta_C2',mu=30,sigma=10,initval=30,lower=1),
-            'beta_C3': pm.TruncatedNormal(f'{self.name}_beta_C3',mu=0.05,sigma=0.1,initval=0.05,lower=0),
-            'beta_C4': pm.TruncatedNormal(f'{self.name}_beta_C4',mu=1,sigma=1,initval=1,lower=0),
-            'beta_R1': pm.TruncatedNormal(f'{self.name}_beta_R1', mu=1, sigma=1,initval=1,lower=0),
-            'beta_R2': pm.HalfNormal(f'{self.name}_beta_R2', sigma=10,initval=1),
-        }
-        return param

+ 5 - 4
main.py

@@ -1,10 +1,11 @@
-from .apps.train.train import train
+from .apps.DHU.train import train as train_DHU
 
 def main(*args,config=None):
-    mode   = config['mode']
+    mode = config['mode']
+    equp_type = config['equp_type']
     
-    if mode == 'train':
-        train(*args,config=config)
+    if mode == 'train' and equp_type == 'DHU':
+        train_DHU(*args,config=config)
     else:
         raise NotImplementedError
     

+ 67 - 73
model/DHU/DHU_AB.py

@@ -6,13 +6,11 @@ import pymc as pm
 import pytensor.tensor as pt
 
 from .._base._base_device import BaseDevice
-from ...components.coil_water import CoolingCoil2
-from ...components.coil_steam import SteamCoilFs,SteamCoilFs2,SteamCoil
-from ...components.wheel2 import WheelS2,WheelS2V2,WheelS2V3
-from ...components.wheel3 import WheelS3,WheelS3V2,WheelS3V3
-from ...components.mixed import Mixed
+from ...components import (
+    coil_water,coil_steam,wheel2,wheel3,mixed
+)
 from ..utils.fit_utils import (
-    observe,record,reorder_posterior
+    observe,reorder_posterior
 )
 from ...tools.optimizer import optimizer
 from ...tools.data_cleaner import DataCleaner
@@ -29,59 +27,57 @@ class DHU_AB(BaseDevice):
         exist_Fa_H    = True,
         exist_Fa_B    = True,
         wheel_1       = None,
-        wheel_2       = 'WheelS3',
+        wheel_2       = None,
         coolingcoil_2 = 'CoolingCoil2',
         coolingcoil_3 = 'CoolingCoil2',
         heatingcoil_1 = 'SteamCoil',
         heatingcoil_2 = 'SteamCoil',
         mixed_1       = 'Mixed',
         mixed_2       = 'Mixed',
-        **kwargs
     ) -> None:
         super().__init__()
         self.DHU_type = DHU_type.replace('DHU_','')
         if self.DHU_type == 'A':
             wheel_1 = wheel_1 if wheel_1 is not None else 'WheelS3'
+            wheel_2 = wheel_2 if wheel_2 is not None else 'WheelS3'
         elif self.DHU_type == 'B':
-            wheel_1 = wheel_1 if wheel_1 is not None else 'WheelS2'
+            wheel_1 = wheel_1 if wheel_1 is not None else 'WheelS2V2'
+            wheel_2 = wheel_2 if wheel_2 is not None else 'WheelS3V3'
         else:
             raise Exception('DHU_type must be A or B')
         
-        if 'components' in kwargs:
-            self.components = kwargs['components']
-        else:
-            self.components = {
-                'wheel_1'      : wheel_1,
-                'wheel_2'      : wheel_2,
-                'coil_2'       : coolingcoil_2,
-                'coil_3'       : coolingcoil_3,
-                'heatingcoil_1': heatingcoil_1,
-                'heatingcoil_2': heatingcoil_2,
-                'mixed_1'      : mixed_1,
-                'mixed_2'      : mixed_2
-            }
+        self.components_str = {
+            'wheel_1'      : wheel_1,
+            'wheel_2'      : wheel_2,
+            'coil_2'       : coolingcoil_2,
+            'coil_3'       : coolingcoil_3,
+            'heatingcoil_1': heatingcoil_1,
+            'heatingcoil_2': heatingcoil_2,
+            'mixed_1'      : mixed_1,
+            'mixed_2'      : mixed_2
+        }
         self.exist_Fa_H = exist_Fa_H
         self.exist_Fa_B = exist_Fa_B
         self.record_load_info(
-            components = self.components,
-            DHU_type   = self.DHU_type,
-            exist_Fa_H = self.exist_Fa_H,
-            exist_Fa_B = self.exist_Fa_B
+            components_str = self.components_str,
+            DHU_type       = self.DHU_type,
+            exist_Fa_H     = self.exist_Fa_H,
+            exist_Fa_B     = self.exist_Fa_B
         )
-        self.components = {k:eval(v)(k) for k,v in self.components.items()}
-        
-        for idx in [1,2]:
-            heatingcoil_idx = f'heatingcoil_{idx}'
-            if isinstance(self.components[heatingcoil_idx],SteamCoilFs2):
-                self.model_observe_data_columns[f'{heatingcoil_idx}_FP'] = f'{heatingcoil_idx}_FP'
-                self.model_observe_data_columns[f'{heatingcoil_idx}_Fs'] = f'{heatingcoil_idx}_Fs'
-            elif isinstance(self.components[heatingcoil_idx],SteamCoilFs):
-                self.model_observe_data_columns[f'{heatingcoil_idx}_Fs'] = f'{heatingcoil_idx}_Fs'
-            elif isinstance(self.components[heatingcoil_idx],SteamCoil):
-                pass
-            else:
-                raise Exception('WRONG')
-        
+    
+    @property
+    def components(self):
+        comp_map = {
+            'WheelS2':wheel2,'WheelS3':wheel3,'CoolingCoil':coil_water,
+            'SteamCoil':coil_steam,'Mixed':mixed
+        }
+        output ={}
+        for comp_name,comp_model in self.components_str.items():
+            for comp_map_k,comp_map_v in comp_map.items():
+                if comp_model.startswith(comp_map_k):
+                    output[comp_name] = getattr(comp_map_v,comp_model)(comp_name)
+        return output
+    
     @property
     def model_input_data_columns(self):
         columns = {
@@ -116,10 +112,21 @@ class DHU_AB(BaseDevice):
             'wheel_2_ToutP'  : 'wheel_2_ToutP',
             'wheel_2_DoutP'  : 'wheel_2_DoutP',
             'wheel_2_ToutR'  : 'wheel_2_ToutR',
-            'wheel_2_ToutC'  : 'wheel_2_ToutC', 
         }
         if self.DHU_type == 'A':
             columns['wheel_1_ToutC'] = 'wheel_1_ToutC'
+        
+        for idx in [1,2]:
+            heatingcoil_idx = f'heatingcoil_{idx}'
+            if isinstance(self.components[heatingcoil_idx],coil_steam.SteamCoilFs2):
+                columns[f'{heatingcoil_idx}_FP'] = f'{heatingcoil_idx}_FP'
+                columns[f'{heatingcoil_idx}_Fs'] = f'{heatingcoil_idx}_Fs'
+            elif isinstance(self.components[heatingcoil_idx],coil_steam.SteamCoilFs):
+                columns[f'{heatingcoil_idx}_Fs'] = f'{heatingcoil_idx}_Fs'
+            elif isinstance(self.components[heatingcoil_idx],coil_steam.SteamCoil):
+                columns['wheel_2_ToutC'] = 'wheel_2_ToutC'
+            else:
+                raise Exception('WRONG')
         return columns
     
     def fit(
@@ -151,32 +158,27 @@ class DHU_AB(BaseDevice):
                 if name not in observed_data.columns:
                     continue
                 observed_data = observed_data.rename(columns={name:std_name})
-            observe('mixed_1_ToutA',res['mixed_1']['ToutA'],observed=observed_data)
-            observe('mixed_1_DoutA',res['mixed_1']['DoutA'],observed=observed_data)
-            observe('coil_2_ToutA',res['coil_2']['ToutA'],observed=observed_data)
-            observe('coil_2_DoutA',res['coil_2']['DoutA'],observed=observed_data)
-            observe('wheel_2_ToutP',res['wheel_2']['ToutP'],observed=observed_data)
-            observe('wheel_2_ToutC',res['wheel_2']['ToutC'],observed=observed_data)
-            observe('wheel_2_DoutP',res['wheel_2']['DoutP'],observed=observed_data,sigma=0.3)
-            observe('wheel_2_ToutR',res['wheel_2']['ToutR'],observed=observed_data)
-            
-            if self.DHU_type == 'A':
-                observe('wheel_1_ToutC',res['wheel_1']['ToutC'],observed=observed_data)
-            
-            for idx in [1,2]:
-                heatingcoil_idx = f'heatingcoil_{idx}'
-                if isinstance(self.components[heatingcoil_idx],SteamCoilFs2):
-                    observe(f'{heatingcoil_idx}_FP',res[heatingcoil_idx]['FP'],observed=observed_data,sigma=10000)
-                    observe(f'{heatingcoil_idx}_Fs',res[heatingcoil_idx]['Fs'],observed=observed_data,sigma=20)
-                elif isinstance(self.components[heatingcoil_idx],SteamCoilFs):
-                    observe(f'{heatingcoil_idx}_Fs',res[heatingcoil_idx]['Fs'],observed=observed_data,sigma=20)
-                elif isinstance(self.components[heatingcoil_idx],SteamCoil):
-                    record(f'{heatingcoil_idx}_Fs',res[heatingcoil_idx]['Fs'])
-                else:
-                    raise Exception('WRONG')
+
+                std_name_equp,std_name_point = std_name.rsplit('_',1)
+                sigma = {
+                    'wheel_2_DoutP'   : 0.3,
+                    'heatingcoil_1_Fs': 20,
+                    'heatingcoil_2_Fs': 20,
+                    'heatingcoil_1_FP': 10000,
+                    'heatingcoil_2_FP': 10000,
+                }
+                observe(
+                    name     = std_name,
+                    var      = res[std_name_equp][std_name_point],
+                    observed = observed_data,
+                    sigma    = sigma.get(std_name,1)
+                )
             
             self.param_posterior = pm.find_MAP(maxeval=50000,include_transformed=False)
-            
+        
+        self.record_load_info(
+            param_posterior = self.param_posterior
+        )
         self.record_model(
             model_name   = 'ATD',
             model        = reorder_posterior(param_prior,self.param_posterior),
@@ -215,16 +217,13 @@ class DHU_AB(BaseDevice):
                 .rm_rule('State != 1')
                 .rm_outrange(method='raw',upper=140,lower=20,include_cols=['wheel_1_TinR','wheel_2_TinR'])
             )
-            
             if self.DHU_type == 'A':
                 clean_data = (
                     clean_data
                     .rm_rule('wheel_1_ToutC<=coil_1_ToutA')
                 )
-        
         if 'observed' in data_type:
             pass
-        
         clean_data = clean_data.get_data(
             fill     = 0 if fill_zero else None,
             save_log = save_log
@@ -620,13 +619,8 @@ class AirFlow_DHU_AB:
         F_air_X_base_adj = F_air_X_base + F_air_val_rw
         F_air_H_base_adj = F_air_H_base - F_air_val_rw * F_air_val_pct if 'H_base' in param['F_air'] else 0
         F_air_B_base_adj = F_air_B_base - F_air_val_rw * (1 - F_air_val_pct) if 'B_base' in param['F_air'] else 0
-        # F_air_X_base_adj = F_air_X_base + F_air_val_rw
-        # F_air_B_base_adj = F_air_B_base - F_air_val_rw * F_air_val_pct[0] if 'B_base' in param['F_air'] else 0
-        # F_air_S_base_adj = F_air_S_base + F_air_val_rw * F_air_val_pct[1] if 'H_base' in param['F_air'] else 0
-        # F_air_H_base_adj = F_air_H_base - F_air_val_rw * F_air_val_pct[2] if 'H_base' in param['F_air'] else 0
         
         # 考虑风机频率变化对风量的影响,得到最终风量
-        # 因为从数据上看,排风机的频率不会影响到新风量,所以在新风阀不动的情况下,新风的增加量可以认为全部进入送风
         F_air_HzP_X = param['F_air']['HzP_X']
         F_air_HzP_H = param['F_air'].get('HzP_H',0)
         F_air_HzP_S = F_air_HzP_X + F_air_HzP_H

+ 12 - 0
model/Room/room.py

@@ -0,0 +1,12 @@
+from .._base._base import BaseModel
+
+class Room(BaseModel):
+    def __init__(self):
+        super().__init__()
+    
+    def fit(self):
+        ...
+    
+    def predict(self):
+        ...
+    

+ 14 - 4
model/_base/_base.py

@@ -1,5 +1,6 @@
 import importlib
 import pickle
+import inspect
 from typing import Union
 from datetime import datetime
 
@@ -25,8 +26,6 @@ class BaseModel:
         self.model_info = {'LOAD_INFO':{}}
     
     def record_load_info(self,**info):
-        for attr_name,attr_value in info.items():
-            setattr(self,attr_name,attr_value)
         self.model_info['LOAD_INFO'].update(info)
     
     def record_model(
@@ -91,9 +90,20 @@ class BaseModel:
     @classmethod
     def load(cls,path):
         model_info = pd.read_pickle(path)
-        load_info  = model_info.get('LOAD_INFO',{})
-        model      = cls(**load_info)
+        
+        load_info_init = {}
+        load_info_attr = {}
+        cls_init_param = list(inspect.signature(cls.__init__).parameters.keys())
+        for load_key,load_value in model_info.get('LOAD_INFO',{}).items():
+            if load_key in cls_init_param:
+                load_info_init[load_key] = load_value
+            else:
+                load_info_attr[load_key] = load_value
+        
+        model = cls(**load_info_init)
         model.model_info = model_info
+        for attr_name,attr_value in load_info_attr.items():
+            setattr(model,attr_name,attr_value)
         return model
     
     def save_to_platform(

+ 19 - 5
tools/data_loader.py

@@ -12,6 +12,7 @@ import psychrolib
 psychrolib.SetUnitSystem(psychrolib.SI)
 get_Dew = np.vectorize(psychrolib.GetTDewPointFromRelHum)
 get_Hr  = np.vectorize(psychrolib.GetHumRatioFromTDewPoint)
+get_RH  = np.vectorize(psychrolib.GetRelHumFromTDewPoint)
 
 from .._data.main import get_data
 
@@ -106,19 +107,29 @@ class DataLoader:
         all_file_path = os.listdir(equp_path)
         for file in all_file_path:
             # 通过干球温度和相对湿度计算露点
-            if '_T' in file and file.replace('_T','_R') in all_file_path:
+            exist_T = '_T' in file
+            exist_R = file.replace('_T','_R') in all_file_path
+            exist_D = file.replace('_T','_D') in all_file_path
+            if exist_T and exist_R and not exist_D:
                 Tdb = pd.read_pickle(os.path.join(equp_path,file)).iloc[:,0].values
                 RH  = pd.read_pickle(os.path.join(equp_path,file.replace('_T','_R'))).iloc[:,0].values
                 Dew = pd.DataFrame({file.replace('_T','_D'):get_Dew(Tdb,np.clip(RH,0,100)/100)},index=self.date_range)
                 pd.to_pickle(Dew,os.path.join(equp_path,file.replace('_T','_D')))
+            if exist_T and exist_D and not exist_R:
+                Tdb = pd.read_pickle(os.path.join(equp_path,file)).iloc[:,0].values
+                Dew = pd.read_pickle(os.path.join(equp_path,file.replace('_T','_D'))).iloc[:,0].values
+                RH  = pd.DataFrame({file.replace('_T','_R'):get_RH(Tdb,Dew)},index=self.date_range)
+                pd.to_pickle(RH,os.path.join(equp_path,file.replace('_T','_R')))
+                
         all_file_path = os.listdir(equp_path)
         for file in all_file_path:
             # 通过露点计算绝对湿度
-            if '_D' in file:
+            exist_D = '_D' in file
+            exist_H = file.replace('_D','_H') in all_file_path
+            if exist_D and not exist_H:
                 Dew = pd.read_pickle(os.path.join(equp_path,file)).iloc[:,0].values
                 Hr  = pd.DataFrame({file.replace('_D','_H'):get_Hr(Dew,101325)},index=self.date_range)
                 pd.to_pickle(Hr,os.path.join(equp_path,file.replace('_D','_H')))
-    
         return self
                 
     
@@ -129,9 +140,12 @@ class DataLoader:
         for file in all_file_path:
             if '.pkl' not in file:
                 continue
-            data_path = os.path.join(equp_path,file)
+            data = pd.read_pickle(os.path.join(equp_path,file))
+            if data.shape[1] != 1:
+                print(data)
+                raise Exception(f'data shape error:{equp_name}')
             data = (
-                pd.read_pickle(data_path)
+                data
                 .set_axis([file.replace('.pkl','')],axis=1)
                 .loc[self.start_time:self.end_time,:]
             )