Source code for kinetic_models

# -*- coding: utf-8 -*-
"""
The kinetic models module provides functions for simulating chemical reaction kinetics using various kinetic models.
To create the global kinetic of reaction, multiple elements are available:

* rate model : describing the rate of purely chemical reaction
* vitrification model : describing a rate depending on vitrification and diffusion phenomenon
* coupling law : describing the relation between rate model and vitrification model
* tg law : describing the evolution of glass transition temperature with respect to extent

"""

import numpy as np
import scipy


[docs] def arrhenius_rate_constant(T,A,Ea): r""" Compute the value of rate constant with the Arrhenius equation. Parameters ---------- T : Float Temperature of reaction in Kelvin. A : Float Pre-exponential factor. Ea : Float Activation energy in J/mol. Returns ------- k : Float Rate constant for the Arrhenius equation. Notes ----- The Arrhenius rate constant is given by: .. math:: k = A e^{ \left( \frac{-E_a}{RT} \right)} """ return A*np.exp(-Ea/(8.31446261815324*T))
[docs] def rate_for_nth_order(extent,T,A1,E1,n): r""" Compute the value of the rate of reaction for a nth order reaction. .. math:: \dfrac{ d\alpha }{dt} = A e^{ \left( \dfrac{-E_a}{RT} \right) } (1-\alpha)^n Parameters ---------- extent : 1-D array Extent of reaction. T : 1-D array Temperature of reaction in Kelvin. A : Float Pre-exponential factor of the reaction. Ea : Float Activation energy of the reaction in J/mol. n : Float Order of reaction. Returns ------- rate : 1-D array Rate of reaction for a nth order reaction. """ return arrhenius_rate_constant(T,A1,E1)*((1-extent)**n)
[docs] def rate_for_autocatalytic(extent,T,A,Ea,m,n): r""" Compute the rate of reaction for an autocatalytic reaction. Parameters ---------- extent : ndarray Extent of reaction. T : ndarray Temperature of reaction in Kelvin. A : float Pre-exponential factor of the reaction. Ea : float Activation energy of the reaction in J/mol. m : float Order of reaction for the autocatalyzed reaction. n : float Order of reaction for the regular reaction. Returns ------- rate : ndarray Rate of reaction for an autocatalytic equation. Notes ----- The rate for an autocatalytic reaction is given by: .. math:: \frac{d\alpha}{dt} = A e^{ \left( \frac{-E_a}{RT} \right)} \alpha^m (1-\alpha)^n References ---------- [1] M. R. Keenan, « Autocatalytic cure kinetics from DSC measurements: Zero initial cure rate », J. Appl. Polym. Sci., vol. 33, nᵒ 5, p. 1725‑1734, avr. 1987, doi: 10.1002/app.1987.070330525. """ if np.any(extent) <= 0: raise ValueError("Be careful, for an autocatalytic model the extent can't be inferior or equal to 0 !!! \n It would result in a rate of reaction equal to 0 and no reaction would occur.") return arrhenius_rate_constant(T,A,Ea) * extent**m * (1 - extent)**n
[docs] def rate_for_kamal(extent,T,A1,E1,A2,E2,m,n): r""" Compute the value of the rate of reaction for a Kamal equation. Parameters ---------- extent : ndarray Extent of reaction. T : ndarray Temperature of reaction in Kelvin. A1 : float Pre-exponential factor of the regular reaction. E1 : float Activation energy of the regular reaction in J/mol. A2 : float Pre-exponential factor of the autocatalyzed reaction. E2 : float Activation energy of the autocatalyzed reaction in J/mol. m : float Order of reaction for the autocatalyzed reaction. n : float Order of reaction for the regular reaction. Returns ------- rate : ndarray Rate of reaction for a Kamal equation. Notes ----- The Kamal equation is given by: .. math:: \frac{d\alpha}{dt} = \left( A_1 e^{ \left( \frac{-E_1}{RT} \right)} + A_2 e^{ \left( \frac{-E_2}{RT} \right) } \alpha^m \right) (1-\alpha)^n References ---------- [1] G. J. Tsamasphyros, Th. K. Papathanassiou, et S. I. Markolefas, « Some Analytical Solutions of the Kamal Equation for Isothermal Curing With Applications to Composite Patch Repair », Journal of Engineering Materials and Technology, vol. 131, no 1, Dec. 2008, doi: 10.1115/1.3026550. Examples -------- >>> import time as t >>> import numpy as np >>> import matplotlib.pyplot as plt ... >>> number_of_points = 10000 >>> time_points = np.linspace(0, 1800, number_of_points) # 30 minutes >>> temperatures = [ np.linspace(293, 443, number_of_points), # 5°C/min np.linspace(293, 593, number_of_points), # 10°C/min np.linspace(293, 743, number_of_points), # 15°C/min np.linspace(293, 1093, number_of_points) # 20°C/min ] >>> conversions = [] >>> rates = [] ... >>> fig, ax1 = plt.subplots(num=1) >>> ax1_bis = ax1.twinx() >>> ax1.set_xlabel("time") >>> ax1.set_ylabel("conversion") >>> ax1_bis.set_ylabel("rate") ... >>> for i, temperature in enumerate(temperatures): >>> t0 = t.time() >>> extent, rate = compute_extent_and_rate(time_points, temperature, rate_for_kamal, (1.666e8, 80000, 1.666e13, 120000, 1, 0.7)) >>> t1 = t.time() >>> execution_time = t1 - t0 >>> print("Time:", execution_time, "s") >>> conversions.append(extent) >>> rates.append(rate) >>> ax1.plot(time_points, extent, label=f"conversion for a heating rate of {(i+1)*5}°/min", linestyle='dotted') >>> ax1_bis.plot(time_points, rate, label=f"rate for a heating rate of {(i+1)*5}°/min") >>> ax1.legend() >>> ax1_bis.legend() ... >>> plt.show() """ return (arrhenius_rate_constant(T,A1,E1) + (arrhenius_rate_constant(T,A2,E2) * extent**m)) * (1 - extent)**n
[docs] def vitrification_WLF_rate(T,Tg,Ad,C1,C2): r""" Compute the vitrification term for a WLF-like model. The vitrification term is given by the WLF model: .. math:: k_v = A_d e^{ \left( \frac{C_1(T - T_g(\alpha))}{C_2 + |T - T_g(\alpha)|} \right) } where: * :math:`T` is the temperature of the reaction (in Kelvin). * :math:`A_d` is the pre-exponential factor of the vitrification term * :math:`C_1` is Constant 1 of the WLF model * :math:`C_2` is Constant 2 of the WLF model * :math:`T_g` is the glass transition temperature (in Kelvin). Usually, the glass transition temperature is determined by using the DiBenedetto equation. Parameters ---------- T : array-like Temperature of the reaction (in Kelvin) Ad : float Pre-exponential factor of the vitrification term C1 : float Constant 1 of the WLF model C2 : float Constant 2 of the WLF model Tg : float Glass transition temperature (in Kelvin) Returns ------- rate : array-like Rate of the reaction References ---------- [1] G. Wisanrakkit et J. K. Gillham, "The glass transition temperature (Tg) as an index of chemical conversion for a high-Tg amine/epoxy system: Chemical and diffusion-controlled reaction kinetics", Journal of Applied Polymer Science, vol. 41, no. 11-12, p. 2885-2929, 1990, doi: 10.1002/app.1990.070411129. """ #Check if temperature of reaction is above glass transition temperature #If so, the vitrification term is computed, else the rate is equal to 0 return Ad*np.exp( (C1*(T-Tg)) / (C2+abs(T-Tg)))
[docs] def vitrification_WLF_rate_no_reaction_below_Tg(T,Tg,Ad,C1,C2): r""" Compute the vitrification term for a WLF-like model when the reaction temperature is above Tg. The vitrification term is equal to 0 when below Tg. .. math:: k_v = A_d e^{ \left( \dfrac{C_1(T-T_g(\alpha))}{C_2+ |T-T_g(\alpha)|} \right) } Usually the glass transition temperature is determined by using the DiBenedetto equation See: G. Wisanrakkit et J. K. Gillham, « The glass transition temperature (Tg) as an index of chemical conversion for a high-Tg amine/epoxy system: Chemical and diffusion-controlled reaction kinetics », Journal of Applied Polymer Science, vol. 41, nᵒ 11‑12, p. 2885‑2929, 1990, doi: 10.1002/app.1990.070411129. Parameters ---------- T : Array-like Temperature of reaction (in Kelvin) Ad : Float Pre-exponential factor of the vitrification term C1 : Float Constant 1 of WLF model C2 : Float Constant 2 of WLF model Tg : Float Glass transition temperature (in Kelvin) Returns ------- Rate : Array-like Rate of reaction References ---------- [1] G. Wisanrakkit et J. K. Gillham, "The glass transition temperature (Tg) as an index of chemical conversion for a high-Tg amine/epoxy system: Chemical and diffusion-controlled reaction kinetics", Journal of Applied Polymer Science, vol. 41, no. 11-12, p. 2885-2929, 1990, doi: 10.1002/app.1990.070411129. """ #Check if temperature of reaction is above glass transition temperature #If so, the vitrification term is computed, else the rate is equal to 0 return np.where( T>Tg, Ad*np.exp( (C1*(T-Tg)) / (C2+abs(T-Tg))), 0)
[docs] def tg_diBennedetto(extent,Tg_0,Tg_inf,coeff): r""" Compute the glass transition temperature using the DiBennedetto equation. Parameters ---------- alpha : array-like conversion or extent of reaction Tg_0 : Float glass transition temperature of unreacted material in Kelvin Tg_inf : Float glass transition temperature of fully reacted material in Kelvin coeff : Float ratio of the changes in isobaric heat capacities at Tg of the fully reacted material and of the initial unreacted material Returns ------- Tg : array-like Glass transition temperature in Kelvin Notes ----- The DiBennedetto equation is given by: .. math:: Tg = Tg_{0} + \dfrac{ (Tg_{\infty}-Tg_{0}). \lambda .\alpha}{1-(1-\lambda)\alpha} \\ with: \\ Tg_{0}: \textrm{The glass transition temperature of unreacted material} \\ Tg_{\infty}: \textrm{The glass transition temperature of completely cured material} \\ \lambda : \dfrac{\Delta C_{p_{\infty}}}{\Delta C_{p_{0}}} \textrm{the ratio of the changes in isobaric heat capacities at} \\ \textrm{Tg of the fully reacted material and of the initial unreacted material}\\ """ return Tg_0 + (Tg_inf - Tg_0)*((coeff*extent)/(1-(1-coeff)*extent))
[docs] def coupling_harmonic_mean(kc,kv,experimental_parameters=None): r""" Return the harmonic mean of a chemical rate and vitrification rate. .. math:: \dfrac{ d\alpha }{dt} = \dfrac{1}{\dfrac{1}{k_c } + \dfrac{1}{k_v}} Parameters ---------- kc : 1-D array Rate of chemical reaction kv : 1-D array Vitrification term experimental_parameters : NoneType NoneType argument to stick with guideline of function creation Returns ------- Rate : 1-D array Rate of reaction References ---------- [1] G. Wisanrakkit et J. K. Gillham, « The glass transition temperature (Tg) as an index of chemical conversion for a high-Tg amine/epoxy system: Chemical and diffusion-controlled reaction kinetics », Journal of Applied Polymer Science, vol. 41, nᵒ 11‑12, p. 2885‑2929, 1990, doi: 10.1002/app.1990.070411129. """ #If kc or kv are equal to 0, then it returns 0 rate = 1 / ((1/kc)+(1/kv)) return rate
[docs] def coupling_product(kc,kv,experimental_parameters=None): r""" Return the product of a chemical rate and vitrification rate. .. math:: \dfrac{ d\alpha }{dt} = k_c * k_v Parameters ---------- kc : 1-D array Rate of chemical reaction kv : 1-D array Vitrification term experimental_parameters : NoneType NoneType argument to stick with guideline of function creation Returns ------- Rate : 1-D array Rate of reaction """ return kc*kv
[docs] def jac_for_rate_for_kamal(extent, T, A1, E1, A2, E2, m, n): r""" Compute the Jacobian vector for the Kamal equation. Parameters ---------- extent : float Extent of the reaction. T : float Temperature in Kelvin. A1 : float Pre-exponential factor for reaction 1. E1 : float Activation energy for reaction 1 in J/mol. A2 : float Pre-exponential factor for reaction 2. E2 : float Activation energy for reaction 2 in J/mol. m : int Power term for reaction 2. n : int Power term for both reactions. Returns ------- jac : ndarray Jacobian vector of shape (6,) representing the first derivatives of the rate expression with respect to (A1, E1, A2, E2, m, n). Notes ----- This function computes the Jacobian vector for a rate expression based on the extent of the reaction, temperature, pre-exponential factors, activation energies, and power terms for two reactions. The Jacobian vector represents the first derivatives of the rate expression with respect to the extent of the reaction. The Jacobian vector J is given by: .. math:: J = \begin{bmatrix} (1 - x)^n e^{-\frac{E_1}{RT}} \\ -\frac{A_1 (1 - x)^n e^{-\frac{E_1}{RT}}}{RT} \\ x^m (1 - x)^n e^{-\frac{E_2}{RT}} \\ -\frac{A_2 x^m (1 - x)^n e^{-\frac{E_2}{RT}}}{RT} \\ A_2 x^m (1 - x)^n \log(x) e^{-\frac{E_2}{RT}} \\ (1 - x)^n \log(1 - x) (A_2 x^m e^{-\frac{E_2}{RT}} + A_1 e^{-\frac{E_1}{RT}}) \end{bmatrix} """ R = 8.31446261815324 # Ideal gas constant exp_E1_RT = np.exp(-E1 / (R * T)) exp_E2_RT = np.exp(-E2 / (R * T)) log_extent = np.log(extent) log_1_minus_extent = np.log(1 - extent) extent_to_m = extent ** m one_minus_extent_to_n = (1 - extent) ** n jac = np.zeros(6) jac[0] = one_minus_extent_to_n * exp_E1_RT jac[1] = -(A1 * one_minus_extent_to_n * exp_E1_RT) / (R * T) jac[2] = extent_to_m * one_minus_extent_to_n * exp_E2_RT jac[3] = -(A2 * extent_to_m * one_minus_extent_to_n * exp_E2_RT) / (R * T) jac[4] = A2 * extent_to_m * one_minus_extent_to_n * log_extent * exp_E2_RT jac[5] = one_minus_extent_to_n * log_1_minus_extent * (A2 * extent_to_m * exp_E2_RT + A1 * exp_E1_RT) return jac
[docs] def hess_for_rate_for_kamal(extent, T, A1, E1, A2, E2, m, n): r""" Compute the Hessian matrix for the Kamal equation. Parameters ---------- extent : float Extent of the reaction. T : float Temperature in Kelvin. A1 : float Pre-exponential factor for reaction 1. E1 : float Activation energy for reaction 1 in J/mol. A2 : float Pre-exponential factor for reaction 2. E2 : float Activation energy for reaction 2 in J/mol. m : int Power term for reaction 2. n : int Power term for both reactions. Returns ------- hessian : ndarray Hessian matrix of shape (6, 6) representing the second derivatives of the rate expression with respect to (A1, E1, A2, E2, m, n). Notes ----- The Hessian matrix H is given by: .. math:: H = \begin{bmatrix} 0 & -\frac{(1 - x)^n e^{-E_1/(RT)}}{RT} & 0 & 0 & 0 & (1 - x)^n \log(1 - x) e^{-E_1/(RT)} \\ -\frac{(1 - x)^n e^{-E_1/(RT)}}{RT} & \frac{A_1 (1 - x)^n e^{-E_1/(RT)}}{(R^2 T^2)} & 0 & 0 & 0 & -\frac{A_1 (1 - x)^n \log(1 - x) e^{-E_1/(RT)}}{RT} \\ 0 & 0 & 0 & -\frac{x^m (1 - x)^n e^{-E_2/(RT)}}{RT} & x^m (1 - x)^n \log(x) e^{-E_2/(RT)} & x^m (1 - x)^n \log(1 - x) e^{-E_2/(RT)} \\ 0 & 0 & -\frac{x^m (1 - x)^n e^{-E_2/(RT)}}{RT} & \frac{A_2 x^m (1 - x)^n e^{-E_2/(RT)}}{(R^2 T^2)} & -\frac{A_2 x^m (1 - x)^n \log(x) e^{-E_2/(RT)}}{RT} & -\frac{A_2 x^m (1 - x)^n \log(1 - x) e^{-E_2/(RT)}}{RT} \\ 0 & 0 & x^m (1 - x)^n \log(x) e^{-E_2/(RT)} & -\frac{A_2 x^m (1 - x)^n \log(x) e^{-E_2/(RT)}}{RT} & A_2 x^m (1 - x)^n \log^2(x) e^{-E_2/(RT)} & A_2 x^m (1 - x)^n \log(1 - x) \log(x) e^{-E_2/(RT)} \\ (1 - x)^n \log(1 - x) e^{-E_1/(RT)} & -\frac{A_1 (1 - x)^n \log(1 - x) e^{-E_1/(RT)}}{RT} & x^m (1 - x)^n \log(1 - x) e^{-E_2/(RT)} & -\frac{A_2 x^m (1 - x)^n \log(1 - x) e^{-E_2/(RT)}}{RT} & A_2 x^m (1 - x)^n \log(1 - x) \log(x) e^{-E_2/(RT)} & (1 - x)^n \log^2(1 - x) (A_2 x^m e^{-E_2/(RT)} + A_1 e^{-E_1/(RT)}) \end{bmatrix} """ R = 8.31446261815324 # Ideal gas constant exp_E1_RT = np.exp(-E1 / (R * T)) exp_E2_RT = np.exp(-E2 / (R * T)) log_1_minus_x = np.log(1 - extent) log_x = np.log(extent) x_to_m = extent ** m one_minus_x_to_n = (1 - extent) ** n hessian = np.zeros((6, 6)) hessian[0, 1] = -one_minus_x_to_n * exp_E1_RT / (R * T) hessian[0, 5] = one_minus_x_to_n * log_1_minus_x * exp_E1_RT hessian[1, 0] = hessian[0, 1] hessian[1, 1] = A1 * one_minus_x_to_n * exp_E1_RT / (R**2 * T**2) hessian[1, 5] = -A1 * one_minus_x_to_n * log_1_minus_x * exp_E1_RT / (R * T) hessian[2, 3] = -x_to_m * one_minus_x_to_n * exp_E2_RT / (R * T) hessian[2, 4] = x_to_m * one_minus_x_to_n * log_x * exp_E2_RT hessian[2, 5] = x_to_m * one_minus_x_to_n * log_1_minus_x * exp_E2_RT hessian[3, 3] = A2 * x_to_m * one_minus_x_to_n * exp_E2_RT / (R**2 * T**2) hessian[3, 4] = -A2 * x_to_m * one_minus_x_to_n * log_x * exp_E2_RT / (R * T) hessian[3, 5] = -A2 * x_to_m * one_minus_x_to_n * log_1_minus_x * exp_E2_RT / (R * T) hessian[4, 2] = hessian[2, 4] hessian[4, 3] = hessian[3, 4] hessian[4, 4] = A2 * x_to_m * one_minus_x_to_n * log_x**2 * exp_E2_RT hessian[4, 5] = A2 * x_to_m * one_minus_x_to_n * log_1_minus_x * log_x * exp_E2_RT hessian[5, 0] = hessian[0, 5] hessian[5, 1] = hessian[1, 5] hessian[5, 2] = hessian[2, 5] hessian[5, 3] = hessian[3, 5] hessian[5, 4] = hessian[4, 5] hessian[5, 5] = one_minus_x_to_n * log_1_minus_x**2 * (A2 * x_to_m * exp_E2_RT + A1 * exp_E1_RT) return hessian
[docs] def compute_extent_and_rate(time, temperature, rate_law=None, rate_law_args=None, vitrification_law=None, vitrification_law_args=None, tg_law=None, tg_law_args=None, coupling_law=None, coupling_law_args=None, initial_extent=0): """ Compute the evolution of extent and rate during a reaction, with or without vitrification. Parameters ---------- time : array-like List or array containing the times during the reaction. temperature : array-like List or array containing the temperatures (in Kelvin) during the reaction. rate_law : function The rate law function that calculates the rate of reaction. rate_law_args : tuple Additional arguments to be passed to the rate law function. vitrification_law : function, optional The vitrification law function that calculates the vitrification term. vitrification_law_args : tuple, optional Additional arguments to be passed to the vitrification law function. tg_law : function, optional The glass transition temperature law function that calculates the Tg. tg_law_args : tuple, optional Additional arguments to be passed to the Tg law function. coupling_law : function, optional Law used to mix the rate of chemical reaction and rate of vitrification. coupling_law_args : tuple, optional Additional arguments to be passed to the coupling law function. initial_extent : float, optional The initial extent of reaction. Default is 0. It must be positive and inferior to 1. Returns ------- extent : ndarray Evolution of extent during the reaction. global_rate : ndarray Evolution of the global rate of reaction. chemical_rate : ndarray Evolution of the chemical rate of reaction. vitrification_term : ndarray, optional Evolution of the vitrification rate of reaction (if vitrification parameters are provided). tg : ndarray, optional Evolution of the Tg in Kelvin (if Tg parameters are provided). """ # Importation of a module to display a progress bar for the finite difference from tqdm import tqdm if coupling_law: if not rate_law: raise NameError("Please, provide the rate law to use for computation or indicate 'None' for the coupling law") if not vitrification_law: raise NameError("Please, provide the vitrification law to use for computation or indicate 'None' for the coupling law") # The number of iterations for the finite difference is equal to the number of time points n = len(time) # Creation and initialization of numpy arrays extent = np.zeros(n) extent[0] = initial_extent if rate_law: chemical_rate = np.zeros(n) chemical_rate[0] = rate_law(extent[0], temperature[0], *rate_law_args) if tg_law: tg = np.zeros(n) tg[0] = tg_law(extent[0],*tg_law_args) if vitrification_law: vitrification_term = np.zeros(n) vitrification_term[0] = vitrification_law(temperature[0],tg[0],*vitrification_law_args) if coupling_law: global_rate = np.zeros(n) global_rate[0] = coupling_law(chemical_rate[0], vitrification_term[0], *coupling_law_args) if vitrification_law is not None else chemical_rate[0] for i in tqdm(range(n - 1), desc="Progress"): dt = time[i + 1] - time[i] if coupling_law: extent_for_next_step = extent[i] + global_rate[i] * dt # Compute extent for the next step elif vitrification_law: extent_for_next_step = extent[i] + vitrification_term[i] * dt else: extent_for_next_step = extent[i] + chemical_rate[i] * dt if extent_for_next_step > 1: extent[i + 1:] = 1 if coupling_law: global_rate[i + 1:] = 0 if rate_law: chemical_rate[i + 1:] = 0 if tg_law: tg[i + 1:] = 0 if vitrification_law: vitrification_term[i + 1:] = 0 break else: extent[i + 1] = extent_for_next_step # Update extent for the next step if rate_law: chemical_rate[i + 1] = rate_law(extent[i + 1], temperature[i + 1], *rate_law_args) if tg_law: tg[i + 1] = tg_law(extent[i + 1], *tg_law_args) if vitrification_law: vitrification_term[i + 1] = vitrification_law(temperature[i + 1], tg[i + 1], *vitrification_law_args) if coupling_law: global_rate[i + 1] = coupling_law(chemical_rate[i + 1], vitrification_term[i + 1], *coupling_law_args) # Return appropriate results based on the provided parameters if coupling_law: if tg_law: return extent, global_rate, chemical_rate, vitrification_term, tg else: return extent, global_rate, chemical_rate, vitrification_term, None if vitrification_law: if tg_law: return extent, vitrification_term, None, vitrification_term, tg else: return extent, vitrification_term, None, vitrification_term, None if rate_law: if tg_law: return extent, chemical_rate, chemical_rate, None, tg else: return extent, chemical_rate, chemical_rate, None, None else: return AttributeError("No law was given for calculations.")
#%% Example 1 - Kamal if __name__=="__main__": import matplotlib.pyplot as plt import time as t number_of_points = 10000 time = np.linspace(0, 1800, number_of_points) # 30 minutes times = [time, time, time, time] temperatures = [np.linspace(293, 443, number_of_points), # 5°C/min np.linspace(293, 593, number_of_points), # 10°C/min np.linspace(293, 743, number_of_points), # 15°C/min np.linspace(293, 1093, number_of_points)] # 20°C/min conversions=[] rates=[] fig, ax1 = plt.subplots() ax1_bis = ax1.twinx() ax1.set_xlabel("time") ax1.set_ylabel("conversion") ax1_bis.set_ylabel("rate") for i in range(len(temperatures)): t0=t.time() extent,rate,_,_,_ = compute_extent_and_rate(time, temperatures[i], rate_law=rate_for_kamal, rate_law_args=(1.666e8,80000,1.666e13,120000,1,0.7)) t1=t.time() print("Time: ",t1-t0,"s") conversions.append(extent) rates.append(rate) ax1.plot(time,extent,label="conversion for a heating rate of " + str((i+1)*5) + "°/min",linestyle='dotted') ax1_bis.plot(time,rate,label="rate for a heating rate of " + str((i+1)*5) + "°/min") ax1.legend() ax1_bis.legend() #%% Test main kinetic models num_points = 10000 time = np.linspace(0, 1800, int(num_points)) temperature = np.full(int(num_points), 293.15) # Chemical rate parameters A1 = 1e10 E1 = 70000 A2 = 1e13 E2 = 85000 m = 0.45 n = 1 # Vitrification parameters extracted from [1] G. Wisanrakkit et J. K. Gillham, « The glass transition temperature (Tg) as an index of chemical conversion for a high-Tg amine/epoxy system: Chemical and diffusion-controlled reaction kinetics », Journal of Applied Polymer Science, vol. 41, nᵒ 11‑12, p. 2885‑2929, 1990, doi: 10.1002/app.1990.070411129. Ad = 30.64 C1 = 42.61 C2 = 51.6 # Tg parameters taken Tg0 = -100 + 273.15 Tginf = 100 + 273.15 lmbd = 0.4 extent_nth_order, rate_nth_order, _, _, tg_nth_order = compute_extent_and_rate(time, temperature, rate_law=rate_for_nth_order, rate_law_args=(A1,E1,n), tg_law = tg_diBennedetto, tg_law_args = (Tg0, Tginf, lmbd), initial_extent=0.001) extent_autocatalytic, rate_autocatalytic, _, _, tg_autocatalytic = compute_extent_and_rate(time, temperature, rate_law=rate_for_autocatalytic, rate_law_args=(A1,E1,m,n), tg_law = tg_diBennedetto, tg_law_args = (Tg0, Tginf, lmbd), initial_extent=0.001) extent_kamal, rate_kamal, _, _, tg_kamal = compute_extent_and_rate(time, temperature, rate_for_kamal, rate_law_args=(A1,E1,A2,E2,m,n), tg_law = tg_diBennedetto, tg_law_args = (Tg0, Tginf, lmbd), initial_extent=0.001) extent_nth_order_vitrification, rate_nth_order_vitrification, _, _, tg_nth_order_vitrification = compute_extent_and_rate(time, temperature, rate_law=rate_for_nth_order, rate_law_args=(A1,E1,n), vitrification_law=vitrification_WLF_rate, vitrification_law_args=(Ad,C1,C2), tg_law = tg_diBennedetto, tg_law_args = (Tg0, Tginf, lmbd), coupling_law=coupling_harmonic_mean, coupling_law_args=(), initial_extent=0.001) extent_nth_order_vitrification_no_reac, rate_nth_order_vitrification_no_reac, _, _, tg_nth_order_vitrification_no_reac = compute_extent_and_rate(time, temperature, rate_law=rate_for_nth_order, rate_law_args=(A1,E1,n), vitrification_law=vitrification_WLF_rate_no_reaction_below_Tg, vitrification_law_args=(Ad,C1,C2), tg_law = tg_diBennedetto, tg_law_args = (Tg0, Tginf, lmbd), coupling_law=coupling_harmonic_mean, coupling_law_args=(), initial_extent=0.001) fig, ax = plt.subplots() ax.plot(time,extent_nth_order,label="n-th order") ax.plot(time,extent_autocatalytic,label="autocatalytic") ax.plot(time,extent_kamal,label="kamal") ax.plot(time,extent_nth_order_vitrification,label="n-th order with vitrification") ax.plot(time,extent_nth_order_vitrification_no_reac,label="n-th order with vitrification_no_reac") ax.set_xlabel("Time (s)") ax.set_ylabel("Extent") ax.legend() ax.set_title("Comparison of the extent evolution for multiple kinetic models") fig, ax = plt.subplots() ax.plot(time,rate_nth_order,label="n-th order") ax.plot(time,rate_autocatalytic,label="autocatalytic") ax.plot(time,rate_kamal,label="kamal") ax.plot(time,rate_nth_order_vitrification,label="n-th order with vitrification") ax.plot(time,rate_nth_order_vitrification_no_reac,label="n-th order with vitrification_no_reac") ax.set_xlabel("Time (s)") ax.set_ylabel("Rate") ax.legend() ax.set_title("Comparison of the rate evolution for multiple kinetic models") fig, ax = plt.subplots() ax.plot(time,temperature- 273.15,color='black',alpha=0.2) ax.plot(time,tg_nth_order - 273.15,label="n-th order") ax.plot(time,tg_autocatalytic - 273.15,label="autocatalytic") ax.plot(time,tg_kamal - 273.15,label="kamal") ax.plot(time,tg_nth_order_vitrification - 273.15,label="n-th order with vitrification") ax.plot(time,tg_nth_order_vitrification_no_reac - 273.15,label="n-th order with vitrification_no_reac") ax.set_xlabel("Time (s)") ax.set_ylabel("Tg") ax.legend() ax.set_title("Comparison of the Tg evolution for multiple kinetic models") # Save data to a text file with open('kinetic_data.txt', 'w') as file: file.write("Time(s)\tTemperature(K)\tExtent_nth_order\tRate_nth_order\tTg_nth_order\tExtent_autocatalytic\tRate_autocatalytic\tTg_autocatalytic\tExtent_kamal\tRate_kamal\tTg_kamal\tExtent_nth_order_vitrification\tRate_nth_order_vitrification\tTg_nth_order_vitrification\tExtent_nth_order_vitrification_no_reac\tRate_nth_order_vitrification_no_reac\tTg_nth_order_vitrification_no_reac\n") for i in range(len(time)): file.write(f"{time[i]}\t{temperature[i]}\t{extent_nth_order[i]}\t{rate_nth_order[i]}\t{tg_nth_order[i]}\t{extent_autocatalytic[i]}\t{rate_autocatalytic[i]}\t{tg_autocatalytic[i]}\t{extent_kamal[i]}\t{rate_kamal[i]}\t{tg_kamal[i]}\t{extent_nth_order_vitrification[i]}\t{rate_nth_order_vitrification[i]}\t{tg_nth_order_vitrification[i]}\t{extent_nth_order_vitrification_no_reac[i]}\t{rate_nth_order_vitrification_no_reac[i]}\t{tg_nth_order_vitrification_no_reac[i]}\n") #%% Convergence graph for multiple rate laws def compute_trap_sums(numbers_of_points, rate_law, rate_law_args, vitrification_law=None, vitrification_args=None, tg_law=None, tg_law_args=None, coupling_law=None, coupling_law_args=None, initial_extent=0.001): trap_sums = [] for num_points in numbers_of_points: time = np.linspace(0, 1800, int(num_points)) temperature = np.full(int(num_points), 443) extent, rate, _, _, _ = compute_extent_and_rate(time, temperature, rate_law=rate_law, rate_law_args=rate_law_args, vitrification_law=None, vitrification_law_args=None, tg_law=None, tg_law_args=None, coupling_law=None, coupling_law_args=None, initial_extent=initial_extent) trap_sums.append(np.trapz(rate, time)) return np.array(trap_sums)-1 A1 = 1e10 E1 = 70000 A2 = 1e13 E2 = 85000 m = 0.45 n = 1 # Vitrification parameters extracted from [1] G. Wisanrakkit et J. K. Gillham, « The glass transition temperature (Tg) as an index of chemical conversion for a high-Tg amine/epoxy system: Chemical and diffusion-controlled reaction kinetics », Journal of Applied Polymer Science, vol. 41, nᵒ 11‑12, p. 2885‑2929, 1990, doi: 10.1002/app.1990.070411129. Ad = 30.64 C1 = 42.61 C2 = 51.6 # Tg parameters taken Tg0 = -100 + 273.15 Tginf = 100 + 273.15 lmbd = 0.4 # Compute trap_sums numbers_of_points = np.logspace(0, 6, 100) trap_sums_nth_order = compute_trap_sums(numbers_of_points, rate_for_nth_order, (A1,E1,n),tg_law = tg_diBennedetto, tg_law_args = (Tg0, Tginf, lmbd),initial_extent=0.001) trap_sums_autocatalytic = compute_trap_sums(numbers_of_points, rate_for_autocatalytic, (A1,E1,m,n),tg_law = tg_diBennedetto,tg_law_args = (Tg0, Tginf, lmbd),initial_extent=0.001) trap_sums_kamal = compute_trap_sums(numbers_of_points, rate_for_kamal, (A1,E1,A2,E2,m,n),tg_law = tg_diBennedetto,tg_law_args = (Tg0, Tginf, lmbd),initial_extent=0.001) trap_sums_nth_order_vitrification = compute_trap_sums(numbers_of_points, rate_for_nth_order, (A1,E1,n), vitrification_law=vitrification_WLF_rate, vitrification_args=(Ad,C1,C2), tg_law = tg_diBennedetto, tg_law_args = (Tg0, Tginf, lmbd), coupling_law=coupling_harmonic_mean, coupling_law_args=(), initial_extent=0.001) trap_sums_nth_order_vitrification_no_reac = compute_trap_sums(numbers_of_points, rate_for_nth_order, (A1,E1,n), vitrification_law=vitrification_WLF_rate_no_reaction_below_Tg, vitrification_args=(Ad,C1,C2), tg_law = tg_diBennedetto, tg_law_args = (Tg0, Tginf, lmbd), coupling_law=coupling_harmonic_mean, coupling_law_args=(), initial_extent=0.001) # Plot the convergence graph fig, ax = plt.subplots(figsize=(8, 6)) ax.plot(1/numbers_of_points,trap_sums_nth_order,label="n-th order",linewidth=8,alpha=0.2) ax.plot(1/numbers_of_points,trap_sums_autocatalytic,label="autocatalytic") ax.plot(1/numbers_of_points,trap_sums_kamal,label="kamal") ax.plot(1/numbers_of_points,trap_sums_nth_order_vitrification,label="n-th order with vitrification",linewidth=4,alpha=0.6) ax.plot(1/numbers_of_points,trap_sums_nth_order_vitrification_no_reac,label="n-th order with vitrification_no_reac") plt.xscale("log") plt.yscale("log") ax.legend() ax.set_xlabel("Timestep (s)") ax.set_ylabel("Trapezoïdal sum") ax.set_title("Convergence for multiple kinetic models") # Show the plot plt.show()