.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "examples/surface_chemistry/trans_PSR_ALD.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_examples_surface_chemistry_trans_PSR_ALD.py: .. _ref_trans_PSR_ignition: =========================================================== Modeling atomic layer deposition process in a transient PSR =========================================================== Atomic layer deposition (ALD) is a technique used to deposit thin films of solid materials in a very controlled manner, and differs from CVD primarily in that it is a transient process with the deposition surface being exposed to pulses of alternating gases. Ideally, the deposition chemistry in ALD is self-limiting, with growth occurring in a layer-by-layer manner and the deposition thickness being controlled only by the number of cycles. The major advantage of ALD over CVD is the improved control over the deposition process and more conformal deposition. The inherently lower deposition rates, however, lead to longer process times and higher costs. During a pulse, it is important that enough molecules react with all parts of the substrate to be coated. But many of the precursor materials are expensive. Thus one of the process optimization goals is to reduce the amount of precursor that flows through the reactor without reacting at the surface. Normally the perfectly stirred reactor (PSR) model is a 0-D steady-state model with constant reactor pressure. When the dynamic responses of a chemistry dominated process are of interest, performing transient simulations of the system becomes necessary. The transient PSR (transPSR) model of the Ansys Chemkin is a useful tool for the ALD process development. It can be applied for optimizing the pulse sequences and the cycle times of the ALD process to obtain the desired pattern and thickness of the deposit layers. It can also be used to develop and to validate the chemical reaction mechanism for the ALD process. This project demonstrates the process of setting up and running the transPSR model with multiple inlets of different flow rate profiles. The alumina ALD chemistry used in this example is described in section 5.4.4 (Alumina ALD) of the Chemkin Tutorial manual. .. GENERATED FROM PYTHON SOURCE LINES 58-60 .. code-block:: Python :dedent: 1 .. GENERATED FROM PYTHON SOURCE LINES 62-64 Import PyChemkin packages and start the logger ============================================== .. GENERATED FROM PYTHON SOURCE LINES 64-91 .. code-block:: Python from pathlib import Path import time import ansys.chemkin.core as ck # Chemkin from ansys.chemkin.core import Color from ansys.chemkin.core.inlet import Stream # external gaseous inlet from ansys.chemkin.core.logger import logger # Chemkin 0-D transient PSR model with given reactor volume from ansys.chemkin.core.stirreactors.transient_PSR import ( TransientPSRSetVolumeFixedTemperature as TransPsr, ) import matplotlib.pyplot as plt # plotting import numpy as np # number crunching # check working directory current_dir = str(Path.cwd()) logger.debug("working directory: " + current_dir) # set verbose mode ck.set_verbose(True) # set interactive mode for plotting the results # interactive = True: display plot # interactive = False: save plot as a PNG file global interactive interactive = True .. GENERATED FROM PYTHON SOURCE LINES 92-108 Create an instance of the Chemistry Set ======================================== The chemistry set describes the atomic layer deposition (ALD) of alumina from trimethylaluminum (TMA) and ozone. This mechanism is deliberately simplistic for illustration purposes only. It demonstrates one way of describing the ALD of alumina, but it should generally be considered as illustrative only and not used as a source of kinetic data for this process. This mechanism is designed to deposit stoichiometric alumina, with three oxygen atoms being deposited for each two aluminum atoms. The ALD mechanism is provided in the *examples\data* folder of the local *PyChemkin* installation. You must provide the mechanism file names, ``chem_AL2O3_ald.inp`` and ``surf_AL2O3_ald.inp``, with the correct path ``example_mech_data`` to the Chemistry Set before pre-processing. The thermodynamic data file from the default Ansys Chemkin installation will be used. .. GENERATED FROM PYTHON SOURCE LINES 108-129 .. code-block:: Python # set mechanism directory (the default Chemkin mechanism data directory) data_dir = Path(ck.ansys_dir) / "reaction" / "data" # find the location of this example py file try: script_dir_obj = Path(__file__).parent.resolve() except NameError: script_dir_obj = Path(current_dir) / ".." / "data" script_dir = str(script_dir_obj) # use the relative path to locate the example mechanism folder example_mech_data = script_dir_obj / ".." / "data" print(f"example data = {str(example_mech_data)}") # create a chemistry set based on the Si-N CVD mechanism MyALDMech = ck.Chemistry(label="Al2O3_ALD") # set mechanism input files # including the full file path is recommended local_data_dir = "AL2O3_ALD" MyALDMech.chemfile = str(example_mech_data / local_data_dir / "chem_AL2O3_ald.inp") MyALDMech.surffile = str(example_mech_data / local_data_dir / "surf_AL2O3_ald.inp") MyALDMech.thermfile = str(data_dir / "therm.dat") .. GENERATED FROM PYTHON SOURCE LINES 130-132 Preprocess the Chemistry Set ============================ .. GENERATED FROM PYTHON SOURCE LINES 132-140 .. code-block:: Python # preprocess the mechanism files ierror = MyALDMech.preprocess() if ierror != 0: print("Error: failed to preprocess the mechanism!") print(f" error code = {ierror}") exit() .. GENERATED FROM PYTHON SOURCE LINES 141-152 Set up the inlet streams ======================== Instantiate a stream feed for the inlet gas mixture. The stream is a mixture with the addition of the inlet flow rate. You specify inlet gas properties in the same way as you set up a mixture. Use the ``x`` method of the ``Stream`` object to specify the inlet gas composition with a ``recipe``. In this project, the ``mass_flowrate`` method, [g/sec] is used set the volumetric flow rates of the two inlet streams. The ``air`` composition is copied from the pre-defined ``Air`` object in Pychemkin. .. GENERATED FROM PYTHON SOURCE LINES 152-353 .. code-block:: Python # set the TMA stream composition and conditions met_organic = Stream(MyALDMech, label="TMA") met_organic.pressure = 1.0 * ck.P_TORRS # 1 [torr] met_organic.temperature = 620.2 # [K] met_organic.x = [("ALMe3", 0.01), ("AR", 0.99)] # TMA stream sccm flow rate profile in pulses # TMA stream sccm data points tma_time = np.array( [ 0.0, 0.19, 0.2, 5.19, 5.2, 5.39, 5.4, 10.39, 10.4, 10.59, 10.6, 15.59, 15.6, 15.79, 15.8, 20.8, ] ) # [sec] tma_profile = np.array( [ 800.0, 800.0, 0.0, 0.0, 800.0, 800.0, 0.0, 0.0, 800.0, 800.0, 0.0, 0.0, 800.0, 800.0, 0.0, 0.0, ] ) # [sccm] # set the TMA stream sccm profile met_organic.set_sccm_flowrate_profile(tma_time, tma_profile) # set the air composition and conditions oxidizers = Stream(MyALDMech, label="O3") oxidizers.pressure = met_organic.pressure oxidizers.temperature = 620.2 oxidizers.x = [("O2", 0.4), ("O3", 0.05), ("AR", 0.55)] # oxidizers stream sccm flow rate profile in pulses # oxidizers stream sccm data points oxid_time = np.array( [ 0.0, 1.19, 1.2, 3.19, 3.2, 6.39, 6.4, 8.39, 8.4, 11.59, 11.6, 13.59, 13.6, 16.79, 16.8, 18.79, 18.8, 20.8, ] ) # [sec] oxid_profile = np.array( [ 0.0, 0.0, 800.0, 800.0, 0.0, 0.0, 800.0, 800.0, 0.0, 0.0, 800.0, 800.0, 0.0, 0.0, 800.0, 800.0, 0.0, 0.0, ] ) # [sccm] # set the oxidizers stream sccm profile oxidizers.set_sccm_flowrate_profile(oxid_time, oxid_profile) # set the air composition and conditions purge = Stream(MyALDMech, label="AR") purge.pressure = met_organic.pressure purge.temperature = 620.2 purge.x = [("AR", 1.0)] # purge stream sccm flow rate profile in pulses # purge stream sccm data points purge_time = np.array( [ 0.0, 0.19, 0.2, 1.19, 1.2, 3.19, 3.2, 5.19, 5.2, 5.39, 5.4, 6.39, 6.4, 8.39, 8.4, 10.39, 10.4, 10.59, 10.6, 11.59, 11.6, 13.59, 13.6, 15.59, 15.6, 15.79, 15.8, 16.79, 16.8, 18.79, 18.8, 20.8, ] ) # [sec] purge_profile = np.array( [ 0.0, 0.0, 800.0, 800.0, 0.0, 0.0, 800.0, 800.0, 0.0, 0.0, 800.0, 800.0, 0.0, 0.0, 800.0, 800.0, 0.0, 0.0, 800.0, 800.0, 0.0, 0.0, 800.0, 800.0, 0.0, 0.0, 800.0, 800.0, 0.0, 0.0, 800.0, 800.0, ] ) # [sccm] # set the purge stream sccm profile purge.set_sccm_flowrate_profile(purge_time, purge_profile) # check the initial flow rates of the streams t = 0.0 print( "Initial 'TMA' stream flow rate = " f"{met_organic.get_sccm_flowrate_profile_data(time=t)} [SCCM]" ) print( "Initial 'O3' stream flow rate = " f"{oxidizers.get_sccm_flowrate_profile_data(time=t)} [SCCM]" ) print( "Initial 'AR purge' stream flow rate = " f"{purge.get_sccm_flowrate_profile_data(time=t)} [SCCM]" ) .. GENERATED FROM PYTHON SOURCE LINES 354-360 Set up the transient PSR bomb reactor ===================================== Instantiate the transient PSR ``ald_reactor`` as a ``TransientPSRSetVolumeFixedTemperature`` object. Since this is a transient simulation, a initial reactor condition is required. In this example, the ``ald_reactor`` is initially filled with pure argon. .. GENERATED FROM PYTHON SOURCE LINES 360-363 .. code-block:: Python ald_reactor = TransPsr(purge, label="ALD_reactor") .. GENERATED FROM PYTHON SOURCE LINES 364-373 Set up additional reactor model parameters ========================================== Before you can run the simulation, you must provide reactor parameters, solver controls, and output instructions. For a PSR, you must provide either the residence time or the reactor volume. You can also make changes to any reactor initial conditions such as the reactor temperature and the gas composition. For example, the ``reset_initial_temperature()``` method and the ``reset_initial_composition()`` method. You can use the solver parameters to improve the convergence performance. .. GENERATED FROM PYTHON SOURCE LINES 373-404 .. code-block:: Python # set PSR volume (cm3): required for the # ``TransientPSRSetVolumeFixedTemperature`` model ald_reactor.volume = 600.0 # reactor active surface area [cm2] ald_reactor.area = 300.0 # transient simulation end time [sec] ald_reactor.time = 20.8 # initial surface conditions # get the number of surface material defined in the surface mechanism n_material = ald_reactor.get_numb_material # get all surface material names ald_material_names = ald_reactor.get_material_names() # set up surface coverage of all surface materials # initial surface coverage (site fractions) site_recipe = [[("O(S)", 1.0)]] # bulk activities bulk_recipe = [[("AL2O3(B)", 1.0)]] # set the surface condition of the surface material for i, mname in enumerate(ald_material_names): # set area fraction [-] (optional: by default, materials have the same area) ald_reactor.set_material_area_fraction(mname, 1.0) # set material temperature [K] if it is different from the gas temperature # (optional: by default, it is the same as the mixture temperature) ald_reactor.set_material_temperature(mname, 723.0) # set reactor surface coverage of the material # site fractions (optional: by default, all sites have the same fraction) ald_reactor.set_site_fraction(mname, site_recipe[i]) # bulk activities (optional: by default, all bulk species activities = 1.0) ald_reactor.set_bulk_activity(mname, bulk_recipe[i]) .. GENERATED FROM PYTHON SOURCE LINES 405-414 Connect the inlet to the reactor ================================ You must connect at least one inlet to the open reactor. Use the ``set_inlet()`` method to add a stream to the PSR. Inversely, use the ``remove_inlet()`` to disconnect an inlet from the PSR. .. note :: There is no limit on the number of inlets that can be connected to a PSR. .. GENERATED FROM PYTHON SOURCE LINES 414-432 .. code-block:: Python # connect the metal organic stream to the reactor ald_reactor.set_inlet(met_organic) # connect the oxidizer stream to the reactor ald_reactor.set_inlet(oxidizers) # connect the purge stream to the reactor ald_reactor.set_inlet(purge) # check the net inlet flow rate to the ALD reactor at time = 0.0 [sec] # the net mass flow rate to the PSR must not be zero at any given time # during the simulation print( f"initial net mass flow rate to the reactor = " f"{ald_reactor.net_mass_flowrate} [g/sec]" ) .. GENERATED FROM PYTHON SOURCE LINES 433-437 Set solver controls =================== You can overwrite the default solver controls by using solver-related methods, such as those for tolerances. .. GENERATED FROM PYTHON SOURCE LINES 437-450 .. code-block:: Python # set solver the tolerances ald_reactor.tolerances = (1.0e-20, 1.0e-8) # solver non-negative mode ald_reactor.force_nonnegative = True # transient solver max time step size [sec] # ald_reactor.set_solver_max_timestep_size(2.0e-3) # set adaptive solution saving distance ald_reactor.adaptive_solution_saving(mode=True, steps=20) # use the legacy solver to get better coonvergence performance for # small mechanism # ald_reactor.set_legacy_option(option=True) .. GENERATED FROM PYTHON SOURCE LINES 451-474 Run the transient PSR model ============================ Once all the necessary input parameters are provided, use the ``run()`` method to start the transient PSR simulation. After the simulation is completed successfully, Use the ``process_solution()`` method to retrieve the raw solution profiles. If you are interested in the exit solution, use the ``get_last_solution_mixture()`` method to get the solution at the exit as a ``Stream`` object ``combustor_exhaust``. .. note :: In Pychemkin, the surface material is referred by its name. Instead of the surface material index, you loop over the surface material names. Use ``get_material_names()`` method to get a list of surface material names defined in the surface mechanism. .. note :: For a site or a bulk species, use any ``Mixture`` or ``Stream`` created with the ``Chemistry Set`` containing the surface mechanism to get its species index. There are two type of index for surface species, the global index includes all species (the gas plus the site and the bulk of all materials); the local index includes the same type of surface species (site or bulk) of all materials. .. GENERATED FROM PYTHON SOURCE LINES 474-552 .. code-block:: Python # set the start wall time start_time = time.time() # run the reactor model runstatus = ald_reactor.run() # if runstatus != 0: # run failed print(Color.RED + ">>> Run failed. <<<", end=Color.END) runtime = time.time() - start_time print(f"Total simulation duration: {runtime} [sec].") # postprocess the solution profiles ald_reactor.process_solution() # get the number of data points in the solution profiles solutionpoints = ald_reactor.getnumbersolutionpoints() print(f"number of solution time points = {solutionpoints}") # store solution profiles # simulation time [sec] sim_time = ald_reactor.get_solution_variable_profile("time") # solution arrays # get all surface material names aldt_material_names = ald_reactor.get_material_names() # AL2O3(B) linear growth rate [micron/min] al2o3_b_growth_solution = np.zeros(solutionpoints, dtype=np.double) al2o3_b_thickness = np.zeros_like(al2o3_b_growth_solution, dtype=np.double) # get gas-species index i_o = met_organic.get_specindex("O") i_tma = met_organic.get_specindex("ALMe3") o_solution = np.zeros_like(al2o3_b_growth_solution, dtype=np.double) tma_solution = np.zeros_like(al2o3_b_growth_solution, dtype=np.double) # get surface site fraction solution profiles # get surface species index # global index includes all species: the gas, the site of all materials, and the bulk # of all materials # local index includes the same type of species (site or bulk) of all materials i_o_s_global, i_o_s_local = met_organic.get_surf_specindex("O(S)") i_al2o3_b_global, i_al2o3_b_local = met_organic.get_surf_specindex("AL2O3(B)") # surface O(S) site fraction o_s_solution = ald_reactor.get_solution_variable_profile("O(S)") # surface ALMe2(S) site fraction alme2_s_solution = ald_reactor.get_solution_variable_profile("ALMe2(S)") # surface ALMeOALMe(S) site fraction almeoalme_s_solution = ald_reactor.get_solution_variable_profile("ALMeOALMe(S)") # loop over all solution time points for i in range(solutionpoints): # get the mixture at the time point solutionmixture = ald_reactor.get_solution_mixture_at_index(solution_index=i) # calculate species production rate due to surface reactions [mole/cm2-sec] brate = 0.0 for m in ald_material_names: # calculate species ROPs due to surface reactions [mole/cm2-sec] surf_rop, _ = solutionmixture.rop_surf(m) # get AL2O3(B) linear growth rate [cm/sec] due to surface reactions brates = solutionmixture.surface_chemistry.get_bulk_linear_growth_rates( m, surf_rop ) brate += brates[i_al2o3_b_local] # linear growth rate of AL2O3(B) bulk species [cm/sec] -> [micron/sec] al2o3_b_growth_solution[i] = brate * 1.0e4 # compute deposit thickness [micron] if i > 0: im = i - 1 delta_time = sim_time[i] - sim_time[im] delta_grate = al2o3_b_growth_solution[im] + al2o3_b_growth_solution[i] # integration al2o3_b_thickness[i] = al2o3_b_thickness[im] + delta_grate * delta_time / 2.0 # gas phase O solution profile [mole fraction] o_solution[i] = solutionmixture.x[i_o] # gas phase ALMe3 solution profile [mole fraction] tma_solution[i] = solutionmixture.x[i_tma] # clean up ck.done() .. GENERATED FROM PYTHON SOURCE LINES 553-558 Plot the transient PSR solution profiles ======================================== Plot the solution profiles over the entire simulation time span. You could observe how the growth of the AL2O3(B) thickness is controlled by turning on and off the inlet streams. .. GENERATED FROM PYTHON SOURCE LINES 558-585 .. code-block:: Python plt.subplots(2, 2, sharex="col", figsize=(12, 6)) plt.suptitle("Transient PSR ALD Solution", fontsize=16) plt.subplot(221) plt.plot(tma_time, tma_profile, "b-") plt.plot(oxid_time, oxid_profile, "r-") plt.legend(["met_organic", "oxidizers"], loc="upper right") plt.ylabel("Inlet volumetric flow rate [SCCM]") plt.subplot(222) plt.plot(sim_time, o_solution, "r-") plt.ylabel("O Mole fraction") plt.subplot(223) plt.plot(sim_time, o_s_solution, "r-") plt.plot(sim_time, almeoalme_s_solution, "b--") plt.plot(sim_time, alme2_s_solution, "g:") plt.legend(["O(S)", "ALMeOALMe(S)", "ALMe2(S)"], loc="upper right") plt.xlabel("Time [sec]") plt.ylabel("Site Fraction") plt.subplot(224) plt.plot(sim_time, al2o3_b_thickness, "b-") plt.xlabel("Time [sec]") plt.ylabel("Deposit thickness AL2O3(B) [micron]") # plot results if interactive: plt.show() else: plt.savefig("plot_trans_PSR_ALD.png", bbox_inches="tight") .. _sphx_glr_download_examples_surface_chemistry_trans_PSR_ALD.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: trans_PSR_ALD.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: trans_PSR_ALD.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: trans_PSR_ALD.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_