excel = Dispatch('excel.Application') # win32com.client로 excel call
openfiledir = os.getcwd() + '\' # 현재 폴더 위치 5
filename = 'MPB_Ins.xlsx' # 현재 폴더 위치의 해당 파일명의 .xlsx 파일 open
Tag = ws.Range("F2").Value # Tag name, Sheet name 으로 사용.
Fluid = ws.Range("F3").Value
P_set = ws.Range("F16").Value # Set Pressure.
P = ws.Range("F17").Value # Relieving Pressure.
P1 = ws.Range("F40").Value # Set P + Allowable overpressure + Atmospheric pressure (Inlet Dp 고려??)
F = ws.Range("F25").Value # F factor 는 input 으로 받아야 함.
A = ws.Range("F26").Value # Pipe 면적
firefighting = ws.Range("F27").Value # API에 해당하는 인자. firefighting 이 있으면 1 없으면 0
Op_Flow = ws.Range("F37").Value # kg/h operating flowrate (VAPOR 경우, 공급유량 만큼 빼주게 끔)
Tw = ws.Range("F36").Value # K 탄소강판 권장 최대 용기 온도 593 도
Pn = ws.Range("F32").Value # MPa operating pressure
Tn = ws.Range("F35").Value # K operating temperature
T1 = P/Pn * Tn
Rule = ws.Range("F20").Value # Code 선택
Case = ws.Range("F21").Value # Case 선택
Phase = ws.Range("F22").Value # Phase 선택
Type = ws.Range("F49").Value # Type 선택
Fl_in = ws.Range("F50").Value # Flange rating 선택
def Ins_PRV(a, b):
D = math.sqrt(a[1]*(2/(a[1]+1))*((a[1]+1)/(a[1]-1)))
D_AIR = math.sqrt(AIR[1](2/(AIR[1]+1))*((AIR[1]+1)/(AIR[1]-1)))
W = b (D/D_AIR) math.sqrt((a[3]/AIR[3]) (AIR[0]+273.15)/(a[0]+273.15) * (a[2] / AIR[2]) ) # kg/h
return W
Ins_nominal = ws.Range("H37").Value
W_ins = Ins_PRV(Rel_Pro, Ins_nominal)
print(W_ins)
ws_report.Range("F37").Value = W_ins
def Code(a,b,c,d): # Property , b : IGF / API / KOSHA / KGS, c : 'LIQUID' / 'VAPOR', d: Case
if d == 'Fire':
# 추후 Code 를 dataframe 또는 np.array 로 표로 만들어서 진행하는 방향??
if b == 'IGF':
if c == 'LIQUID':
D = math.sqrt(a[1]*(2/(a[1]+1))**((a[1]+1)/(a[1]-1)))
D_AIR = math.sqrt(AIR[1]*(2/(AIR[1]+1))**((AIR[1]+1)/(AIR[1]-1)))
G = 12.4 / (a[6]*D) * math.sqrt (a[2]*(a[0]+273.15)/a[3]) # T는 절대온도로 환산.
Q = F * G * A**0.82 * 3600 # Nm3/h
W = Q * AIR[4] * (D/D_AIR) * math.sqrt((a[3]/AIR[3]) * (AIR[0]+273.15)/(a[0]+273.15) * (a[2] / AIR[2]) ) # kg/h
## 22. 02. 25 Convert 시 Z 항 추가
print ("G :" ,G, "Q : ", Q,"W :", W, "Z :", a[2], "Tg", a[0], "Mg", a[3], "D", D)
return (W)
elif c == 'VAPOR':
W = Op_Flow
return (W)
elif b == 'API':
if c == 'LIQUID':
if firefighting == 0:
W = 70900 * F * A**0.82 / a[6] / 1000 * 3600 # kg/h without fire fighting
elif firefighting == 1:
W = 43200 * F * A**0.82 / a[6] / 1000 * 3600 # kg/h with fire fighting
return (W)
elif c == 'VAPOR':
W = 0.2772 * math.sqrt(a[3]*P*1000) * (A * (Tw-T1)**1.25 / T1**1.1506)
return (W)
elif b == 'KOSHA':
if c == 'LIQUID':
if firefighting == 0:
W = 4.1868 * 61000 * F * A**0.82 / a[6] # kg/h without fire fighting
elif firefighting == 1:
W = 4.1868 * 37000 * F * A**0.82 / a[6] # kg/h with fire fighting
return (W)
elif c == 'VAPOR':
W = 8.769 * math.sqrt(a[3]*P) * (A * (Tw-T1)**1.25 / T1**1.1506)
return (W)
elif b == 'KGS' :
if c == 'LIQUID':
if firefighting == 0:
W = 4.1868 * 61000 * F * A**0.82 / a[6] # kg/h without fire fighting
elif firefighting == 1:
W = 4.1868 * 37140 * F * A**0.82 / a[6] # kg/h with fire fighting
return (W)
elif c == 'VAPOR':
W = 0.277 * math.sqrt(a[3]*P*1000) * (A * (Tw-T1)**1.25 / T1**1.1506)
return (W)
else: # Fire 아닌 경우는 operating condition 으로 배출. 온도만 operating temp.
W = Op_Flow
S = RP.REFPROPdll(Comp_Name,"PT","S",SI,0,0,Pn,Tn,Comp_Value).Output[0]
Ori_A = np.array
for i in Size:
# print (a,Ori_A)
if a < float(Size[0,1]): # 최소값 error항
Ori_A = "Out of API range(under)"
elif a > float(Size[13,2]): # 최대값 error항
Ori_A = "Out of API range(over)"
elif a > float(i[1]): # Orifice size 찾는 항
Ori_A = (i[0], float(i[2]))
# print(i, Ori_A)
return Ori_A # Ori_A 에는 designation, Orifice area 정보 포함.
Required Orifice size (kb 에 따라 2개 list 로 정의함.)
대부분 0을 쓰나, subcritical 경우에는 vendor 에 문의, API520표에 보면 bellows 에서 10% overpressure 일대는 표 참조.
------------------- Cri, Sub-cri 로 이미 계산되어 kb_f 제외 22.04.29
kb_f = int(ws.Range("F46").Value)
Ori_A = Ori(Req_Ori)
def Flange(a,b): # a : Spring / Pilot, b : Flange size
print (a,b)
if a == "Bellows" or a == "Conventional":
Ori_Table = df2.loc[Ori(Req_Ori)[0]] # Orifice designation 에 맞는 table 찾기
df_Orifice = Ori_Table.loc[Ori_Table["Flange_Inlet"] == b, :]
import os, numpy as np import math import matplotlib.pyplot as plt
from win32com.client import Dispatch import pandas as pd import numpy as np import pickle
import csv import openpyxl
from ctREFPROP.ctREFPROP import REFPROPFunctionLibrary
Revision 2 : sub-critical flow 추가
RP = REFPROPFunctionLibrary(os.environ['RPPREFIX']) RP.SETPATHdll(os.environ['RPPREFIX'])
Reference Table 불러오기
df1 = pd.read_pickle('Pipetable.p')
df2 = pd.read_pickle('Ori_Spring.p')
df3 = pd.read_pickle('Ori_Pilot.p')
Input value load
excel = Dispatch('excel.Application') # win32com.client로 excel call openfiledir = os.getcwd() + '\' # 현재 폴더 위치 5 filename = 'MPB_Ins.xlsx' # 현재 폴더 위치의 해당 파일명의 .xlsx 파일 open
excel.Visible = True
wb = excel.Workbooks.Open(openfiledir + filename) ws = wb.Worksheets("Input")
ws2 = wb.Sheets.Add(Before = None , After = wb.Sheets(wb.Sheets.count))
Composition 설정
Comp_Name = "Nitrogen;Methane;Ethane;Propane;Butane;Isobutane;Pentane;Isopentane"
Comp_Value = [ws.Range("F5").Value, ws.Range("F6").Value, ws.Range("F7").Value, ws.Range("F8").Value, ws.Range("F9").Value, ws.Range("F10").Value, ws.Range("F11").Value, ws.Range("F12").Value]
Input value load
Tag = ws.Range("F2").Value # Tag name, Sheet name 으로 사용.
Fluid = ws.Range("F3").Value
P_set = ws.Range("F16").Value # Set Pressure. P = ws.Range("F17").Value # Relieving Pressure. P1 = ws.Range("F40").Value # Set P + Allowable overpressure + Atmospheric pressure (Inlet Dp 고려??)
F = ws.Range("F25").Value # F factor 는 input 으로 받아야 함. A = ws.Range("F26").Value # Pipe 면적 firefighting = ws.Range("F27").Value # API에 해당하는 인자. firefighting 이 있으면 1 없으면 0 Op_Flow = ws.Range("F37").Value # kg/h operating flowrate (VAPOR 경우, 공급유량 만큼 빼주게 끔)
Tw = ws.Range("F36").Value # K 탄소강판 권장 최대 용기 온도 593 도 Pn = ws.Range("F32").Value # MPa operating pressure Tn = ws.Range("F35").Value # K operating temperature T1 = P/Pn * Tn
Rule = ws.Range("F20").Value # Code 선택 Case = ws.Range("F21").Value # Case 선택 Phase = ws.Range("F22").Value # Phase 선택
Type = ws.Range("F49").Value # Type 선택 Fl_in = ws.Range("F50").Value # Flange rating 선택
P_vent = ws.Range("F42").Value # Vent pressure
단위계 설정
SI = RP.GETENUMdll(0,"SI WITH C").iEnum
Property = "T;CP/CV;Z;M;D;H;"
print (RP.REFPROPdll("AIR","PT","T;H;M;D;T;Z;CP/CV",SI,0,0,0.101325,0,[1,0]).Output[0:Property.count(';')])
print (RP.REFPROPdll("AIR","PT","M;D;H",SI,0,0,0.101325,0,[1,0]).Output[0:Property.count(';')])
Ref. Air 물성치 설정
AIR = np.array(RP.REFPROPdll("AIR","PT",Property,SI,0,0,0.101325,0,[1,0]).Output[0:Property.count(';')])
def Rel_T(a): # if 문으로 liquid, vapor 나누어야 함. Qmass = 0.01
Comp_Rel_L = T.Output[len(Comp_Value):len(Comp_Value)*2] # Liquid composition
Pro_L = RP.REFPROPdll(Comp_Name,"Pvap",Property,SI,0,0,a,Q,Comp_Rel_L) # Liquid composition 의 Properties
print (Pro_V.Output[0:Property.count(';')])
Rel_Pro = Rel_T(P)
def Ins_PRV(a, b): D = math.sqrt(a[1]*(2/(a[1]+1))*((a[1]+1)/(a[1]-1))) D_AIR = math.sqrt(AIR[1](2/(AIR[1]+1))*((AIR[1]+1)/(AIR[1]-1))) W = b (D/D_AIR) math.sqrt((a[3]/AIR[3]) (AIR[0]+273.15)/(a[0]+273.15) * (a[2] / AIR[2]) ) # kg/h return W Ins_nominal = ws.Range("H37").Value W_ins = Ins_PRV(Rel_Pro, Ins_nominal) print(W_ins)
ws_report.Range("F37").Value = W_ins
def Code(a,b,c,d): # Property , b : IGF / API / KOSHA / KGS, c : 'LIQUID' / 'VAPOR', d: Case
S = RP.REFPROPdll(Comp_Name,"PT","S",SI,0,0,Pn,Tn,Comp_Value).Output[0]
Tr = RP.REFPROPdll(Comp_Name,"PS","T",SI,0,0,P,S,Comp_Value).Output[0]
----------------------- Tank PRV 50% 2개 일때 사용하는 코드 추가 22.04.29
Category = ws.Range("G26").Value # K operating temperature
Rel_Flowrate = np.array(Code(Rel_Pro,Rule,Phase,Case)) ## Required if Category == "Tank": Rel_Flowrate = Rel_Flowrate/2 else: Rel_Flowrate = Rel_Flowrate
-----------------------------
Orifice Sizing
P2 = ws.Range("F42").Value # MPa Kd = ws.Range("F44").Value # C = 0.03948 math.sqrt(Rel_Pro[1] (2/(Rel_Pro[1]+1)) ** ((Rel_Pro[1]+1)/(Rel_Pro[1]-1)))
Kb : initial 1 (for conventional, pilot type), calculated value 2()
Kb = np.array([1, (1/(17.9 C) math.sqrt((Rel_Pro[1]/(Rel_Pro[1]-1)) * (P2/P)(2/Rel_Pro[1]) - (P2/P)((Rel_Pro[1]+1)/Rel_Pro[1]) ))])
kc 는 1을 보통 쓰나, critical flow 가 나뉘면서 metrix 화되어, 공통 인자는 kc를 list 화 시킴.
------------------------kc 가 왜 2개로 되어있는지 모르겠음. 하나로 수정해서 Req_ori 도 list 가 아닌 float 으로만 남김. (22.04.29)
Kc = ws.Range("F45").Value
Kc = np.array([ws.Range("F45").Value, 1]) # Combination correction factor of installations with a rupture disk, No rupture disk = 1
Critical flow check
P_criflow = P1000 (2/(Rel_Pro[1]+1))*(Rel_Pro[1]/(Rel_Pro[1]-1)) if P_criflow > P21000 : # Critical flow 에서는 P1 이 relieving pressure. Inlet pressure 가야하나? Check_cri = "Critical flow"
Required Orifice size (kb 에 따라 2개 list 로 정의함.)
else: # Sub critical flow 에서는 P1 을 Valve Inlet pressure 로 정의. Inlet pressure drop 도 고려해서 orifice size 크게 가져간다. Check_cri = "Sub-Critical flow" r = P2/P1 F2 = math.sqrt( (Rel_Pro[1]/(Rel_Pro[1]-1))* r*(2/Rel_Pro[1]) (1- r*((Rel_Pro[1]-1)/Rel_Pro[1]))/(1-r) ) Req_Ori = 17.9 Rel_Flowrate / (F2 Kd Kc) math.sqrt((Rel_Pro[0]+273.15)Rel_Pro[2]/ (Rel_Pro[3] P11000(P11000 - P21000))) # Sub-critical flow
Req_Ori_ref = Rel_Flowrate / (C Kd P1000 Kb[1] Kc) math.sqrt((Rel_Pro[0]+273.15)Rel_Pro[2]/Rel_Pro[3]) print("r :", r, "F2 :", F2,"P1 :", P1,"P2 :", P2)
Required Orifice size (kb 에 따라 2개 list 로 정의함.)
Req_Ori = Rel_Flowrate / (C Kd P1000 Kb Kc) math.sqrt((Rel_Pro[0]+273.15)*Rel_Pro[2]/Rel_Pro[3]) # critical flow
r = P2/P
F2 = math.sqrt( (Rel_Pro[1]/(Rel_Pro[2]-1))* r*(2/Rel_Pro[2]) (1- r**((Rel_Pro[2]-1)/Rel_Pro[2]))/(1-r) )
Req_Ori = 17.9 Rel_Flowrate / (F2 Kd Kc) math.sqrt((Rel_Pro[0]+273.15)Rel_Pro[2]/ (Rel_Pro[3] P1000(P1000 - P21000))) # Sub-critical flow
API 520의 Orifice Size, Designation 구하는 함수
def Ori(a): # a : Req_Ori[0] Size = np.array([['D', 0.000001, 70.97], ['E', 70.97, 126.45], ['F', 126.45, 198.06], ['G', 198.06, 324.52], ['H', 324.52, 506.45], ['J', 506.45, 830.32], ['K', 830.32, 1185.8], ['L', 1185.8, 1840.64], ['M', 1840.64, 2322.58], ['N', 2322.58, 2799.99], ['P', 2799.99, 4116.12], ['Q', 4116.12, 7129.02], ['R', 7129.02, 10322.56], ['T', 10322.56, 16774.16]] )
Required Orifice size (kb 에 따라 2개 list 로 정의함.)
대부분 0을 쓰나, subcritical 경우에는 vendor 에 문의, API520표에 보면 bellows 에서 10% overpressure 일대는 표 참조.
------------------- Cri, Sub-cri 로 이미 계산되어 kb_f 제외 22.04.29
kb_f = int(ws.Range("F46").Value)
Ori_A = Ori(Req_Ori)
def Flange(a,b): # a : Spring / Pilot, b : Flange size
print (a,b)
print (Orifice)
print (Orifice)
kc는 critical flow list 맞추기 위해 사용. 0,1 모두 1의 값을 가짐
if Ori_A == "Out of API range(under)" or Ori_A =="Out of API range(over)": data_flange = [["-","-","-","-"]] df_Orifice = pd.DataFrame(data_flange, columns =['Flange_Inlet','Orifice_Inlet','OrificeOutlet','Flange Outlet']) Rated_flowrate = "-" df_Orifice["Required Capacity (kg/h)"] = Rel_Flowrate df_Orifice["Orifice Area (m2)"] = Ori_A df_Orifice["Code"] = Rule df_Orifice["Case"] = Case df_Orifice["Phase"] = Phase
else:
---------------------------------------- Cri, Sub-cri 에 따라 kb 값 사용
------------------------------------------
df_Orifice = Flange(Type,Fl_in)
df_Orifice["Required Capacity (kg/h)"] = Rel_Flowrate
df_Orifice["Orifice Area (m2)"] = Ori_A[1]
df_Orifice["Code"] = Rule
df_Orifice["Case"] = Case
df_Orifice["Phase"] = Phase
print (df_Orifice)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Report 작성 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
wb.Sheets("Format").Copy(Before = None, After = wb.Sheets("Format")) wb.Activesheet.Name = Tag ws_report = wb.Worksheets(Tag)
ws_report.Range("L6").Value = Tag ws_report.Range("L7").Value = Type ws_report.Range("L8").Value = Rule ws_report.Range("AA6").Value = Case ws_report.Range("AA8").Value = Fl_in if Ori_A == "Out of API range(under)" or Ori_A =="Out of API range(over)": ws_report.Range("AD8").Value = dfOrifice['Flange Outlet'] ws_report.Range("L13").Value = df_Orifice['Orifice Area (m2)'] ws_report.Range("AA9").Value = df_Orifice['Required Capacity (kg/h)'] ws_report.Range("AA12").Value = Ori_A else: ws_report.Range("AD8").Value = float(dfOrifice['Flange Outlet']) ws_report.Range("L13").Value = float(df_Orifice['Orifice Area (m2)']) ws_report.Range("AA9").Value = float(df_Orifice['Required Capacity (kg/h)']) ws_report.Range("AA12").Value = Ori_A[0] ws_report.Range("L9").Value = F ws_report.Range("L10").Value = Rel_Pro[6] ws_report.Range("L11").Value = A ws_report.Range("L12").Value = Req_Ori ws_report.Range("AA10").Value = Rated_flowrate ws_report.Range("AA11").Value = Rel_Pro[0]
ws_report.Range("AA13").Value = df_Orifice['Orifice_Inlet'] ws_report.Range("AA14").Value = P_vent1000 ws_report.Range("AD13").Value = df_Orifice['Orifice_Outlet'] ws_report.Range("L14").Value = Fluid ws_report.Range("L15").Value = Rel_Pro[3] ws_report.Range("L16").Value = Rel_Pro[1] ws_report.Range("L17").Value = Rel_Pro[2] ws_report.Range("L19").Value = P_set ws_report.Range("L21").Value = P1000
ws_report.Range("AA16").Value = Kb[0]
ws_report.Range("AA20").Value = Kc ws_report.Range("AA21").Value = Kd ws_report.Range("AA22").Value = C ws_report.Range("AA16").Value = Check_cri
if Check_cri =="Sub-Critical flow": ws_report.Range("AB34").Value = P11000 - 101.325 else: ws_report.Range("AB34").Value = P1000 - 101.325
Composition write
C = RP.REFPROPdll(Comp_Name,"PQmass","XMOLEVAP;XMOLELIQ;HEATVAPZ_P",SI,0,0,P,0.01,Comp_Value) Composition = C.Output[0:len(Comp_Value)] # Vapor composition
ws_report.Range("E44").Value = Composition[0] ws_report.Range("E45").Value = Composition[1] ws_report.Range("E46").Value = Composition[2] ws_report.Range("E47").Value = Composition[3] ws_report.Range("E48").Value = Composition[4] ws_report.Range("E49").Value = Composition[5] ws_report.Range("E50").Value = Composition[6] ws_report.Range("E51").Value = Composition[7]
df1 = pd.read_pickle('Pipetable.p')
def pipe(a,b): # ND, 'Sch'
print ("ND".center(8),"Schedule".center(8),"Inner_Dia.".center(12), "Out_Dia.".center(12), "Thickness".center(12))
print (a, b, df1.loc[a,"Out_Dia"]-2*df1.loc[a,b], df1.loc[a,"Out_Dia"], df1.loc[a,b])
pipe(50,'10s')
#