Esempio n. 1
0
def rate_of_change_product(prod,state,prod_orifice=0,verbose=False):
  #state is container supplying the product
  P_N=state.P[-1]*norm.P0
  P_tank=prod.Pprod*norm.P0
  #compute flows in sN/m3
  in_prod_flow=orifice.orifice_flow2(P_N/1000,P_tank/1000,prod_orifice,T=param.Ta)/60
  #output from product tank
  out_prod_flow=orifice.orifice_flow2(P_tank/1000,1e5/1000,param.product_orifice,T=param.Ta)/60
  #these are flow rates in Nm^3/second
  #convert prod_flow to dimensionless flow rates
  in_prod_flow/=norm.z0**2*norm.v0
  out_prod_flow/=norm.z0**2*norm.v0
  net_flow=in_prod_flow-out_prod_flow
  #Product tank
  dPprod=dg.kprod*net_flow
  #if dg.kprod > 1e6 or prod.Pprod > 1e6 or state.yA[-1] - prod.yprod) > 1e6 or in_prod_flow>1e6:
  with warnings.catch_warnings():
    warnings.filterwarnings('error')
    try:
      dyprod=dg.kprod/prod.Pprod*(state.yA[-1] - prod.yprod)*in_prod_flow
    except Warning:
      sys.stdout.write('WARNING:{} {} {} {}\n'.format(prod.Pprod, state.yA, prod.yprod, in_prod_flow))
      raise 
  #print('in={} out={}'.format(in_prod_flow,out_prod_flow))
  #print(prod.Pprod,prod.yprod,net_flow,dg.kprod)
  #print('d',dPprod,dyprod)
  return dPprod, dyprod
Esempio n. 2
0
def rate_of_change(state, in_P=None, out_P=None,
                   in_orifice=0, out_orifice=0,
                   in_yA=None,
                   in_yB=None,
                   out_yA=None,
                   out_yB=None,verbose=False,
                   prod_orifice=0, modehalf=None):
  #calculate in and out velocities and call rate_of_change_vel
  #orifice_flow2(p1, p2, d, C=0.70, T=T_room):
  P_feed=in_P*norm.P0       #convert to Pa abs
  P_1=state.P[0]*norm.P0    #denormalize, convert to Pa
  if verbose:
    sys.stdout.write('state.P: {}\n'.format(state.P))
  P_N=state.P[-1]*norm.P0
  P_exit=out_P*norm.P0
  #compute flows in sN/m3
  if verbose:
    print('P_feed {} P_1 {} P_N {} P_exit {}'.format(P_feed,P_1,P_N,P_exit))
  in_flow=orifice.orifice_flow2(P_feed/1000,P_1/1000,in_orifice,T=param.Ta)/60
  out_flow=orifice.orifice_flow2(P_N/1000,P_exit/1000,out_orifice,T=param.Ta)/60
  #NOTE:
  #HERE WE override the flow rates in m/3
  #if modehalf is not None and modehalf[0]==1:
  #  #we are doing pressurization, override the inflow rate with a constant
  #  in_flow=9.5/60/1000    # LPM to in Nm^3/sec
  #these are flow rates in Nm^3/second
  #now convert to velocity in m/s
  with warnings.catch_warnings():
    warnings.filterwarnings('error')
    try:
      in_vel=in_flow*(1e5/P_1)/param.area    # [m/s]
      out_vel=out_flow*(1e5/P_N)/param.area  # [m/s]
    except Warning:
      sys.stdout.write('WARNING: {} {}\n'.format(in_flow, P_1))
      sys.stdout.write('WARNING: {} {}\n'.format(out_flow, P_N))
      raise 
  #convert prod_flow to dimensionless flow rates
  if verbose:
    print('in_vel={}'.format(in_vel))
    print('out_vel={}'.format(out_vel))
    print('Pressure: {}'.format(state.P))
  #convert to dimensionless
  in_vel/=norm.v0
  out_vel/=norm.v0
  #constant_vel=True
  #if constant_vel and in_vel>0:
  #  in_vel=1.0
  #  out_vel=0
  jplus5,jminus5=difference.gen_j5_functions(param.mode,in_vel>=0 and out_vel>=0)
  #print('flow rates, m/s {} {}'.format(in_vel, out_vel))
    
  return rate_of_change_vel(state, vin=in_vel, vout=out_vel,
                     in_yA=in_yA, in_yB=in_yB,
                            out_yA=out_yA,out_yB=out_yB,verbose=verbose,
                            jplus5=jplus5,jminus5=jminus5)
Esempio n. 3
0
def product_flow_rate(P):
  #given an array of product pressures, compute the corresponding output flow rates
  #in LPM
  p1=P*norm.P0
  p2=1e5   # 1 atm in [Pa]
  #Nm3/min
  f=orifice.orifice_flow2(p1/1000,p2/1000,param.product_orifice)
  return f*1000   # LPM
def feed_rates(P):
  #return vin, vout (normalized)
  #orifice_flow2(p1, p2, d, C=0.70, T=T_room):
  #input
  P_feed=param.feed_pressure*1e5   # feed_pressure in bar, convert to Pa abs
  P_1=P[0]*norm.P0           #denormalize, convert to Pa
  P_N=P[-1]*norm.P0
  P_exit=1e5                       #1ATM
  #compute flows in sN/m3
  #print('P_feed {} P_1 {} P_N {} P_exit {}'.format(P_feed,P_1,P_N,P_exit))
  in_flow=orifice.orifice_flow2(P_feed/1000,P_1/1000,param.input_orifice,T=param.Ta)
  out_flow=orifice.orifice_flow2(P_N/1000,P_exit/1000,param.output_orifice,T=param.Ta)
  #now convert to velocity in m/s
  in_vel=in_flow*(1e5/P_1)/param.area    # [m/s]
  out_vel=out_flow*(1e5/P_N)/param.area  # [m/s]
  #convert to dimensionless
  #print('flow rates, m/s {} {}'.format(in_vel, out_vel))
  return in_vel/norm.v0, out_vel/norm.v0
Esempio n. 5
0
def create_param(mods, print=print):
    #mods is a dict to override the parameter defaults set here
    #override print function if needed for debug statements to a log file
    #any keys with value of None are ignored
    #real_cycle_time and real_vent_time will override cycle_time and vent_time
    #cycles and time are used to set the simulate end time
    param = AttrDict()
    #Physical constants
    param.R = 8.314  #J/mol-K
    #Simulation parameters
    #the mode for the difference scheme
    param.mode = 1
    param.N = 10  #discretization steps in spatial direction
    param.tstep = 0.20  #time step for ODE (dimensionless)
    param.adsorb = True  #set to True to have adsorption happen
    #cycle_time, vent_time
    param.cycle_time = 36  #time for a half-cycle in dimensionless time units
    param.vent_time = 23.04  #from beginning of cycle in dimensionless time units
    #Physical system parameters
    param.Ta = 20 + 273.15  #room temperature in K
    #Picked these sizes for a volume of about 2000 cm3
    param.D = 0.095  #Diameter of cylinder [m]
    #param.D=0.05    #Diameter of cylinder [m]
    param.L = .33  #Length of cylinder (m)  - 13"
    #param.L=1.00      #Length of cylinder (m)
    param.product_tank_volume = 2.0 / 1000  # 2L in m3
    #we set an orifice so that we can 5LPM at a certain pressure (3bar abs) and
    #try to get an output pressure sits around that 3 bar.
    #param.epsilon=0.36  # void fraction (fraction of space filled with gas vs. spheres)
    param.epsilon = 0.37
    param.epsilonp = 0.35  #particle voidage
    param.rp = 1e-3  #particle radius [m]
    param.tauprime = 3.0  # tortuosity
    #flow params
    param.input_orifice = 2.0  # dia mm - pressure feed
    param.output_orifice = 1.40  # dia mm - to output tank
    #we use an orifice that will provide 5LPM at 3 bar abs product tank
    param.product_orifice = 0.475  # dia mm - output from product tank to patient
    #try a smaller for testing
    #param.product_orifice=0.3   # dia mm - output from product tank to patient
    param.blowdown_orifice = 2.80  # dia mm
    param.vent_orifice = 1  # dia mm
    param.feed_pressure = 3.5  #pressure in bar (1e5 Pa), absolute
    param.PH = param.feed_pressure * 1e5  #Pa, will be the normalization pressure
    param.feed_O2 = 0.21  #feed gas fraction O2
    param.product_pressure = 2  # bar in output product tank (not used)
    #Note: random sphere packing is 64%, densest is 74%, void fractions=.36, .26
    #param.DL=1.0        #Axial dispersion coefficient (m2/s)
    #Mol wt O=16, N=14
    #air is about 14e-3 kg/mol
    #These are for my calculation, note different from paper (paper was doing
    #CO2 and N2 separation, and used a more complex Langmuir dual-site model
    #param.qAs=5.26e-3  #saturation constant for O2  mol/cm3 (at infinite pressure)
    #param.qBs=5.26e-3  #saturation constant for N2  mol/cm3 (at infinite pressure)
    #Langmuir constants.  inverse of pressure mol/m3 where we reach 50% of saturation
    #Estimated these from chart in Yang book at 1 ATM.

    #param.bA=573.5   # cm3/mol, inverse of 42.5 bar
    #param.bB=2030    # cm3/mol, inverse of 12 bar
    #From Mofahari 2013 paper, for Zeolite 13X
    #param.bA=0.042    # 1/bar @ Room temp
    #param.bB=0.616    # 1/bar @ Room temp
    #From Jee paper, same as Mofahari but *0.10 for k3!!
    #adding correct exponent on Pressure
    param.bA = 0.0042  # 1/bar @ Room temp
    param.bB = 0.0616  # 1/bar @ Room temp
    param.qAs = 0.0025  #saturation constant for O2  mol/g (at infinite pressure)
    param.qBs = 0.0073  #saturation constant for N2  mol/g (at infinite pressure)
    param.nA = 1.006  #exponent on bar pressure O2
    param.nB = 0.830  #exponent on bar pressure N2
    param.kA = 0.620
    param.kB = 0.197
    #From Sircar ch22 paper @ 25C
    #param.bA=0.0295    # 1/bar @ Room temp
    #param.bB=0.107    # 1/bar @ Room temp
    #param.qAs=0.00312  #saturation constant for O2  mol/g (at infinite pressure)
    #param.qBs=0.00312  #saturation constant for N2  mol/g (at infinite pressure)

    #For 5A:
    #param.bA=0.052    # 1/bar @ Room temp
    #param.bB=0.165    # 1/bar @ Room temp
    #param.qAs=0.0019  #saturation constant for O2  mol/g (at infinite pressure)
    #param.qBs=0.0025  #saturation constant for N2  mol/g (at infinite pressure)
    #param.kA=.15
    #param.kB=.05

    #From the new paper
    param.rho_s = 1130  #adsorbent density kg/m3
    param.rho_w = 7800  #wall density kg/m3
    param.rho_pellet = 1.160 * 1000  # [kg/m3]
    param.Cpg = 30.7  #specific heat capacity of gas phase [J/mol/K]
    param.Cpa = 30.7  #specific heat capacity of adsorbed phase [J/mol/K]
    param.Cps = 1070  #specific heat capacity of adsorbent [J/kg/K]
    param.Cpw = 502  #specific heat capacity of wall [J/kg/K]
    param.mu = 1.72e-5  #fluid viscocity
    param.Dm = 1.6e-5  #Molecular diffusivity [FOR CO2/N2 paper]
    param.gamma = 1.4  #adiabatic constant
    param.Kz = 0.09  #Effective gas thermal conductivity [J/m/K/s]
    param.Kw = 16  #Effective thermal cond of wall
    param.hin = 8.6  #inside heat transfer coeff
    param.hout = 2.5  #outside heat transfer coeff
    param.R = 8.314  #Gas constant  [m3Pa/mol/K]
    param.eta = .72  #compression/evacuation efficiency
    #not doing Temp and Tw simulation at the moment
    param.HA = 0  #heat of adsorption of component A  [J/mol]
    param.HB = 0  #heat of adsorption of component B  [J/mol]
    #####
    #Apply overrides, ignoring None values.  But see cycles/time logic below
    for k in param.keys():
        if k in mods and mods[k] is not None:
            param[k] = mods[k]
            print('params overriding {}:{}'.format(k, param[k]))
    #####
    #Things we have to calculate, need to do this after the overriding
    param.area = (param.D / 2)**2 * math.pi  #[m2]
    param.volume = param.area * param.L
    print('canister volume is {} L'.format(param.volume * 1000))
    param.rin = param.D / 2
    param.rout = param.rin + 0.25
    param.epst = param.epsilon / (1 - param.epsilon)
    param.epsb = (1 - param.epsilon) / param.epsilon
    param.cellsize = param.L / param.N
    param.deltaZ = 1 / param.N  # nondimensional deltaZ
    param.container_vol = param.area * param.L  #volume of reactor (m3)
    #We compute the initial flow rate and use that for velocity normalization
    in_flow = orifice.orifice_flow2(
        param.feed_pressure * 1e5 / 1000, 100, param.input_orifice,
        T=param.Ta) / 60
    in_vel = in_flow / param.area  # [m/s]
    param.norm_v0 = in_vel  # [m/s]  feed velocity, which varies
    param.norm_t0 = param.L / param.norm_v0  # so t=time/norm.t0
    param.feed_N2 = 1.0 - param.feed_O2  # feed gas fraction N2
    #molecular weight of air is 29 g/mol or 29e-03 kg/mol
    param.rho_g = 100000 / param.R / param.Ta * 29e-3  # Needs to be adjusted for pressure/Temp

    param.Dp = param.Dm / param.tauprime
    #NOTE: norm.v0 is computed after this, will be set to param.v0
    #Axial dispersion coefficient m2/sec
    #param.DL=0.7*param.Dm+0.5*param.norm_v0*param.rp*2.0
    param.DL = 4.9e-4  # m2/sec from another paper
    print('param.DL={}'.format(param.DL))
    #compute param.end_time for the simulation
    #want exactly 1 to be not None and 1 None
    #assert (mod.cycles is None) + (mod.time is None) == 1
    #total time to simulate (dimensionless).  This is handled differently because
    #we allow 2 ways to override (cycles takes precedence over time)
    #First we check for real_cycle_time and real_vent_time
    rct = mods.get('real_cycle_time', None)
    rvt = mods.get('real_vent_time', None)
    #note: param.real_vent_time will only exist if this method of setting was used.
    #simulation should not care about real cycle time, this is just for reporting
    #in log files
    if rct:
        param.cycle_time = rct / param.norm_t0
        param.real_cycle_time = rct
        print('using a real cycle time of {:7.2f} -> {:7.2f}'.format(
            rct, param.cycle_time))
    if rvt:
        param.vent_time = rvt / param.norm_t0
        param.real_vent_time = rvt
        print('using a real vent time of {:7.2f} -> {:7.2f}'.format(
            rvt, param.vent_time))
    param.end_time = 2 * param.cycle_time  #default will be 2 cycles
    if mods.get('cycles', None) is not None:
        param.end_time = param.cycle_time * mods['cycles']
    else:
        if mods.get('time', None) is not None:
            param.end_time = mods['time']
    print('running for end_time of {}'.format(param.end_time))
    #this might not be an integer
    param.cycles = param.end_time / param.cycle_time
    #set real times
    param.real_cycle_time = param.cycle_time * param.norm_t0
    param.real_vent_time = param.vent_time * param.norm_t0
    return param