def getHeight(self, bname, AR=0.5, get_unit=False): """Get the height of decanter depends on the diameter of decanter. :param bname: Block name. :param AR: aspect ratio which is the ratio of diameter to height. The default value is 0.5. :param get_unit: "True" to get the unit. "False" is a default value. :return: float. a calculated value. """ ## 先檢查輸入的資料型態正確與否 if type(get_unit) != bool: raise TypeError("unit must be a 'Boolean'!!!") if type(AR) != int and type(AR) != float: raise TypeError("AR must be a Number!!!") ## AR 不能是負數 if AR < 0: raise ValueError("AR should not be a negative number!!") ## 檢查bname是否為Decanter if self.master.BlockType(bname) != 'Decanter': raise UDE.AspenPlus_BlockTypeError( bname + ' is not a Decanter,' + 'please check the name you type!!') d = self.getDiameter(bname) if get_unit: # 取得單位 return "meter" else: # 取得數值 return d / AR
def getHeight(self, bname, get_unit=False): """Get the Column's height. it is a calculated value. Total number of trays(NT) with 2-ft spacing plus 20% extra length. It means H[ft] = 2.4×(NT-1) which is equal to H[m] = 0.73152×(NT-1). Reference: Luyben, W. L. , Comparison of extractive distillation and pressure-swing distillation for acetone/chloroform separation . Comput Chem Eng 2013, 50, 1-7. """ ## 先檢查輸入的資料型態正確與否 if type(get_unit) != bool: raise TypeError("unit must be a 'Boolean'!!!") ## 檢查bname是否為RadFrac if self.master.BlockType(bname) != 'RadFrac': raise UDE.AspenPlus_BlockTypeError( bname + ' is not a RadFrac,' + 'please check the name you type!!') ## 取得指定蒸餾塔的總板數 n = self.getNStage(bname) ## 是要取得數值還是單位 if get_unit: # 取得單位 return "meter" else: # 取得數值 return 0.73152 * (n - 1)
def Connections(self, bname, table=False): """Show the connected stream of the desired block. :param bname: block name in 'Str-Type'. :param table: a boolen value. default is False for print the result on the screen. If table=True, return result in list type. """ ## 先檢查輸入的資料型態是否正確 if type(bname) is not str: raise TypeError("bname must be a 'String'!!!") if type(table) is not bool: raise TypeError("table must be a 'Boolen' value!!!") ## 將輸入的字串全部大寫,因為Aspen的單元名稱都是大寫 bname = bname.upper() ## 檢查輸入的單元名稱是否在檔案當中,如無回傳錯誤 if bname not in self.BlocksList(): raise UDE.AspenPlus_BlockTypeError("Cannot Find " + bname + " in the AspenFile. " + "Please Check the name you type!!") ## 在屏幕上輸出指定單元所連接的物流名稱 if not table: print("{0[0]:13s}{0[1]:13s}".format(["Stream_Name","Streams_Type"])) print("==========================") for e in self.aspen.Tree.Elements("Data").Elements("Blocks").Elements(bname).Elements("Connections").Elements: streamtype = self.aspen.Tree.Elements("Data").Elements("Blocks").Elements(bname).Elements("Connections").Elements(e.Name).Value print("{0:13s}{1:13s}".format(e.Name,streamtype)) ## 將結果以列表輸出 elif table: a_list = [] for e in self.aspen.Tree.Elements("Data").Elements("Blocks").Elements(bname).Elements("Connections").Elements: a_list.append(e.Name) return a_list
def getVolume(self, bname, rtime=40, LF=1 / 2, get_unit=False): """Get the volume of decanter depends on the volume flowrate of inlet stream. The calculation condition is based on residual time (rtime) of decanter and liquid level fraction (LF) in the tank. :param bname: Block name. :param rtime: Residual time of decanter in minute. The default value is 40 min. :param LF: Liquid level fraction in the decanter. The default value is 1/2. :param get_unit: "True" to get the unit. "False" is a default value. :return: float. a calculated value. """ ## 先檢查輸入的資料型態正確與否 if type(get_unit) != bool: raise TypeError("unit must be a 'Boolean'!!!") if type(rtime) != int and type(rtime) != float: raise TypeError("rtime must be a Number!!!") if type(LF) != int and type(LF) != float: raise TypeError("LF must be a Number!!!") ## rtime 不能是負數 if rtime < 0: raise ValueError("rtime should not be a negative number!!") ## LF 不能是負數 if LF < 0: raise ValueError("LF should not be a negative number!!") ## 檢查bname是否為Decanter if self.master.BlockType(bname) != 'Decanter': raise UDE.AspenPlus_BlockTypeError( bname + ' is not a Decanter,' + 'please check the name you type!!') ## 找出Decanter進料物流名稱 dec_fstream = [] for e in self.aspen.Tree.Elements("Data").Elements("Blocks").Elements( bname).Elements("Connections").Elements: streamtype = self.aspen.Tree.Elements("Data").Elements( "Blocks").Elements(bname).Elements("Connections").Elements( e.Name).Value if streamtype == "F(IN)": dec_fstream.append(e.Name) ## 檢查Decanter所有進料物流都沒有氣體以避免體積計算誤差 for fstream in dec_fstream: if self.master.Stream.getVaporFrac(fstream) != 0: raise UDE.AspenPlus_BlockError( f"The feed stream of decanter '{fstream}' " + "contains gas. The volume-calculation may " + " have a great deviation !!!") if get_unit: # 取得單位 return "cum" else: # 取得數值 v = 0 for fstream in dec_fstream: v += self.master.Stream.getVolumeFlow(fstream, unit_change_to=20) return v * rtime / LF
def getCondenserArea(self, bname, get_unit=False, Uc=0.852): """Get the condenser area of specified column. If it is not condenser, the return value is 'None'. bname: Block name. get_unit: "True" to get the unit. "False" is a default value. Ur: Heat transfer coefficient of reboiler [kW/K-sqrm]. The default value is 0.852. """ # TODO: 不知道需不需要考慮LMTD,需要的話在增加判斷式 ## 先檢查輸入的資料型態正確與否 if type(get_unit) != bool: raise TypeError("unit must be a 'Boolean'!!!") if type(Uc) != int and type(Uc) != float: raise TypeError("unit must be a 'Number'!!!") ## Ur不應該小於等於0,那...那是什麼設備?? if Uc <= 0: raise ValueError("Uc shouldn't be equal to or smaller than 0. " + "Please Check the Value you Type!!!") ## 檢查bname是否為RadFrac if self.master.BlockType(bname) != 'RadFrac': raise UDE.AspenPlus_BlockTypeError( bname + ' is not a RadFrac,' + 'please check the name you type!!') ## 檢查指定蒸餾塔有沒有condenser path = "\Data\Blocks" + "\\" + bname + "\Input\CONDENSER" if self.aspen.Tree.FindNode(path).Value == "NONE": return bottom_name = None ## 找出冷凝器的出口物流名稱後取得其溫度 for e in self.aspen.Tree.Elements("Data").Elements("Blocks").Elements( bname).Elements("Connections").Elements: streamtype = self.aspen.Tree.Elements("Data").Elements( "Blocks").Elements(bname).Elements("Connections").Elements( e.Name).Value if streamtype == "LD(OUT)": bottom_name = e.Name Temp = self.master.Stream.getTemperature(bottom_name, unit_change_to=1) utility = self.master.Cost.WaterType(bname) DeltaT = Temp - self.master.Cost.Water[utility]['Temperature']['Value'] Qr = -self.getQC(bname) ## 溫差不該小於等於0,那應該就不是condenser了 if DeltaT <= 0: raise ValueError( "DeltaT shouldn't be equal to or smaller than 0. " + "Please connect to the programmer!!!") ## 是要取得數值還是單位 if get_unit: # 取得單位 return "sqrm" else: # 取得數值 return Qr / Uc / DeltaT
def getNStage(self, bname): """Get the number of stage for the specified RadFrac block. bname: Block name. """ ## 檢查bname是否為RadFrac if self.master.BlockType(bname) != 'RadFrac': raise UDE.AspenPlus_BlockTypeError( bname + ' is not a RadFrac,' + 'please check the name you type!!') path = "\Data\Blocks" + "\\" + bname + r"\Input\NSTAGE" return self.aspen.Tree.FindNode(path).Value
def getRefluxRatio(self, bname) -> float: """Get the reflux ratio of specified column. :param bname: Block name. :return: float. """ ## 檢查bname是否為RadFrac if self.master.BlockType(bname) != 'RadFrac': raise UDE.AspenPlus_BlockTypeError( bname + ' is not a RadFrac,' + 'please check the name you type!!') path = "\Data\Blocks" + "\\" + bname + r"\Output\MOLE_RR" return self.aspen.Tree.FindNode(path).Value
def getDiameter(self, bname, get_unit=False, unit_change_to=None): """Get the Column's diameter. Before using this function. you have to turn on the 'Sizaing' or 'Rating' function in AspenPlus. bname: Block name. get_unit: "True" to get the unit. "False" is a default value. unit_change_to: unit index in AspenFile for the changing unit. If you don't want to change the unit, unit_change_to should be 'None'(Default value is this). """ ## 此功能只能取sizing的結果不是取rating的結果 ## 先檢查輸入的資料型態正確與否 if type(get_unit) != bool: raise TypeError("unit must be a 'Boolean'!!!") if unit_change_to is not None and type(unit_change_to) != int: raise TypeError("unit_change_to must be a 'Integer' or 'None'!!!") ## 檢查bname是否為RadFrac if self.master.BlockType(bname) != 'RadFrac': raise UDE.AspenPlus_BlockTypeError( bname + ' is not a RadFrac,' + 'please check the name you type!!') ## 檢查是否有開啟Sizing功能 if self.aspen.Tree.FindNode("\Data\Blocks" + "\\" + bname + "\Subobjects\Column Internals" ).AttributeValue(38) == 0: # 用是否有子代判斷 raise UDE.AspenPlus_SizingError( "'Sizing' function dosen't open!!!") ## 確認是否為 Apwn36.0(V10) if self.aspen.Name.find('36.0') != -1: # 第十版的直徑路徑 path = "\Data\Blocks" + "\\" + bname + "\Subobjects\Column Internals\INT-1\Subobjects\Sections\CS-1\Input\CA_DIAM\INT-1\CS-1" else: # V8.8以前的的直徑路徑 path = "\Data\Blocks" + "\\" + bname + "\Subobjects\Tray Sizing\\1\Output\DIAM4\\1" ## 看看有沒有要改單位 if unit_change_to is None: # 沒有要改單位 ## 是要取得數值還是單位 if get_unit: # 取得單位 return self.master.UnitFind(self.aspen.Tree.FindNode(path), table=True) else: # 取得數值 return self.aspen.Tree.FindNode(path).Value elif unit_change_to is not None: # 要改單位的話 if get_unit: # 取得單位 pq = self.aspen.Tree.FindNode(path).AttributeValue(2) return self.master.Unit(item=[pq, unit_change_to], table=True) else: # 取得數值 return self.master.UnitChange(self.aspen.Tree.FindNode(path), unit_change_to)
def getNF(self, bname, *, sname=None): """Get the number of feed stage for the specified RadFrac block and feed stream or Get the all feed stream for the specified RadFrac block in a list-type. :param bname: Block name. :param sname: Feed stream name. :return: if sname is None, return a feed stream list for specified block. if sname is specified, return the NF of specified block and feed stream. """ ## 檢查bname是否為RadFrac if self.master.BlockType(bname) != 'RadFrac': raise UDE.AspenPlus_BlockTypeError( bname + ' is not a RadFrac,' + 'please check the name you type!!') ## 對sname進行檢查並處理 if sname is None: pass elif type(sname) is str: ## 把sname轉換成大寫,因為Aspen裡面的名稱都大寫 sname = sname.upper() ## 檢查輸入的物流名稱是否在檔案當中,如無回傳錯誤 if sname not in self.master.StreamsList(): raise UDE.AspenPlus_StreamTypeError( "Cannot Find " + sname + " in the AspenFile. " + "Please Check the name you type!!") else: raise TypeError("sname must be a 'String'!!!") ## 指定塔的進料物流 物件 obj = self.aspen.Tree.Elements("Data").Elements("Blocks").Elements( bname).Elements("Input").Elements("FEED_STAGE") ## 列出指定塔的進料物流有哪些 nlist = [] for e in obj.Elements: nlist.append(e.Name) if sname is None: # 沒輸入sname就回傳進料物流列表 return nlist elif sname is not None: # 有輸入sname且為特定塔的進料物流的話,輸出進料板 if sname not in nlist: raise UDE.AspenPlus_StreamTypeError( sname + " is not a feed stream" + " for " + bname + ". " + "Please Check the name you type!!") return obj.Elements(sname).Value
def wrapper(self, *args, **kwargs): # Inner function for this decorator def name_up_and_arg_change(name, args): """將name變大寫並且回傳回args""" name = name.upper() args = list(args) # 要把args第一個參數(name)變成大寫 args[0] = name # 但是args是tuple不能修改,所以要先 args = tuple(args) # 變成list,修改過後再轉回tuple return name, args if Type == 'stream': sname = args[0] ## 先檢查輸入的資料型態正確與否 if type(sname) != str: raise TypeError("sname must be a 'String'!!!") # 把sname變大寫並回傳回args sname, args = name_up_and_arg_change(sname, args) ## 檢查輸入的物流名稱是否在檔案當中,如無回傳錯誤 if sname not in self.master.StreamsList(): raise UDE.AspenPlus_StreamTypeError("Cannot Find " + sname + " in the AspenFile. " + "Please Check the name you type!!") elif Type == 'block': bname = args[0] ## 先檢查輸入的資料型態正確與否 if type(bname) != str: raise TypeError("bname must be a 'String'!!!") # 把bname變大寫並回傳回args bname, args = name_up_and_arg_change(bname, args) ## 檢查輸入的物流名稱是否在檔案當中,如無回傳錯誤 if bname not in self.master.BlocksList(): raise UDE.AspenPlus_BlockTypeError("Cannot Find " + bname + " in the AspenFile. " + "Please Check the name you type!!") else: raise TypeError('No Match Value for Type, ' + 'Please Check the value you type !!!') return func(self, *args, **kwargs)
def getQC(self, bname, get_unit=False, unit_change_to=None): """Get the condenser duty of specified column. If it is not condenser, the return value is 'None'. bname: Block name. get_unit: "True" to get the unit. "False" is a default value. unit_change_to: unit index in AspenFile for the changing unit. If you don't want to change the unit, unit_change_to should be 'None'(Default value is this). """ ## 先檢查輸入的資料型態正確與否 if type(get_unit) != bool: raise TypeError("unit must be a 'Boolean'!!!") if unit_change_to is not None and type(unit_change_to) != int: raise TypeError("unit_change_to must be a 'Integer' or 'None'!!!") ## 檢查bname是否為RadFrac if self.master.BlockType(bname) != 'RadFrac': raise UDE.AspenPlus_BlockTypeError( bname + ' is not a RadFrac,' + 'please check the name you type!!') ## 檢查指定蒸餾塔有沒有condenser path = "\Data\Blocks" + "\\" + bname + "\Input\CONDENSER" if self.aspen.Tree.FindNode(path).Value == "NONE": return path = "\Data\Blocks" + "\\" + bname + "\Output\COND_DUTY" ## 看看有沒有要改單位 if unit_change_to is None: # 沒有要改單位 ## 是要取得數值還是單位 if get_unit: # 取得單位 return self.master.UnitFind(self.aspen.Tree.FindNode(path), table=True) else: # 取得數值 return self.aspen.Tree.FindNode(path).Value elif unit_change_to is not None: # 要改單位的話 if get_unit: # 取得單位 pq = self.aspen.Tree.FindNode(path).AttributeValue(2) return self.master.Unit(item=[pq, unit_change_to], table=True) else: # 取得數值 return self.master.UnitChange(self.aspen.Tree.FindNode(path), unit_change_to)
def setNStage(self, bname, set_value): """Get the number of stage for the specified RadFrac block. bname: Block name. """ ## 檢查輸入的資料型態正確與否 if type(set_value) != int: raise TypeError("set_value must be a 'Integer'!!!") ## 檢查set_value 是否為正整數 if set_value < 0: raise ValueError("set_value must be a 'Positive' number!!!") ## 檢查bname是否為RadFrac if self.master.BlockType(bname) != 'RadFrac': raise UDE.AspenPlus_BlockTypeError( bname + ' is not a RadFrac,' + 'please check the name you type!!') path = "\Data\Blocks" + "\\" + bname + r"\Input\NSTAGE" self.aspen.Tree.FindNode(path).Value = set_value return
def BlockType(self, bname) -> str: """Get the block type of the bname in string. :param bname: Block name in AspenFile. :return: Sting. a string in block type for specified block. """ ## 先檢查輸入的資料型態是否正確 if type(bname) is not str: raise TypeError("bname must be a 'String'!!!") ## 將輸入的字串全部大寫,因為Aspen的單元名稱都是大寫 bname = bname.upper() ## 檢查輸入的單元名稱是否在檔案當中,如無回傳錯誤 if bname not in self.BlocksList(): raise UDE.AspenPlus_BlockTypeError("Cannot Find " + bname + " in the AspenFile. " + "Please Check the name you type!!") return self.aspen.Tree.Elements("Data").Elements("Blocks").Elements(bname).AttributeValue(6)
def getDiameter(self, bname, get_unit=False): """Get the diameter of decanter depends on the volume of decanter. :param bname: Block name. :param get_unit: "True" to get the unit. "False" is a default value. :return: float. a calculated value. """ ## 先檢查輸入的資料型態正確與否 if type(get_unit) != bool: raise TypeError("unit must be a 'Boolean'!!!") ## 檢查bname是否為Decanter if self.master.BlockType(bname) != 'Decanter': raise UDE.AspenPlus_BlockTypeError( bname + ' is not a Decanter,' + 'please check the name you type!!') v = self.getVolume(bname) if get_unit: # 取得單位 return "meter" else: # 取得數值 return (2 * v / 3.1415926)**(1 / 3)
def getQ(self, bname, get_unit=False, unit_change_to=None): """Get the duty of Heat exchanger. bname: block name. get_unit: "True" to get the unit. "False" is a default value. unit_change_to: unit index in AspenFile for the changing unit. If you don't want to change the unit, unit_change_to should be 'None'(Default value is this). """ ## 先檢查輸入的資料型態正確與否 if type(get_unit) != bool: raise TypeError("unit must be a 'Boolean'!!!") if unit_change_to is not None and type(unit_change_to) != int: raise TypeError("unit_change_to must be a 'Integer' or 'None'!!!") ## 檢查bname是否為Heater if self.master.BlockType(bname) != 'Heater': raise UDE.AspenPlus_BlockTypeError( bname + ' is not a Heater,' + 'please check the name you type!!') path = "\Data\Blocks" + "\\" + bname + "\Output\QNET" ## 看看有沒有要改單位 if unit_change_to is None: # 沒有要改單位 ## 是要取得數值還是單位 if get_unit: # 取得單位 return self.master.UnitFind(self.aspen.Tree.FindNode(path), table=True) else: # 取得數值 return self.aspen.Tree.FindNode(path).Value elif unit_change_to is not None: # 要改單位的話 if get_unit: # 取得單位 pq = self.aspen.Tree.FindNode(path).AttributeValue(2) return self.master.Unit(item=[pq, unit_change_to], table=True) else: # 取得數值 return self.master.UnitChange(self.aspen.Tree.FindNode(path), unit_change_to)
def getArea(self, bname, exchanger_type, get_unit=False, U=None): """Get the theoretical area of 'Heater'. The available exchanger types are 'Condenser', 'Heater', 'Reboiler', and 'Cooler'. :param bname: block name. :param exchanger_type: the type for calculating cost of heater. the available value are 'CONDENSER', 'HEATER', 'REBOILER', ane 'COOLER'. :param get_unit: "True" to get the unit. "False" is a default value. :param U: Heat transfer coefficient of exchanger [kW/K-sqrm]. The value will be change to match the different condition. You can specified the value you want. :return: """ # 把heater當mixer用還沒有解決辦法 from math import log ## 先檢查輸入的資料型態正確與否 if type(get_unit) != bool: raise TypeError("unit must be a 'Boolean'!!!") ## 檢查bname是否為Heater if self.master.BlockType(bname) != 'Heater': raise UDE.AspenPlus_BlockTypeError( bname + ' is not a Heater,' + 'please check the name you type!!') ## 檢查Uc的輸入型態是否正確,如為數字檢查數字範圍是否正確 if (U is not None) and type(U) != int and type(U) != float: raise TypeError("U must be a 'Number'!!!") elif U is not None: ## Ur不應該小於等於0,那...那是什麼設備?? if U <= 0: raise ValueError( "U shouldn't be equal to or smaller than 0. " + "Please Check the Value you Type!!! ") duty = abs(self.getQ(bname, unit_change_to=14)) # kW ## 檢查 exchanger_type是否為三個選項之一,並執行個別的算法,都不是則回報錯誤 if exchanger_type == 'CONDENSER': ## Uc 沒有定義設定值,就用condenser的預設值 if U is None: U = 0.852 ## 找出Heater出口的物流名稱,並取得其凱式溫度與適合公共流體 outlet_name = None for e in self.aspen.Tree.Elements("Data").Elements( "Blocks").Elements(bname).Elements("Connections").Elements: streamtype = self.aspen.Tree.Elements("Data").Elements( "Blocks").Elements(bname).Elements("Connections").Elements( e.Name).Value if streamtype == "P(OUT)" and self.master.BlockType( bname) == 'Heater': outlet_name = e.Name if outlet_name is None: raise UDE.AspenPlus_BlockTypeError( "Please Connect the Developer!!!") To = self.master.Stream.getTemperature(outlet_name, unit_change_to=1) utility = self.master.Cost.WaterType(bname) ## 取得 deltaT deltaT = To - self.master.Cost.Water[utility]['Temperature'][ 'Value'] elif (exchanger_type == 'HEATER') or (exchanger_type == 'REBOILER'): ## Uc 沒有定義設定值,就用condenser的預設值 if U is None: U = 0.568 ## 找出Heater出口的物流名稱,並取得其凱式溫度與適合公共流體 outlet_name = None for e in self.aspen.Tree.Elements("Data").Elements( "Blocks").Elements(bname).Elements("Connections").Elements: streamtype = self.aspen.Tree.Elements("Data").Elements( "Blocks").Elements(bname).Elements("Connections").Elements( e.Name).Value if streamtype == "P(OUT)" and self.master.BlockType( bname) == 'Heater': outlet_name = e.Name if outlet_name is None: raise UDE.AspenPlus_BlockTypeError( "Please Connect the Developer!!!") To = self.master.Stream.getTemperature(outlet_name, unit_change_to=1) utility = self.master.Cost.SteamType(bname) ## 取得 deltaT deltaT = self.master.Cost.Steam[utility]['Temperature'][ 'Value'] - To elif exchanger_type == 'COOLER': if U is None: U = 0.852 ## 找出Heater出口的物流名稱,並取得其凱式溫度與適合公共流體 outlet_name = None inlet_name = None for e in self.aspen.Tree.Elements("Data").Elements( "Blocks").Elements(bname).Elements("Connections").Elements: streamtype = self.aspen.Tree.Elements("Data").Elements( "Blocks").Elements(bname).Elements("Connections").Elements( e.Name).Value if streamtype == "P(OUT)" and self.master.BlockType( bname) == 'Heater': outlet_name = e.Name elif streamtype == "F(IN)" and self.master.BlockType( bname) == 'Heater': inlet_name = e.Name if (outlet_name is None) or (inlet_name is None): raise UDE.AspenPlus_BlockTypeError( "Please Connect the Developer!!!") To = self.master.Stream.getTemperature(outlet_name, unit_change_to=1) Ti = self.master.Stream.getTemperature(inlet_name, unit_change_to=1) utility = self.master.Cost.WaterType(bname) Tu = self.master.Cost.Water[utility]['Temperature']['Value'] ## 取得 deltaT deltaT = (Ti - To) / log((Ti - Tu) / (To - Tu)) else: raise TypeError('exchanger_type must be a String and ' + "one of them that 'CONDENSER', 'HEATER', " + "'REBOILER', and 'COOLER' !!!") A = duty / U / deltaT if get_unit: return 'sqrm' else: return A