Temperature Programs

Real thermal analysis experiments use multi-segment temperature profiles: ramp to a target, hold isothermally, ramp again, or cool back down. The TemperatureProgram class lets you define arbitrary piecewise-linear furnace programs.

Concepts

A temperature program is a sequence of segments, each described by a TemperatureSegment:

Segment type

rate_C_per_min

T_target_C

hold_min

Heating ramp

> 0

target temperature

ignored

Cooling ramp

< 0

target temperature

ignored

Isothermal hold

0

current temperature

hold duration

The program starts at T_initial_C and executes each segment in order. Ramp segments heat or cool at a constant rate until the target is reached; hold segments keep the temperature constant for the specified duration.

Creating a program

Single ramp (legacy shortcut)

If you only need a single linear ramp, you can either use the legacy TASimulator parameters or the factory method:

from ta import TemperatureProgram

prog = TemperatureProgram.single_ramp(
    T_initial_C=50.0,
    T_final_C=600.0,
    rate_C_per_min=10.0,
)

Ramp – hold – ramp

A common pattern in TGA methods (e.g., ASTM E1131): ramp to an intermediate temperature, hold to allow equilibration, then ramp to the final temperature.

from ta import TemperatureProgram, TemperatureSegment

prog = TemperatureProgram(50.0, [
    TemperatureSegment(rate_C_per_min=10.0, T_target_C=200.0),
    TemperatureSegment(rate_C_per_min=0.0,  T_target_C=200.0, hold_min=30.0),
    TemperatureSegment(rate_C_per_min=5.0,  T_target_C=600.0),
])

print(f"Total time: {prog.total_time_s / 60:.0f} min")  # 15 + 30 + 80 = 125 min

Ramp – hold – ramp – cool

Add a cooling segment with a negative rate:

prog = TemperatureProgram(50.0, [
    TemperatureSegment(10.0, 300.0),                  # heat to 300
    TemperatureSegment(0.0,  300.0, hold_min=5.0),    # hold 5 min
    TemperatureSegment(5.0,  500.0),                   # heat to 500
    TemperatureSegment(-10.0, 200.0),                  # cool to 200
])

Inspecting a program

Segment boundaries

The segment_boundaries property returns (time_s, temperature_C) tuples at each transition point (including the initial state):

for i, (t, T) in enumerate(prog.segment_boundaries):
    print(f"  Boundary {i}: t = {t/60:.1f} min, T = {T:.0f} deg C")

Temperature at a given time

# Temperature at t = 120 s
T = prog.T_furnace_C(120.0)

The lookup is O(log n) in the number of segments, so it is fast even for complex programs.

Passing to the simulator

Pass the program via the temperature_program parameter. When provided, it overrides T_initial_C, T_final_C, and heating_rate_C_per_min:

from ta import TASimulator

sim = TASimulator(
    mechanism="gri30.yaml",
    sample_composition="C2H6:0.7, CH4:0.3",
    condensed_species=["C2H6", "CH4"],
    temperature_program=prog,
    dt=2.0,
)
result = sim.run()

The SimulationResult stores the program in its temperature_program attribute so downstream code can access segment boundaries for annotation or analysis.

Validation rules

The constructor validates each segment:

  • At least one segment is required.

  • Ramp direction: the sign of rate_C_per_min must match the direction from the current temperature to T_target_C (positive rate for heating, negative for cooling).

  • No zero-distance ramps: if T_target_C equals the current temperature, use rate_C_per_min=0 (a hold) instead.

  • Hold duration: isothermal segments (rate_C_per_min=0) require hold_min > 0.

# This raises ValueError: rate is positive but T_target < T_current
TemperatureProgram(200.0, [
    TemperatureSegment(10.0, 100.0),
])

Full example

from ta import (
    TASimulator,
    TemperatureProgram,
    TemperatureSegment,
    compute_tga, compute_dtg, compute_dta, compute_ms,
)
from ta.plotter import plot_ta_figure

prog = TemperatureProgram(50.0, [
    TemperatureSegment(10.0, 300.0),
    TemperatureSegment(0.0,  300.0, hold_min=5.0),
    TemperatureSegment(5.0,  500.0),
    TemperatureSegment(-10.0, 200.0),
])

result = TASimulator(
    mechanism="gri30.yaml",
    sample_composition="C2H6:0.7, CH4:0.3",
    condensed_species=["C2H6", "CH4"],
    temperature_program=prog,
    dt=2.0,
).run()

tga = compute_tga(result)
dtg = compute_dtg(tga, result.time_s)
dta = compute_dta(result)
ms  = compute_ms(result, top_n=5)

fig, _ = plot_ta_figure(result.furnace_temperature_C, tga, dtg, dta, ms)
fig.savefig("multistage.png", dpi=150)