def test_Reactor(self): phase = ct.PureFluid('liquidvapor.xml', 'oxygen') air = ct.Solution('air.xml') phase.TP = 93, 4e5 r1 = ct.Reactor(phase) r1.volume = 0.1 air.TP = 300, 4e5 r2 = ct.Reactor(air) r2.volume = 10.0 air.TP = 500, 4e5 env = ct.Reservoir(air) w1 = ct.Wall(r1,r2) w1.expansion_rate_coeff = 1e-3 w2 = ct.Wall(env,r1, Q=500000, A=1) net = ct.ReactorNet([r1,r2]) net.atol = 1e-10 net.rtol = 1e-6 states = ct.SolutionArray(phase, extra='t') for t in np.arange(0.0, 60.0, 1): net.advance(t) states.append(TD=r1.thermo.TD, t=net.time) self.assertEqual(states.X[0], 0) self.assertEqual(states.X[-1], 1) self.assertNear(states.X[30], 0.54806, 1e-4)
def setUp(self): self.referenceFile = pjoin(os.path.dirname(__file__), 'data', 'WallTest-integrateWithAdvance.csv') # reservoir to represent the environment self.gas0 = ct.Solution('air.xml') self.gas0.TP = 300, ct.one_atm self.env = ct.Reservoir(self.gas0) # reactor to represent the side filled with Argon self.gas1 = ct.Solution('air.xml') self.gas1.TPX = 1000.0, 30*ct.one_atm, 'AR:1.0' self.r1 = ct.Reactor(self.gas1) # reactor to represent the combustible mixture self.gas2 = ct.Solution('h2o2.xml') self.gas2.TPX = 500.0, 1.5*ct.one_atm, 'H2:0.5, O2:1.0, AR:10.0' self.r2 = ct.Reactor(self.gas2) # Wall between the two reactors self.w1 = ct.Wall(self.r2, self.r1, A=1.0, K=2e-4, U=400.0) # Wall to represent heat loss to the environment self.w2 = ct.Wall(self.r2, self.env, A=1.0, U=2000.0) # Create the reactor network self.sim = ct.ReactorNet([self.r1, self.r2])
def create_reactors(self, add_Q=False, add_mdot=False, add_surf=False): self.gas = ct.Solution('gri30.xml') self.gas.TPX = 900, 25*ct.one_atm, 'CO:0.5, H2O:0.2' self.gas1 = ct.Solution('gri30.xml') self.gas1.ID = 'gas' self.gas2 = ct.Solution('gri30.xml') self.gas2.ID = 'gas' resGas = ct.Solution('gri30.xml') solid = ct.Solution('diamond.xml', 'diamond') T0 = 1200 P0 = 25*ct.one_atm X0 = 'CH4:0.5, H2O:0.2, CO:0.3' self.gas1.TPX = T0, P0, X0 self.gas2.TPX = T0, P0, X0 self.r1 = ct.IdealGasReactor(self.gas1) self.r2 = self.reactorClass(self.gas2) self.r1.volume = 0.2 self.r2.volume = 0.2 resGas.TP = T0 - 300, P0 env = ct.Reservoir(resGas) U = 300 if add_Q else 0 self.w1 = ct.Wall(self.r1, env, K=1e3, A=0.1, U=U) self.w2 = ct.Wall(self.r2, env, A=0.1, U=U) if add_mdot: mfc1 = ct.MassFlowController(env, self.r1, mdot=0.05) mfc2 = ct.MassFlowController(env, self.r2, mdot=0.05) if add_surf: self.interface1 = ct.Interface('diamond.xml', 'diamond_100', (self.gas1, solid)) self.interface2 = ct.Interface('diamond.xml', 'diamond_100', (self.gas2, solid)) C = np.zeros(self.interface1.n_species) C[0] = 0.3 C[4] = 0.7 self.surf1 = ct.ReactorSurface(self.interface1, A=0.2) self.surf2 = ct.ReactorSurface(self.interface2, A=0.2) self.surf1.coverages = C self.surf2.coverages = C self.surf1.install(self.r1) self.surf2.install(self.r2) self.net1 = ct.ReactorNet([self.r1]) self.net2 = ct.ReactorNet([self.r2]) self.net1.set_max_time_step(0.05) self.net2.set_max_time_step(0.05) self.net2.max_err_test_fails = 10
def setup_reactor(self): self.gas = ct.Solution(self.rxnmech) self.xinit = {"O2": 0.21, "N2": 0.79} self.gas.TPX = self.T0, self.p0, self.xinit self.injection_gas, _ = setup_injection_gas(self.rxnmech, self.fuel, pure_fuel=True) self.injection_gas.TP = self.T0, self.p0 fuel_res = ct.Reservoir(self.injection_gas) # Create the reactor object self.reactor = ct.Reactor(self.gas) self.rempty = ct.Reactor(self.gas) # Set the initial states of the reactor self.reactor.chemistry_enabled = True self.reactor.volume = self.history["V"][0] # Add in a fuel injector self.injector = ct.MassFlowController(fuel_res, self.reactor) # Add in a wall that moves according to piston velocity self.piston = ct.Wall( left=self.reactor, right=self.rempty, A=np.pi / 4.0 * self.bore**2, U=0.0, velocity=self.history["piston_velocity"][0], ) # Create the network object self.sim = ct.ReactorNet([self.reactor])
def setup(order): gas1.TPX = 1200, 1e3, 'H:0.002, H2:1, CH4:0.01, CH3:0.0002' gas2.TPX = 900, 101325, 'H2:0.1, OH:1e-7, O2:0.1, AR:1e-5' net = ct.ReactorNet() rA = ct.Reactor(gas1) rB = ct.Reactor(gas2) if order % 2 == 0: wA = ct.Wall(rA, rB) wB = ct.Wall(rB, rA) else: wB = ct.Wall(rB, rA) wA = ct.Wall(rA, rB) wA.left.kinetics = interface wB.right.kinetics = interface wA.area = 0.1 wB.area = 10 C1 = np.zeros(interface.nSpecies) C2 = np.zeros(interface.nSpecies) C1[0] = 0.3 C1[4] = 0.7 C2[0] = 0.9 C2[4] = 0.1 wA.left.coverages = C1 wB.right.coverages = C2 if order // 2 == 0: net.addReactor(rA) net.addReactor(rB) else: net.addReactor(rB) net.addReactor(rA) return rA,rB,wA,wB,net
def __init__(self, initial_temperature, initial_pressure, volume, is_reactive, end_temp=2500., end_time=0.2, chem_file='species.cti', cti_source=None): if volume is None: volume = np.genfromtxt('volume.csv', delimiter=',') inp_time = volume[:, 0] inp_vol = volume[:, 1] self.time = [] self.temperature = [] self.pressure = [] self.input_volume = volume self.simulated_volume = [] self.end_temp = end_temp self.end_time = end_time self.is_reactive = is_reactive self.chem_file = chem_file self.initial_temperature = initial_temperature self.initial_pressure = initial_pressure if cti_source is None: gas = ct.Solution(chem_file) else: gas = ct.Solution(source=cti_source) gas.TP = self.initial_temperature, self.initial_pressure if not self.is_reactive: gas.set_multiplier(0) reac = ct.IdealGasReactor(gas) env = ct.Reservoir(ct.Solution('air.xml')) ct.Wall(reac, env, A=1.0, velocity=VolumeProfile(inp_time, inp_vol)) netw = ct.ReactorNet([reac]) netw.set_max_time_step(inp_time[1]) self.time.append(netw.time) self.temperature.append(reac.T) self.pressure.append(gas.P/1E5) self.simulated_volume.append(reac.volume) while reac.T < self.end_temp and netw.time < self.end_time: netw.step() self.time.append(netw.time) self.temperature.append(reac.T) self.pressure.append(gas.P/1E5) self.simulated_volume.append(reac.volume) self.time = np.array(self.time) self.pressure = np.array(self.pressure) self.temperature = np.array(self.temperature) self.simulated_volume = np.array(self.simulated_volume) self.derivative = self.calculate_derivative(self.pressure, self.time)
def makeReactors(self): self.net = ct.ReactorNet() self.gas = ct.Solution('diamond.xml', 'gas') self.solid = ct.Solution('diamond.xml', 'diamond') self.interface = ct.Interface('diamond.xml', 'diamond_100', (self.gas, self.solid)) self.r1 = ct.Reactor(self.gas) self.net.addReactor(self.r1) self.r2 = ct.Reactor(self.gas) self.net.addReactor(self.r2) self.w = ct.Wall(self.r1, self.r2)
def def_walls(r, env, zone, z, v_factor, keywords, t_wall, a_rcm): """ Defines walls for handling the volume variation and heat transfer Returns ------- wq : List of Cantera Wall objects handling the heat transfer wv: List of Cantera Wall objects handling the volume trace """ wq = [0] wv = [0] q = heat_transfer(z, zone, r, t_wall) wq.append(ct.Wall(r[1], env, A=zone[1].surface_area, Q=q[1])) for x in range(2, z): wq.append(ct.Wall(r[x], env, A=zone[x].surface_area, Q=q[x])) wq.append(ct.Wall(r[z], env, A=zone[z].surface_area, Q=q[z])) for x in range(1, z + 1): wv.append(ct.Wall(r[x], env, velocity=VolumeProfile(keywords, a_rcm))) wv[x].area = v_factor[x] return wq, wv
def setUp(self): # reservoir to represent the environment self.gas0 = ct.Solution('air.xml') self.gas0.TP = 300, ct.one_atm self.env = ct.Reservoir(self.gas0) # reactor to represent the side filled with Argon self.gas1 = ct.Solution('air.xml') self.gas1.TPX = 1000.0, 30 * ct.one_atm, 'AR:1.0' self.r1 = ct.Reactor(self.gas1) # reactor to represent the combustible mixture self.gas2 = ct.Solution('h2o2.xml') self.gas2.TPX = 500.0, 1.5 * ct.one_atm, 'H2:0.5, O2:1.0, AR:10.0' self.r2 = ct.Reactor(self.gas2) # Wall between the two reactors self.w1 = ct.Wall(self.r2, self.r1, A=1.0, K=2e-4, U=400.0) # Wall to represent heat loss to the environment self.w2 = ct.Wall(self.r2, self.env, A=1.0, U=2000.0) # Create the reactor network self.sim = ct.ReactorNet([self.r1, self.r2])
def test_sensitivities2(self): net = ct.ReactorNet() gas1 = ct.Solution('diamond.xml', 'gas') solid = ct.Solution('diamond.xml', 'diamond') interface = ct.Interface('diamond.xml', 'diamond_100', (gas1, solid)) r1 = ct.IdealGasReactor(gas1) net.add_reactor(r1) net.atol_sensitivity = 1e-10 net.rtol_sensitivity = 1e-8 gas2 = ct.Solution('h2o2.xml') gas2.TPX = 900, 101325, 'H2:0.1, OH:1e-7, O2:0.1, AR:1e-5' r2 = ct.IdealGasReactor(gas2) net.add_reactor(r2) w = ct.Wall(r1, r2) w.area = 1.5 w.left.kinetics = interface C = np.zeros(interface.n_species) C[0] = 0.3 C[4] = 0.7 w.left.coverages = C w.left.add_sensitivity_reaction(2) r2.add_sensitivity_reaction(18) for T in (901, 905, 910, 950, 1500): while r2.T < T: net.step(1.0) S = net.sensitivities() # number of non-species variables in each reactor Ns = r1.component_index(gas1.species_name(0)) # Index of first variable corresponding to r2 K2 = Ns + gas1.n_species + interface.n_species # Constant volume should generate zero sensitivity coefficient self.assertArrayNear(S[1,:], np.zeros(2)) self.assertArrayNear(S[K2+1,:], np.zeros(2)) # Sensitivity coefficients for the disjoint reactors should be zero self.assertNear(np.linalg.norm(S[Ns:K2,1]), 0.0, atol=1e-5) self.assertNear(np.linalg.norm(S[K2+Ns:,0]), 0.0, atol=1e-5)
def make_reactors(self): self.net = ct.ReactorNet() self.gas = ct.Solution('diamond.xml', 'gas') self.solid = ct.Solution('diamond.xml', 'diamond') self.interface = ct.Interface('diamond.xml', 'diamond_100', (self.gas, self.solid)) self.r1 = ct.IdealGasReactor(self.gas) self.r1.volume = 0.01 self.net.add_reactor(self.r1) self.r2 = ct.IdealGasReactor(self.gas) self.r2.volume = 0.01 self.net.add_reactor(self.r2) self.w = ct.Wall(self.r1, self.r2) self.w.area = 1.0
def test_sensitivities2(self): net = ct.ReactorNet() gas1 = ct.Solution('diamond.xml', 'gas') solid = ct.Solution('diamond.xml', 'diamond') interface = ct.Interface('diamond.xml', 'diamond_100', (gas1, solid)) r1 = ct.Reactor(gas1) net.addReactor(r1) gas2 = ct.Solution('h2o2.xml') gas2.TPX = 900, 101325, 'H2:0.1, OH:1e-7, O2:0.1, AR:1e-5' r2 = ct.Reactor(gas2) net.addReactor(r2) w = ct.Wall(r1, r2) w.left.kinetics = interface C = np.zeros(interface.nSpecies) C[0] = 0.3 C[4] = 0.7 w.left.coverages = C w.left.addSensitivityReaction(2) r2.addSensitivityReaction(18) for T in (901, 905, 910, 950, 1500): while r2.T < T: net.step(1.0) S = net.sensitivities() K1 = gas1.nSpecies + interface.nSpecies # Constant internal energy and volume should generate zero # sensitivity coefficients self.assertArrayNear(S[0:2,:], np.zeros((2,2))) self.assertArrayNear(S[K1+2:K1+4,:], np.zeros((2,2))) S11 = np.linalg.norm(S[2:K1+2,0]) S21 = np.linalg.norm(S[2:K1+2,1]) S12 = np.linalg.norm(S[K1+4:,0]) S22 = np.linalg.norm(S[K1+4:,1]) self.assertTrue(S11 > 1e5 * S12) self.assertTrue(S22 > 1e5 * S21)
def __init__(self, idx, properties, model, path=''): """Initialize simulation case. :param idx: identifier number for this case :type idx: int :param properties: set of properties for this case :type properties: dict :param str model_file: Filename for Cantera-format model :param str path: Path for data file """ self.idx = idx self.properties = properties self.gas = model self.time_end = 10 #This is just a filler idk how end time should actually be determined yet self.gas.TP = (self.properties['temperature'], self.properties['pressure'] * float(ct.one_atm)) self.gas.set_equivalence_ratio(self.properties['equivalence_ratio'], self.properties['fuel'], self.properties['oxidizer']) # Create non-interacting ``Reservoir`` on other side of ``Wall`` env = ct.Reservoir(ct.Solution('air.xml')) # All reactors are ``IdealGasReactor`` objects self.reac = ct.IdealGasReactor(self.gas) self.wall = ct.Wall(self.reac, env, A=1.0, velocity=0) # Create ``ReactorNet`` newtork self.reac_net = ct.ReactorNet([self.reac]) # Set file for later data file file_path = os.path.join(path, str(self.idx) + '.h5') self.save_file = file_path self.sample_points = [] self.ignition_delay = 0.0
def test_ConstPressureReactor(self): phase = ct.Nitrogen() air = ct.Solution('air.xml') phase.TP = 75, 4e5 r1 = ct.ConstPressureReactor(phase) r1.volume = 0.1 air.TP = 500, 4e5 env = ct.Reservoir(air) w2 = ct.Wall(env,r1, Q=250000, A=1) net = ct.ReactorNet([r1]) states = ct.SolutionArray(phase, extra='t') for t in np.arange(0.0, 100.0, 10): net.advance(t) states.append(TD=r1.thermo.TD, t=t) self.assertEqual(states.X[1], 0) self.assertEqual(states.X[-2], 1) for i in range(3,7): self.assertNear(states.T[i], states.T[2])
gas2.TPX = 900.0, ct.one_atm, 'CO:2, H2O:0.01, O2:5' r1 = ct.IdealGasReactor(gas1) r1.volume = 0.5 r2 = ct.IdealGasReactor(gas2) r2.volume = 0.1 # The wall is held fixed until t = 0.1 s, then released to allow the pressure to # equilibrate. def v(t): if t < 0.1: return 0.0 else: return (r1.thermo.P - r2.thermo.P) * 1e-4 w = ct.Wall(r1, r2, velocity=v) net = ct.ReactorNet([r1, r2]) states1 = ct.SolutionArray(r1.thermo, extra=['t','v']) states2 = ct.SolutionArray(r2.thermo, extra=['t','v']) for n in range(200): time = (n+1)*0.001 net.advance(time) if n % 4 == 3: print(fmt % (time, r1.T, r2.T, r1.volume, r2.volume, r1.volume + r2.volume, r2.thermo['CO'].X[0])) states1.append(r1.thermo.state, t=1000*time, v=r1.volume) states2.append(r2.thermo.state, t=1000*time, v=r2.volume)
return 2.0 * np.pi * rps * t + ca_start * np.pi / 180.0 # set up IC engine parameters stroke *= 0.001 bore *= 0.001 conrod *= 0.001 area = 0.25 * np.pi * bore * bore vol_h = stroke * area # volume cylinder vol_c = vol_h / (cratio - 1.0) # volume combustion dome ca = crank_angle(0.0) # initial CA r_ca = stroke * 0.5 # crank radius vol_ini= (r_ca + conrod - (r_ca * np.cos(ca) + np.sqrt(conrod**2 - r_ca**2 * np.sin(ca)**2))) * area + vol_c r.volume = vol_ini # initial volume # set up piston piston = ct.Wall(outer, r) piston.area = area # piston area def piston_speed(t): ca = crank_angle(t) return -2.0 * np.pi * rps * (r_ca * np.sin(ca) + r_ca**2 * np.sin(2.0 * ca) / 2.0 / np.sqrt(conrod**2 - r_ca**2 * np.sin(ca)**2)) piston.set_velocity(piston_speed) # piston speed # set up time t_sim = (ca_end - ca_start) / rps / 360.0 # simulation time t_step = ca_step / rps / 360.0 # simulation time step t_out = ca_out / rps / 360.0 # simulation output time ttt = 0.0 # set up output data arrays states = ct.SolutionArray(r.thermo) t = []
def addWall(self, **kwargs): self.w = ct.Wall(self.r1, self.r2, **kwargs) return self.w
fmt = '%10.3f %10.1f %10.4f %10.4g %10.4g %10.4g %10.4g' print('%10s %10s %10s %10s %10s %10s %10s' % ('time [s]', 'T1 [K]', 'T2 [K]', 'V1 [m^3]', 'V2 [m^3]', 'V1+V2 [m^3]', 'X(CO)')) gas1 = ct.Solution('h2o2.cti') gas1.TPX = 900.0, ct.one_atm, 'H2:2, O2:1, AR:20' gas2 = ct.Solution('gri30.xml') gas2.TPX = 900.0, ct.one_atm, 'CO:2, H2O:0.01, O2:5' r1 = ct.Reactor(gas1) r1.volume = 0.5 r2 = ct.Reactor(gas2) r2.volume = 0.1 w = ct.Wall(r1, r2, K=1.0e3) net = ct.ReactorNet([r1, r2]) tim = [] t1 = [] t2 = [] v1 = [] v2 = [] v = [] xco = [] xh2 = [] for n in range(30): time = (n + 1) * 0.002 net.advance(time)
cstr = ct.IdealGasReactor(gas) # Set its volume to 10 cm^3. In this problem, the reactor volume is fixed, so # the initial volume is the volume at all later times. cstr.volume = 10.0 * 1.0e-6 # We need to have heat loss to see the oscillations. Create a reservoir to # represent the environment, and initialize its temperature to the reactor # temperature. env = ct.Reservoir(gas) # Create a heat-conducting wall between the reactor and the environment. Set its # area, and its overall heat transfer coefficient. Larger U causes the reactor # to be closer to isothermal. If U is too small, the gas ignites, and the # temperature spikes and stays high. w = ct.Wall(cstr, env, A=1.0, U=0.02) # Connect the upstream reservoir to the reactor with a mass flow controller # (constant mdot). Set the mass flow rate to 1.25 sccm. sccm = 1.25 vdot = sccm * 1.0e-6 / 60.0 * ( (ct.one_atm / gas.P) * (gas.T / 273.15)) # m^3/s mdot = gas.density * vdot # kg/s mfc = ct.MassFlowController(upstream, cstr, mdot=mdot) # now create a downstream reservoir to exhaust into. downstream = ct.Reservoir(gas) # connect the reactor to the downstream reservoir with a valve, and set the # coefficient sufficiently large to keep the reactor pressure close to the # downstream pressure of 60 Torr.
def setup_case(self): """ Sets up the case to be run. Initializes the ``ThermoPhase``, ``Reactor``, and ``ReactorNet`` according to the values from the input file. """ self.gas = ct.Solution(self.mech_filename) initial_temp = self.keywords['temperature'] # The initial pressure in Cantera is expected in Pa; in SENKIN # it is expected in atm, so convert initial_pres = self.keywords['pressure'] * ct.one_atm # If the equivalence ratio has been specified, send the # keywords for conversion. if 'eqRatio' in self.keywords: reactants = utils.equivalence_ratio( self.gas, self.keywords['eqRatio'], self.keywords['fuel'], self.keywords['oxidizer'], self.keywords['completeProducts'], self.keywords['additionalSpecies'], ) else: # The reactants are stored in the ``keywords`` dictionary # as a list of strings, so they need to be joined. reactants = ','.join(self.keywords['reactants']) self.gas.TPX = initial_temp, initial_pres, reactants # Create a non-interacting ``Reservoir`` to be on the other # side of the ``Wall``. env = ct.Reservoir(ct.Solution('air.xml')) # Set the ``temp_func`` to ``None`` as default; it will be set # later if needed. self.temp_func = None # All of the reactors are ``IdealGas`` Reactors. Set a ``Wall`` # for every case so that later code can be more generic. If the # velocity is set to zero, the ``Wall`` won't affect anything. # We have to set the ``n_vars`` here because until the first # time step, ``ReactorNet.n_vars`` is zero, but we need the # ``n_vars`` before the first time step. if self.keywords['problemType'] == 1: self.reac = ct.IdealGasReactor(self.gas) # Number of solution variables is number of species + mass, # volume, temperature self.n_vars = self.reac.kinetics.n_species + 3 self.wall = ct.Wall(self.reac, env, A=1.0, velocity=0) elif self.keywords['problemType'] == 2: self.reac = ct.IdealGasConstPressureReactor(self.gas) # Number of solution variables is number of species + mass, # temperature self.n_vars = self.reac.kinetics.n_species + 2 self.wall = ct.Wall(self.reac, env, A=1.0, velocity=0) elif self.keywords['problemType'] == 3: self.reac = ct.IdealGasReactor(self.gas) # Number of solution variables is number of species + mass, # volume, temperature self.n_vars = self.reac.kinetics.n_species + 3 self.wall = ct.Wall(self.reac, env, A=1.0, velocity=VolumeProfile(self.keywords)) elif self.keywords['problemType'] == 4: self.reac = ct.IdealGasConstPressureReactor(self.gas, energy='off') # Number of solution variables is number of species + mass, # temperature self.n_vars = self.reac.kinetics.n_species + 2 self.wall = ct.Wall(self.reac, env, A=1.0, velocity=0) elif self.keywords['problemType'] == 5: self.reac = ct.IdealGasReactor(self.gas, energy='off') # Number of solution variables is number of species + mass, # volume, temperature self.n_vars = self.reac.kinetics.n_species + 3 self.wall = ct.Wall(self.reac, env, A=1.0, velocity=0) elif self.keywords['problemType'] == 6: from user_routines import VolumeFunctionTime self.reac = ct.IdealGasReactor(self.gas) # Number of solution variables is number of species + mass, # volume, temperature self.n_vars = self.reac.kinetics.n_species + 3 self.wall = ct.Wall(self.reac, env, A=1.0, velocity=VolumeFunctionTime()) elif self.keywords['problemType'] == 7: from user_routines import TemperatureFunctionTime self.reac = ct.IdealGasConstPressureReactor(self.gas, energy='off') # Number of solution variables is number of species + mass, # temperature self.n_vars = self.reac.kinetics.n_species + 2 self.wall = ct.Wall(self.reac, env, A=1.0, velocity=0) self.temp_func = ct.Func1(TemperatureFunctionTime()) elif self.keywords['problemType'] == 8: self.reac = ct.IdealGasConstPressureReactor(self.gas, energy='off') # Number of solution variables is number of species + mass, # temperature self.n_vars = self.reac.kinetics.n_species + 2 self.wall = ct.Wall(self.reac, env, A=1.0, velocity=0) self.temp_func = ct.Func1(TemperatureProfile(self.keywords)) elif self.keywords['problemType'] == 9: self.reac = ct.IdealGasReactor(self.gas) # Number of solution variables is number of species + mass, # volume, temperature self.n_vars = self.reac.kinetics.n_species + 3 self.wall = ct.Wall(env, self.reac, A=1.0, velocity=ICEngineProfile(self.keywords)) if 'reactorVolume' in self.keywords: self.reac.volume = self.keywords['reactorVolume'] # Create the Reactor Network. self.netw = ct.ReactorNet([self.reac]) if 'sensitivity' in self.keywords: self.sensitivity = True # There is no automatic way to calculate the sensitivity of # all of the reactions, so do it manually. for i in range(self.reac.kinetics.n_reactions): self.reac.add_sensitivity_reaction(i) # If no tolerances for the sensitivity are specified, set # to the SENKIN defaults. if 'sensAbsTol' in self.keywords: self.netw.atol_sensitivity = self.keywords['sensAbsTol'] else: self.netw.atol_sensitivity = 1.0E-06 if 'sensRelTol' in self.keywords: self.netw.rtol_sensitivity = self.keywords['sensRelTol'] else: self.netw.rtol_sensitivity = 1.0E-04 else: self.sensitivity = False # If no solution tolerances are specified, set to the default # SENKIN values. if 'abstol' in self.keywords: self.netw.atol = self.keywords['abstol'] else: self.netw.atol = 1.0E-20 if 'reltol' in self.keywords: self.netw.rtol = self.keywords['reltol'] else: self.netw.rtol = 1.0E-08 if 'tempLimit' in self.keywords: self.temp_limit = self.keywords['tempLimit'] else: # tempThresh is set in the parser even if it is not present # in the input file self.temp_limit = (self.keywords['tempThresh'] + self.keywords['temperature']) self.tend = self.keywords['endTime'] # Set the maximum time step the solver can take. If a value is # not specified in the input file, default to 0.001*self.tend. # Set the time steps for saving to the binary file and writing # to the screen. If the time step for printing to the screen is # not set, default to printing 100 times. print_time_int = self.keywords.get('prntTimeInt') save_time_int = self.keywords.get('saveTimeInt') max_time_int = self.keywords.get('maxTimeStep') time_ints = [ value for value in [print_time_int, save_time_int, max_time_int] if value is not None ] if time_ints: self.netw.set_max_time_step(min(time_ints)) else: self.netw.set_max_time_step(self.tend / 100) if print_time_int is not None: self.print_time_step = print_time_int else: self.print_time_step = self.tend / 100 self.print_time = self.print_time_step self.save_time_step = save_time_int if self.save_time_step is not None: self.save_time = self.save_time_step # Store the species names in a slightly shorter variable name self.species_names = self.reac.thermo.species_names # Initialize the ignition time, in case the end time is reached # before ignition occurs self.ignition_time = None
gas.TPX = 1000, 1*ct.one_atm, 'CH4:1, O2:2, N2:7.52' # reactor to represent the cylinder filled with gas r = ct.IdealGasReactor(gas) r.volume = 1.0 sim = ct.ReactorNet([r]) time = 0.0 # initial time states = ct.SolutionArray(gas, extra=['t']) # implementation of heater that gives constant heat heater_gas = ct.Solution('gri30.xml') heater_gas.TPX = 1000, ct.one_atm, 'H2:2,O2:1' heater = ct.Reservoir(heater_gas) w = ct.Wall(heater, r, A=1.0, K=0, U=0, Q=1000000000, velocity=0) time_step = 1.e-6 print('%10s %10s %10s %14s' % ('t [s]','T [K]','P [Pa]','u [J/kg]')) for n in range(100): time += time_step sim.advance(time) states.append(r.thermo.state, t=time*1e3) plt.clf() plt.subplot(2, 2, 1) plt.plot(states.t, states.T) plt.xlabel('Time (ms)') plt.ylabel('Temperature (K)')
def run(self, initialTime: float = -1.0, finalTime: float = -1.0): ''' Run the shock tube simulation ''' #p_alpha = meq.Master_Equation.chebyshev_specific_poly(self,l,meq.Master_Equation.calc_reduced_P(self,target_press_new*101325)) if self.exact_deriv_flag == False: inp_time = self.volume_trace_class.time inp_vol = self.volume_trace_class.volume if self.exact_deriv_flag == True: inp_time = self.volume_trace_class_exact_derivitive.time inp_vol = self.volume_trace_class_exact_derivitive.volume if initialTime == -1.0: initialTime = self.initialTime if finalTime == -1.0: finalTime = self.finalTime self.timeHistory = None self.kineticSensitivities = None #3D numpy array, columns are reactions with timehistories, depth gives the observable for those histories conditions = self.settingRCMConditions() mechanicalBoundary = conditions[1] #same solution for both cp and cv sims if mechanicalBoundary == 'constant pressure': print('RCM is not a constant pressure simulation') else: if self.exact_deriv_flag == False: RCM = ct.IdealGasReactor(self.processor.solution) env = ct.Reservoir(ct.Solution('air.xml')) wall = ct.Wall(RCM, env, A=1.0, velocity=self.volume_trace_class) sim = ct.ReactorNet([RCM]) sim.set_max_time_step(inp_time[1]) if self.exact_deriv_flag == True: RCM = ct.IdealGasReactor(self.processor.solution) env = ct.Reservoir(ct.Solution('air.xml')) wall = ct.Wall( RCM, env, A=1.0, velocity=self.volume_trace_class_exact_derivitive) sim = ct.ReactorNet([RCM]) sim.set_max_time_step(inp_time[1]) sim.rtol = self.rtol sim.atol = self.atol sim.rtol_sensitivity = self.rtol_sensitivity sim.atol_sensitivity = self.atol_sensitivity columnNames = [RCM.component_name(item) for item in range(RCM.n_vars)] columnNames = ['time'] + ['pressure'] + columnNames self.timeHistory = pd.DataFrame(columns=columnNames) if self.kineticSens == 1: for i in range(self.processor.solution.n_reactions): RCM.add_sensitivity_reaction(i) dfs = [pd.DataFrame() for x in range(len(self.observables))] tempArray = [ np.zeros(self.processor.solution.n_reactions) for x in range(len(self.observables)) ] t = self.initialTime counter = 0 #print(sim.rtol_sensitivity,sim.atol_sensitivity) vol_sol = ct.SolutionArray(self.processor.solution, extra=["time", "volume"]) self.vol_sol = vol_sol #while t < self.finalTime and RCM.T < 2500: #should we have this temperature limiting factor? #while t < self.finalTime and RCM.T<2500: while t < self.finalTime: t = sim.step() #print(t) if mechanicalBoundary == 'constant volume': state = np.hstack([ t, RCM.thermo.P, RCM.mass, RCM.volume, RCM.T, RCM.thermo.X ]) #vol_sol.append(time=sim.time, T=RCM.T, P=RCM.thermo.P, X=RCM.thermo.X, volume=RCM.volume) #t = sim.step() else: print('RCM has variable volume') self.timeHistory.loc[counter] = state if self.kineticSens == 1: counter_1 = 0 for observable, reaction in itertools.product( self.observables, range(self.processor.solution.n_reactions)): tempArray[self.observables.index( observable)][reaction] = sim.sensitivity( observable, reaction) counter_1 += 1 if counter_1 % self.processor.solution.n_reactions == 0: dfs[self.observables.index(observable)] = dfs[ self.observables.index(observable)].append( ((pd.DataFrame( tempArray[self.observables.index( observable)])).transpose()), ignore_index=True) counter += 1 #temp_v = pd.DataFrame() #temp_v['velocity'] = np.array(self.volume_trace_class.velocity_list_temp) #temp_v['time']= self.timeHistory['time'] #temp_v.to_csv('/Users/carlylagrotta/Dropbox/Columbia/MSI/data/DME-Methanol_Blends_RCM/time_history_velocity_test/velocity_unrefined.csv',index=False) if self.timeHistories != None: self.timeHistory.time = self.timeHistory.time + self.time_shift_value #self.timeHistory.time = self.timeHistory.time + 0 self.timeHistories.append(self.timeHistory) #print(self.timeHistory) ############################################################ if self.kineticSens == 1: numpyMatrixsksens = [ dfs[dataframe].values for dataframe in range(len(dfs)) ] self.kineticSensitivities = np.dstack(numpyMatrixsksens) return self.timeHistory, self.kineticSensitivities else: import matplotlib.pyplot as plt #plt.figure() #plt.plot(self.timeHistory['time'],self.timeHistory['pressure']/100000) return self.timeHistory
gas.TPX = Tin, p, comp mdot = vin * area * gas.density dx = length / n_reactor #r = ct.IdealGasReactor(gas) r = ct.Reactor(gas) # since water is not ideal gas r.volume = area * dx upstream = ct.Reservoir(gas, name='upstream') downstream = ct.Reservoir(gas, name='downstream') m = ct.MassFlowController(upstream, r, mdot=mdot) v = ct.PressureController(r, downstream, master=m, K=1.0e-5) gas.TPX = Tout, p, comp outer = ct.Reservoir(gas) wall = ct.Wall(outer, r, U=ht) wall.area = area_wall / n_reactor sim = ct.ReactorNet([r]) # solve outfile = open('wall_heat_transfer.csv', 'w', newline='') writer = csv.writer(outfile) writer.writerow(['Distance (m)', 'u(m/s)', 'rtime(s)', 'T(K)', 'P(Pa)'] + gas.species_names) t_res = 0.0 for n in range(n_reactor): gas.TDY = r.thermo.TDY upstream.syncState() sim.reinitialize()
import cantera as ct gri3 = ct.Solution('gri30.xml') temp = 1500.0 pres = ct.one_atm gri3.TPX = temp, pres, 'CH4:0.1, O2:2, N2:7.52' r = ct.Reactor(gri3) air = ct.Solution('air.xml') air.TP = temp, pres env = ct.Reservoir(air) # Define a wall between the reactor and the environment, and make it flexible, # so that the pressure in the reactor is held at the environment pressure. w = ct.Wall(r, env) w.expansion_rate_coeff = 1.0e6 # set expansion parameter. dV/dt = KA(P_1 - P_2) w.area = 1.0 sim = ct.ReactorNet([r]) # enable sensitivity with respect to the rates of the first 10 # reactions (reactions 0 through 9) for i in range(10): r.add_sensitivity_reaction(i) # set the tolerances for the solution and for the sensitivity coefficients sim.rtol = 1.0e-6 sim.atol = 1.0e-15 sim.rtol_sensitivity = 1.0e-6 sim.atol_sensitivity = 1.0e-6
def before_component_name(self, i): # Other components are handled by the method from the base Reactor class if i == self.i_wall: return 'v_wall' gas = ct.Solution('h2o2.yaml') # Initial condition P = ct.one_atm gas.TPY = 920, P, 'H2:1.0, O2:1.0, N2:3.76' # Set up the reactor network res = ct.Reservoir(gas) r = InertialWallReactor(gas, neighbor=res) w = ct.Wall(r, res) net = ct.ReactorNet([r]) # Integrate the equations, keeping T(t) and Y(k,t) states = ct.SolutionArray(gas, 1, extra={'t': [0.0], 'V': [r.volume]}) while net.time < 0.5: net.advance(net.time + 0.005) states.append(TPY=r.thermo.TPY, V=r.volume, t=net.time) # Plot the results try: import matplotlib.pyplot as plt L1 = plt.plot(states.t, states.T, color='r', label='T', lw=2) plt.xlabel('time (s)') plt.ylabel('Temperature (K)') plt.twinx()
# To effect the volume change, instances of `Wall`s can be installed between two Cantera reactors. # ============================================================================= # In this case, we only care about reactions and changes in the reactor representing the RCM reaction chamber, # so we install a `Wall` between the `IdealGasReactor` and a `Reservoir`, which represents the environment. # Then we specify the `VolumeProfile` class as the means to calculate the velocity. # Other options would be a constant value, or a function that takes a single argument (the simulation time). # With the velocity defined and the `Wall` installed, the simulation can proceed as before. # ============================================================================= # In[13]: reac = ct.IdealGasReactor(gas) env = ct.Reservoir(ct.Solution('air.xml')) wall = ct.Wall(reac, env, A=1.0, velocity=vpro) netw = ct.ReactorNet([reac]) netw.set_max_time_step(inp_time[1]) vol_sol = ct.SolutionArray(gas, extra=["time", "volume"]) # End the simulation when the temperature exceeds 2500 K or the time reaches 0.1 s while reac.T < 2500 and netw.time < 0.1: vol_sol.append(time=netw.time, T=reac.T, P=reac.thermo.P, X=reac.thermo.X, volume=reac.volume) netw.step() # We can compare a number of parameters between the simulation and experiment. # ============================================================================= # First, we can compare the volume that is read from the input file to the simulated volume of the reactor as a function of time. # Remember that we did not specify the volume of the reactor directly, # instead we specified it through the velocity.
def execute(): #============================================================================== # set up the cantera gases and reactors #============================================================================== drop = droplet(50e-6, 'A2', 300, 1) #drop is defined only to use the drop properties #environment air gas_env = ct.Solution('a2.cti') gas_env.TPX = 300, ct.one_atm, 'O2:29,N2:71' gas_fuel = ct.Solution('a2.cti') gas_fuel.TPX = drop.ABP, ct.one_atm, 'POSF10325:1' #gaseous fuel at average boiling temperature gas_kernel = ct.Solution('a2.cti') gas_kernel.TPX = 4000, ct.one_atm, 'O2:29,N2:71' gas_kernel.equilibrate('HP') res_air = ct.Reservoir(gas_env) res_fuel = ct.Reservoir(gas_fuel) kernel = ct.IdealGasConstPressureReactor(gas_kernel) kernel.volume = 2.4e-8 #m^3 mfc1 = ct.MassFlowController(res_air, kernel) #connect air reservoir to kernel mfc2 = ct.MassFlowController(res_fuel, kernel) #connect fuel reservoir to kernel mfc1.set_mass_flow_rate(6e-5) mfc2.set_mass_flow_rate(0) w = ct.Wall(kernel, res_fuel) w.set_heat_flux(0) w.expansion_rate_coeff = 1e8 net = ct.ReactorNet({kernel}) net.atol = 1e-10 #absolute tolerance net.rtol = 1e-10 #relative tolerance #============================================================================== # endtime, time steps, and data to be stored #============================================================================== dt = 1e-6 #change time step for advancing the reactor endtime = 1000e-6 mdot_fuel = 1.98e-6 #this number should be calculated based on equivalence ratio, kg/s droplets = [] T = [] time = [] m = [] m_dot = [] q_dot = [] droplet_count = [] X_fuel = [] X_O = [] X_CO2 = [] num_d = [] #============================================================================== # advance reaction #============================================================================== print("Running simulation") for i in range(1,int(endtime/dt)): print (int(endtime/dt) - i) #entrain droplets every 1 microsecond droplets.extend(entrainer(400, 5.*mdot_fuel)) mdot, qdot = vaporize(gas_kernel.T, droplets, dt) # print(gas_kernel.T, mdot, qdot, len(droplets)) # print(str(mdot) + str(qdot) + str(gas_kernel.T)+' '+str(len(droplets))) mfc2.set_mass_flow_rate(mdot) w.set_heat_flux(qdot) #heat required to heat up the droplets during that time step net.advance(i*dt) num_d.append(len(droplets)) #storing variables T.append(gas_kernel.T) time.append(net.time) m.append(kernel.mass) m_dot.append(mdot) q_dot.append(qdot) droplet_count.append(len(droplets)) if gas_kernel.X[0] < 0: gas_kernel.X[0] = 0 X_fuel.append(gas_kernel.X[gas_kernel.species_index("POSF10325")]) X_O.append(gas_kernel.X[gas_kernel.species_index("O")]) X_CO2.append(gas_kernel.X[gas_kernel.species_index("CO2")]) #============================================================================== # plotting important information #============================================================================== plt.plot([t*1e3 for t in time],T, linewidth=3) plt.xlabel('time(ms)', FontSize=15) plt.ylabel('Temperature (K)', FontSize=15) plt.show() fig, ax1 = plt.subplots() ax2 = ax1.twinx() ax1.plot([t*1e3 for t in time], X_fuel, color = 'red', linewidth = 3) ax1.set_xlabel('time (ms)', FontSize=15) ax1.set_ylabel('X_fuel', color='red', FontSize=15) # ax2.plot([t*1e3 for t in time], X_O, color = 'green', linewidth = 3) ax2.set_ylabel('X_CO2', color='green', FontSize=15) ax2.plot([t*1e3 for t in time], X_CO2, color = 'blue', linewidth = 3) plt.show() fig, ax1 = plt.subplots() ax2 = ax1.twinx() ax1.plot([t*1e3 for t in time], m_dot, color = 'red', linewidth = 3) ax1.set_xlabel('time (ms)', FontSize=15) ax1.set_ylabel('m_dot', color='red', FontSize=15) ax2.plot([t*1e3 for t in time], q_dot, color = 'green', linewidth = 3) ax2.set_ylabel('q_dot', color='green', FontSize=15) plt.show() fig,ax1 = plt.subplots() ax1.plot([t*1e3 for t in time], num_d, linewidth=3) ax1.set_xlabel('time(ms)', FontSize=15) ax1.set_ylabel('#droplets', FontSize=15) plt.show()
def setup_case(self, model_file, species_key, path=''): """Sets up the simulation case to be run. :param str model_file: Filename for Cantera-format model :param dict species_key: Dictionary with species names for `model_file` :param str path: Path for data file """ self.gas = ct.Solution(model_file) # Convert ignition delay to seconds self.properties.ignition_delay.ito('second') # Set end time of simulation to 100 times the experimental ignition delay self.time_end = 100. * self.properties.ignition_delay.magnitude # Initial temperature needed in Kelvin for Cantera self.properties.temperature.ito('kelvin') # Initial pressure needed in Pa for Cantera self.properties.pressure.ito('pascal') # convert reactant names to those needed for model reactants = [species_key[spec['species-name']] + ':' + str(spec['amount'].magnitude) for spec in self.properties.composition ] reactants = ','.join(reactants) # Reactants given in format for Cantera if self.properties.composition_type in ['mole fraction', 'mole percent']: self.gas.TPX = (self.properties.temperature.magnitude, self.properties.pressure.magnitude, reactants ) elif self.properties.composition_type == 'mass fraction': self.gas.TPY = (self.properties.temperature.magnitude, self.properties.pressure.magnitude, reactants ) else: raise(BaseException('error: not supported')) return # Create non-interacting ``Reservoir`` on other side of ``Wall`` env = ct.Reservoir(ct.Solution('air.xml')) # All reactors are ``IdealGasReactor`` objects self.reac = ct.IdealGasReactor(self.gas) if self.apparatus == 'shock tube' and self.properties.pressure_rise is None: # Shock tube modeled by constant UV self.wall = ct.Wall(self.reac, env, A=1.0, velocity=0) elif self.apparatus == 'shock tube' and self.properties.pressure_rise is not None: # Shock tube modeled by constant UV with isentropic compression # Need to convert pressure rise units to seconds self.properties.pressure_rise.ito('1 / second') self.wall = ct.Wall(self.reac, env, A=1.0, velocity=PressureRiseProfile( model_file, self.gas.T, self.gas.P, self.gas.X, self.properties.pressure_rise.magnitude, self.time_end ) ) elif (self.apparatus == 'rapid compression machine' and self.properties.volume_history is None ): # Rapid compression machine modeled by constant UV self.wall = ct.Wall(self.reac, env, A=1.0, velocity=0) elif (self.apparatus == 'rapid compression machine' and self.properties.volume_history is not None ): # Rapid compression machine modeled with volume-time history # First convert time units if necessary self.properties.volume_history.time.ito('second') self.wall = ct.Wall(self.reac, env, A=1.0, velocity=VolumeProfile(self.properties.volume_history) ) # Number of solution variables is number of species + mass, # volume, temperature self.n_vars = self.reac.kinetics.n_species + 3 # Create ``ReactorNet`` newtork self.reac_net = ct.ReactorNet([self.reac]) # Set maximum time step based on volume-time history, if present if self.properties.volume_history is not None: # Minimum difference between volume profile times min_time = numpy.min(numpy.diff(self.properties.volume_history.time.magnitude)) self.reac_net.set_max_time_step(min_time) # Check if species ignition target, that species is present. if self.properties.ignition_type['target'] not in ['pressure', 'temperature']: # Other targets are species spec = self.properties.ignition_type['target'] # Try finding species in upper- and lower-case try_list = [spec, spec.lower()] # If excited radical, may need to fall back to nonexcited species if spec[-1] == '*': try_list += [spec[:-1], spec[:-1].lower()] ind = None for sp in try_list: try: ind = self.gas.species_index(sp) break except ValueError: pass if ind: self.properties.ignition_target = ind self.properties.ignition_type = self.properties.ignition_type['type'] else: print('Warning: ' + spec + ' not found in model; ' 'falling back on pressure.' ) self.properties.ignition_target = 'pressure' self.properties.ignition_type = 'd/dt max' else: self.properties.ignition_target = self.properties.ignition_type['target'] self.properties.ignition_type = self.properties.ignition_type['type'] # Set file for later data file file_path = os.path.join(path, self.meta['id'] + '.h5') self.meta['save-file'] = file_path
# use GRI-Mech 3.0 for the methane/air mixture, and set its initial state gas = ct.Solution('gri30.xml') gas.TP = 500.0, 0.2 * ct.one_atm gas.set_equivalence_ratio(1.1, 'CH4:1.0', 'O2:2, N2:7.52') # create a reactor for the methane/air side r2 = ct.IdealGasReactor(gas) #----------------------------------------------------------------------------- # Now couple the reactors by defining common walls that may move (a piston) or # conduct heat #----------------------------------------------------------------------------- # add a flexible wall (a piston) between r2 and r1 w = ct.Wall(r2, r1, A=1.0, K=0.5e-4, U=100.0) # heat loss to the environment. Heat loss always occur through walls, so we # create a wall separating r1 from the environment, give it a non-zero area, # and specify the overall heat transfer coefficient through the wall. w2 = ct.Wall(r2, env, A=1.0, U=500.0) sim = ct.ReactorNet([r1, r2]) # Now the problem is set up, and we're ready to solve it. print('finished setup, begin solution...') time = 0.0 n_steps = 300 outfile = open('piston.csv', 'w') csvfile = csv.writer(outfile)
inlet = ct.Reservoir(gas) # define injector state (gaseous!) gas.TPX = T_injector, p_injector, comp_injector injector = ct.Reservoir(gas) # define outlet pressure (temperature and composition don't matter) gas.TPX = T_ambient, p_outlet, comp_ambient outlet = ct.Reservoir(gas) # define ambient pressure (temperature and composition don't matter) gas.TPX = T_ambient, p_ambient, comp_ambient ambient_air = ct.Reservoir(gas) # set up connecting devices inlet_valve = ct.Valve(inlet, r) injector_mfc = ct.MassFlowController(injector, r) outlet_valve = ct.Valve(r, outlet) piston = ct.Wall(ambient_air, r) # convert time to crank angle def crank_angle(t): return np.remainder(2 * np.pi * f * t, 4 * np.pi) # set up IC engine parameters V_oT = V_H / (epsilon - 1.) A_piston = .25 * np.pi * d_piston ** 2 stroke = V_H / A_piston r.volume = V_oT piston.area = A_piston def piston_speed(t): return - stroke / 2 * 2 * np.pi * f * np.sin(crank_angle(t)) piston.set_velocity(piston_speed)