Kinetics

Thermal analysis

Conceiving a minimal working example for a thermal analysis implementation is not a trivial task; one needs to get as little code as possible while still presenting a meaninful case. Here we have chosen to simulate the drying of some glass powder, represented by SIO2_GLASS compound available in the built-in database. The main and most demanding task is to define the kinetics that is provided to ThermalAnalysisData. Below we do so using anonymous functions to implement the kinetics of water evaporation proposed by [3] and later used by [4]. Please notice that a temperature-dependent reaction enthalpy is used here, what makes the sample not so minimal.

Note: this 1-reaction example would work if we do not encapsulate outputs is arrays as used for illustration, but trying to do so would quickly break more complex cases and it was chosen that it would not consititute a minimal working example.

data = ThermalAnalysisData(;
    selected_species = ["WATER_L", "SIO2_GLASS"],
    released_species = ["WATER_G"],
    n_reactions = 1,

    reaction_rates = function(obj, m, T, Y)
        k = 5.e+07exp(-61.0e+03 / (GAS_CONSTANT * T))
        [k * m * Y[1] / obj.sample.molar_masses[1]]
    end,

    net_production_rates = function(obj, r)
        diagm(obj.sample.molar_masses) * (hcat([-1; 0]) * r)
    end,

    mass_loss_rate = function(obj, r)
        [-1obj.losses.molar_masses[1] * r[1]]
    end,

    heat_release_rate = function(obj, r, T)
        h2o_l, h2o_g = obj.sample.species[1], obj.losses.species[1]
        ΔH = AuChimiste.enthalpy_reaction(T, [1], [1], [h2o_l], [h2o_g])
        r' * [ΔH;]
    end
)

@info(typeof(data))
@info(species_table(data.sample.db))
[ Info: ThermalAnalysisData{2, 1, 1}
┌ Info: 2×4 DataFrame
│  Row │ names       display         source        state
│      │ String      String          String        String
│ ─────┼──────────────────────────────────────────────────
│    1 │ SIO2_GLASS  SiO2 (glass)    SCHIELTZ1964  solid
└    2 │ WATER_L     Water (liquid)  SCHIELTZ1964  liquid

Below we define the test conditions; this comprises the heating rate and the interval of trial, the initial mass of the sample and its composition (here defined simply by the humidity level).

# Analysis heating rate.
Θ = 20.0

# Integration interval to simulate problem.
T_ini = 300.0
T_end = 400.0

# Initial mass [mg].
m = 15.0

# Humidity level [%wt]
h = 2

# Initial composition of the system.
y0 = [h, 100-h] * 0.01

To run the test we provide the temperature program (which can be any arbitrary differentiable function of time in seconds) to [ThermalAnalysisModel] and run the simulation. Notice that conversion factor from minute to second units is provided in the functions. Because any valid thermal cycle can be provided by the user, the interface of solve gets as second argument a time, which is computed in place. Solution can be retrieved as a DataFrame by using tabulate as illustrated below.

model = ThermalAnalysisModel(; data = data,
    program_temperature = (t)->T_ini + (Θ/60) * t)

sol = solve(model, (T_end-T_ini) * 60/Θ, m, y0)

tabulate(model, sol)[1:5, :]
5×12 DataFrame
RowTime [s]Temperature [K]Mass [mg]Specific heat [kJ/(kg.K)]Heat input [mW]DSC signal [W/g]TGA signal [%wt]Enthalpy change [MJ/kg]Energy consumption [MJ/kg]SiO2 (glass) [%wt]Water (liquid) [%wt]Water (gas) [mg/s]
Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64Float64
10.0300.015.0810.5974.92890.328593100.00.00.098.02.0-0.000359133
20.0235837300.00815.0810.614.92950.32863399.99997.74991e-67.74991e-698.00011.99994-0.000359353
30.170319300.05714.9999810.6964.933220.32888199.99965.59904e-55.59902e-598.00041.9996-0.000360725
40.470319300.15714.9998810.8714.940850.3293999.99890.0001547330.00015473198.00111.99889-0.000363544
50.770319300.25714.9997811.0454.948530.32990299.99810.0002536290.00025362598.00181.99817-0.000366381

A standard plotting utilitiy is provided; no effort was put on automatic dimensioning of the axis because this is problem-specific and even very personal to the researcher. Instead, handles to the figure and axes are provided so that anyone can customize the default plot.

fig, ax, lx = AuChimiste.plot(model, sol; xticks = T_ini:10:T_end)
# Please see the sources for details of hidden code here...

For more details, please check the manual or a more elaborated tutorial.

Database manipulations

add_load_path(".")
load_path()
4-element Vector{String}:
 "/home/runner"
 "/home/runner/work/AuChimiste.jl/AuChimiste.jl"
 "/home/runner/work/AuChimiste.jl/AuChimiste.jl/data"
 "/home/runner/work/AuChimiste.jl/AuChimiste.jl/docs/build/basics/"
reset_load_path()
load_path()
3-element Vector{String}:
 "/home/runner"
 "/home/runner/work/AuChimiste.jl/AuChimiste.jl/data"
 "/home/runner/work/AuChimiste.jl/AuChimiste.jl/docs/build/basics"