You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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 로 정의함.)
Req_Ori = Rel_Flowrate / (C * Kd * P1000 * Kb[0] * Kc) * math.sqrt((Rel_Pro[0]+273.15)Rel_Pro[2]/Rel_Pro[3]) # critical flow
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 * P*1000 * 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 * P*1000 * 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)
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, :]
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','Orifice_Outlet','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 값 사용
if Check_cri =='Critical flow':
kb_f = 0
else:
kb_f = 1
#------------------------------------------
Rated_flowrate = Ori_A[1] * C * Kd * P1000 * Kb[kb_f] * Kc * math.sqrt(Rel_Pro[3]/(Rel_Pro[0]+273.15)/Rel_Pro[2])
#(C * Kd * P1000 * Kb[0] * Kc) * math.sqrt((Rel_Pro[0]+273.15)*Rel_Pro[2]/Rel_Pro[3])
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
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 = df_Orifice['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(df_Orifice['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')
The text was updated successfully, but these errors were encountered: