Open2

DWSIM FOSSEE Custome Model - Isotherma Batch Reactor のトレース

田舎のケミカルエンジニア田舎のケミカルエンジニア

DWSIM ver 8.8.0
FOSSEE Custom Models > isothermal batch reactor の ironpythonのコードを確認する。

FOSSEE(Free/Libre and Open Source Software for Education)は、インドのオープンソースを利用して教育を達成することを目指している団体のようです。(IITの先生方?)

https://fossee.in/

以下を理解して、独自モデルを追加できるようになりたい。

#Reference: H.S. Fogler, Elements of Chemical Reaction Engineering, 2nd edition
#All units in SI units
#Valid for isothermal constant volume batch reactor
#This code is only if out temperature is defined. Does not work if output temperature not given.
#Regarding variables in input section:
#1)time: This is the final time of reaction taking place
#2)Tout: The Temperature of operation of reactor
#3)Pout: The operating pressure of reactor
#4)Vol: The volume of reactor
#5)Adiabatic: If the adiabatic reaction, then 1 else 0
#6)OutTemp: If Out temperature of reaction specified, then 1 else 0
#7)dHT0: The reaction enthalpy at standard temperature(T0)
#8)timestep: The timestep for discretization
#9)noofrxn: The number of reactions
#10)stoi: Array of the stoichiometries of species. Row denotes the stoichiometries of species in reaction. Column denotes the reaction
#11)coeffKf: The coeffiecients of temperature terms of reaction constant
#12)Cpa: The coeffiecients of temperature terms of specific heat capacity
#13) basecomp: The array for base components of a reaction. Row denotes the base components, column denotes the reaction taking place
#14)orders: The array for the order of reaction of basecomponents
#Import statement
from DWSIM.Thermodynamics import *
import math
from System import Array
import clr
clr.AddReference('DWSIM.MathOps.DotNumerics')
from DotNumerics.ODE import *
#Initializing values in input streams (Only one input stream for batch reactor)
feed=[0] #the array containing the properties of feed stream
P=[0]  #array for Pressure of feed stream
massflow=[0] #array for total mass flow of feed stream
molfrac=[0] #array for molar fraction of feed stream
molflow=[0] #array for total molar flow of feed stream
enthalpy=[0] #array for the total enthalpy of feed stream
#Extracting input from streams
feed[0] = ims1
P = feed[0].GetProp("pressure", "Overall", None, "", "")
massflow = feed[0].GetProp("totalFlow" ,"Overall", None, "", "mass")
molflow = feed[0].GetProp("totalFlow" ,"Overall", None, "", "mole")
enthalpy = feed[0].GetProp("enthalpy" ,"Overall", None, "Mixture", "mass")
molfrac = feed[0].GetProp("fraction", "Overall", None, "", "mole")
Tin = feed[0].GetProp("temperature", "Overall", None, "", "")
Pout =feed[0].GetProp("pressure", "Overall", None, "", "")
if Simultaneous==1:
    #dummy variable j is used for jth component
    comp=len(molfrac) #total number of compounds/elements involved in all reactions
    n=int(noofrxn) #Number of reactions
    stoic=[] #Stoichiometry of compounds/elements involved in reaction
    stoic=eval(stoi)
    r=[0]*n #Number of reactants in each reaction
    p=[0]*n #Number of products in each reaction
    ini=[0]*n #array for getting a reactants index for each reaction used while finding limiting reagent
    reac=[[0]*comp for m in range(0,n)] #Array for finding whether a component is reactant or not
    for m in range(0,n):
     for i in range(0,comp):
      if stoic[m][i]<0:
       r[m]=r[m]+1
       reac[m][i]=1
       ini[m]=i
      elif stoic[m][i]>0:
       p[m]=p[m]+1 
    ord=[] #Array for getting order of reaction
    ord=eval(orders)
    basecomponent=[] #Array for getting the basecomponents of a reaction
    basecomponent=eval(basecomp)
    order=[[0]*comp for i in range(0,n)] #List containing the orders of each specie in each reaction
    for m in range(0,n):
     for i,j in zip(range(0,len(basecomponent[m])),basecomponent[m]):
      order[m][j-1]=ord[m][i]
    #Initializing input variables
    molein=[0]*(comp) #Array for the moles of each component in input stream
    concin=[0]*(comp) #Array for the concentration of each component in input stream
    #initial conditions
    y0=[0]*(comp) #Initial concentration
    for i in range(0,comp):
        molein[i]=molflow[0]*molfrac[i]
        concin[i]=molein[i]/Vol
        y0[i]=concin[i] #input moles of each specie  
    #Output variables
    moleout=[0]*(comp) #Array for the moles of each component in output stream
    concout=[0]*(comp) #Array for the concentration of each component in output stream
    noofbase=[0]*n #Number of base components
    for m in range(0,n):
     noofbase[m]=len(basecomponent[m])
    Kfe=[] #Array for coefficients of Temperature in reaction constant for each reaction
    Kfe=eval(coeffKf)
    Kf=[0]*n
    for m in range(0,n):
     Kf[m]=Kfe[m][0]+Kfe[m][1]*Tout+Kfe[m][2]*Tout**2+Kfe[m][3]*Tout**3+Kfe[m][4]/Tout+Kfe[m][5]*math.exp(-Kfe[m][6]*Tout)
    def pro(ilist,alist,ordlist,mrxn): #Defining a product function used in solving differential equations
     t=1
     for i in ilist:
      t=t*alist[i-1]**ordlist[mrxn][i-1]
     return t
    yprime=[0]*comp
    cont=[[0]*comp for m in range(0,n)]
    def ode(t,y):
      for m in range(0,n):
       for i in range(0,comp):
        if y[i]<0 and stoic[m][i]<0:
         y[i]=0
         cont[m][i]=0
        else:
         cont[m][i]=1
      for i in range(0,comp):
       yprime[i]=sum((Kf[m]*stoic[m][i]/Vol*cont[m][i]*pro(basecomponent[m],y,order,m) for m in range(0,n))) 
      return Array[float](yprime)
    func=OdeFunction(ode)
    init=OdeExplicitRungeKutta45(func,comp)
    init.InitializeODEs(func,comp)
    x=Array[float](y0)
    ans = init.Solve(x,0,timestep,time)
    ans2 = []
    dimans=len(ans) #Dimension of ans i.e. the array of solutions 
    y=[0]*len(ans) 
    for i,j in zip(ans,range(0,len(ans))):
        ans2.append(i)
        y[j]=i
    finalconc=[0]*comp #Final concentration of species
    for i in range(0,comp):
       finalconc[i]=y[dimans-comp+i]
    for i in range(0,comp):
      concout[i]=finalconc[i] #Out concentration of species
      if concout[i]<0:
       concout[i]=0
       finalconc[i]=0
     #finding the limiting reagent
    limrat=[0]*n #ratio of concentration and stoichiometry of limiting reagent
    limrea=[0]*n #Array for index of limiting reagent
    for m in range(0,n):
      limrat[m]=y0[ini[m]]/abs(stoic[m][ini[m]])
      limrea[m]=ini[m]
      for i in range(0,comp):
       if reac[m][i]==1:
        if limrat[m]>y0[i]/abs(stoic[m][i]):
         limrea[m]=i
         limrat[m]=y0[i]/abs(stoic[m][i]) 
     #defining out mass
    massout=massflow #The total mass in product stream
     #finding the mole flow rate of out stream
    finalmole=[0]*comp #Final moles of each specie in product stream
    for i in range(0,comp):
      finalmole[i]=finalconc[i]*Vol 
     #total out moles
    totalmoleout=[0] #Total moles in product stream
    for i in range(0,comp):
      totalmoleout[0]=totalmoleout[0]+finalmole[i]
     #finding mole fraction
    molefraco=[0]*comp #Mole frcation of species in product stream
    for i in range(0,comp):
      molefraco[i]=finalmole[i]/totalmoleout[0]
     #settting final temperature
    To=[0] #Temperature in product stream 
    To[0]=Tout
elif Simultaneous==0:
    #dummy variable j is used for jth component
    comp=len(molfrac) #total number of compounds/elements involved in all reactions
    n=int(noofrxn) #Number of reactions
    stoic=[] #Stoichiometry of compounds/elements involved in reaction
    stoic=eval(stoi)
    ini=[0]*n #array for getting a reactants index for each reaction used while finding limiting reagent
    reac=[[0]*comp for m in range(0,n)] #Array for finding whether a component is reactant or not
    r=0
    p=0
    for m in range(0,n):
     for i in range(0,comp):
      if stoic[m][i]<0:
       reac[m][i]=1
       ini[m]=i
       r=r+1
      elif stoic[m][i]>0:
        p=p+1
    ord=[] #Array for getting order of reaction
    ord=eval(orders)
    basecomponent=[] #Array for getting the basecomponents of reactions
    basecomponent=eval(basecomp)
    #Initializing input variables
    molein=[0]*(comp) #Array for the moles of each component in input stream(unit=mole/s)
    concin=[0]*(comp) #Array for the concentration of each component in input stream(unit=mole/(m^3*s))
    #initial conditions
    y0=[0]*(comp) #input concentration of each specie (unit=mole/(m^3*s))
    for i in range(0,comp):
        molein[i]=molflow[0]*molfrac[i]
        concin[i]=molein[i]/Vol
    for i in range(0,comp):
        y0[i]=concin[i]   
    #Output variables
    moleout=[0]*(comp) #Array for the moles of each component in output stream (unit=mole/s)
    concout=[0]*(comp) #Array for the concentration of each component in output stream (unit=mole/(m^3*s))
    for m in range(0,int(n)):
            noofbase=len(basecomponent[m]) #Number of base components in mth reaction
            #defining list containing orders of reactions
            order=[0]*(comp) #Array of order of all species whether involved or not in mth reaction
            for i,j in zip(basecomponent[m],range(0,len(ord[m]))):
             order[i-1]=ord[m][j]
            #Calculating value of Kf
            Kfe=[] #Array for getting the input values of coefficients of temperature in reaction constant 
            Kfe=eval(coeffKf)
            Kf=Kfe[m][0]+Kfe[m][1]*Tout+Kfe[m][2]*Tout**2+Kfe[m][3]*Tout**3+Kfe[m][4]/Tout+Kfe[m][5]*math.exp(-Kfe[m][6]*Tout) #Evaluating the reaction constant
            #assuming same Tout for all the reactions
            yprime=[0] #variable for dy/dt
            def ode(t,y): 
             pro=1  #check for -ve concentrations
             for i in basecomponent[m]: #if a basecomponent's number is given 3 in input, it means that index of that compound is 2
               for j in range(0,comp): 
                if y0[j]+stoic[m][j]*y[0]<0: #If the concentration at any instant of goes negative, stop the reaction containing that specie
                 pro=0
                 break
               if pro!=0:
                 pro=pro*(y0[i-1]+stoic[m][i-1]*y[0])**order[i-1] #y(variable) denotes the reaction coordinate of a reaction at time t
             yprime[0]=Kf*pro
             return Array[float](yprime)
            func=OdeFunction(ode)
            init=OdeExplicitRungeKutta45(func,1)
            init.InitializeODEs(func,1)
            x=Array[float]([0])
            ans = init.Solve(x,0,time,time)
            ans2 = []
            for i in ans:
              ans2.append(i)
              y=i
            #equation to be solved by ode solver: dx/dt=Kf(a0-a*x)^(order(A))*(b0-b*x)^(order(B))
            for j in range(0,comp):
              concout[j]=y0[j]+stoic[m][j]*y #(unit=mole/s)
            #finding the limiting reagent
            limrat=[0]*n
            limrea=[0]*n
            #finding concentration out
            for m in range(0,n):
             limrat[m]=y0[ini[m]]/abs(stoic[m][ini[m]])
             limrea[m]=ini[m]
             for i in range(0,comp):
               if reac[m][i]==1:
                if limrat[m]>y0[i]/abs(stoic[m][i]):
                 limrea[m]=i
                 limrat[m]=y0[i]/abs(stoic[m][i])
             if concout[limrea[m]]<0:
               concout[limrea[m]]=0
               for i in range(0,comp):
                concout[i]=y0[i]-stoic[m][i]/stoic[m][limrea[m]]*y0[limrea[m]]
            #setting new values of concentration in for new reaction
            for e in range(0,comp):
             y0[e]=concout[e] #(unit=mole/(m^3*s)) 
            totalmoleout=[0] #(unit=mole/s)
            for i in range(0,comp):
             moleout[i]=concout[i]*Vol
             totalmoleout[0]=totalmoleout[0]+moleout[i]
            molefraco=[0]*(comp)
            for i in range(0,comp):
             molefraco[i]=moleout[i]/totalmoleout[0]
            #Calculating mass out
            massout=[0] #unit=Kg/s
            massout[0]=massflow[0] 
            #setting out temperature
            To=[0] #unit=Kelvin
            To[0]=Tout
out=[0]
out[0]=oms1
out[0].Clear()
out[0].ClearAllProps()
out[0].SetProp("totalFlow" ,"Overall", None, "", "mass",massout)
out[0].SetProp("totalflow", "Overall",None, "","mole",totalmoleout)
out[0].SetProp("fraction","Overall",None,"","mole",molefraco)
out[0].SetProp("pressure", "Overall", None, "", "",Pout)  #Not able to set the desired pressure value
out[0].SetProp("temperature", "Overall", None, "", "",To)

田舎のケミカルエンジニア田舎のケミカルエンジニア

先のソースコードを開くまでは、DWSIMを起動後、シミュレーションファイルを新規作成し、物質、物性、単位系の設定後、Flowsheet画面になる。画面右のFlowsheet ObjectsからFOSSEE Custom modelsを開き、isothermal batch reactor のアイコンをFlowsheet上にドラッグアンドドロップする。

画面左の Open Python Script Editor を押すと、ソースコードが表示される。

また、シンボルをドラッグアンドドロップすると、このモデルについて説明PDFが自動で開く。

説明概要は、
「このプログラムは、等温バッチ反応器のために書かれたコードの要約です。このプログラムの機能は、反応設計方程式のセットを解くことです(完全分離モデル、すなわちバッチ反応器モデルを使用)。これらの方程式は、同時にまたは順次に解くオプションがあります。同時条件の場合、入力される時間は反応の総時間です。一方、順次条件の場合、入力される時間は各反応にかかる時間であり、総時間は入力された時間×反応の数となります。」 (マニュアルを翻訳)

仮定は

  1. 定容バッチ反応器
  2. 反応は不可逆で、前進反応のみで構成されています(コードは少し変更するだけで可逆反応に簡単に拡張できます)
  3. 反応器の温度は、外部の冷却/加熱媒体を使用してほぼ一定の温度に維持されます

モル/質量バランスはバッチリアクターで次のように書かれます。

\frac{dc_1}{dt} = k \gamma c_1^a c_2^b
c1: 定容バッチ反応器内の物質1の濃度
c2: 定容バッチ反応器内の物質2の濃度
t: 時間
k: 正反応定数
a/b: 1/2に関する反応の次数
γ: 物質1の化学計量係数

参照
DWSIM、FOSSEE custom models の isothermal batch reactor のマニュアル