Extracting Main-Peak Parameters¶
In isothermal heat-flow calorimetry of cementitious materials, the main hydration peak (driven by the silicate reaction) is characterized by parameters such as the maximum slope, the mean slope of the ascending flank, and the times at which these features occur.
Measurement.get_mainpeak_params() extracts these parameters in a single call and returns
them in a pandas.DataFrame. Two analysis modes are available:
method="mean"(default) — combines a maximum-slope and a mean-slope (flank-tangent) analysis.method="ascending"— uses a first-ascending-slope-to-fraction approach, which can be more robust for noisy or asymmetric peaks.
This tutorial walks through both, starting from a minimal call and progressively customizing the processing parameters.
from pathlib import Path
import matplotlib.pyplot as plt
from calocem import Measurement, ProcessingParameters
# Paths are relative to this notebook's directory (docs/).
datapath = Path("../calocem/DATA")
metadatapath = Path("../calocem/METADATA/metadata_dummy.csv")
tam = Measurement(
folder=datapath,
regex=r".*peak_detection_example2.*",
show_info=False,
metadata_path=metadatapath,
metadata_id_column="file_name",
)
A minimal call¶
Without any custom processing parameters, get_mainpeak_params() runs both the maximum-slope
and mean-slope analyses on the loaded sample. Setting show_plot=True and plot_type="mean"
produces a flank-tangent visualization alongside the returned DataFrame.
mainpeak = tam.get_mainpeak_params(show_plot=True, plot_type="mean")
sorted_cols = sorted(mainpeak.columns)
display(sorted_cols)
['astm_time_s', 'curvature_at_max_slope', 'dorm_time_s', 'flank_end_value', 'flank_start_value', 'gradient_from_max_slope', 'gradient_of_mean_slope', 'max_slope_time_s', 'mean_slope_time_s', 'normalized_heat_at_onset_time_mean_slope_abscissa_j_g', 'normalized_heat_at_onset_time_mean_slope_dormant_j_g', 'normalized_heat_flow_w_g_astm', 'normalized_heat_flow_w_g_at_max_slope', 'normalized_heat_flow_w_g_at_mean_slope', 'normalized_heat_flow_w_g_at_peak', 'normalized_heat_flow_w_g_dormant', 'normalized_heat_j_g_astm', 'normalized_heat_j_g_at_max_slope', 'normalized_heat_j_g_at_mean_slope', 'normalized_heat_j_g_at_onset_time_max_slope', 'normalized_heat_j_g_at_peak', 'normalized_heat_j_g_dormant', 'onset_time_min_from_mean_slope', 'onset_time_min_max_slope', 'onset_time_s_from_max_slope', 'onset_time_s_from_mean_slope', 'onset_time_s_from_mean_slope_abscissa', 'onset_time_s_max_slope_abscissa', 'peak_time_s', 'sample', 'sample_short']
Customizing the processing¶
Real calorimetry data is rarely clean enough for the defaults to give the best result.
Three groups of ProcessingParameters typically matter for slope analysis:
- Smoothing —
median_filterremoves single-point noise;spline_interpolationsmooths the first and second derivatives, which the slope detection relies on. - Cutoff —
cutoff.cutoff_mindiscards the early measurement period (mixing artefacts, thermal equilibration) so they don't bias the analysis. - Flank fraction —
slope_analysis.flank_fraction_start/_enddefine which portion of the ascending flank counts as "the slope" for the mean-slope (flank-tangent) analysis.
processparams = ProcessingParameters()
processparams.slope_analysis.flank_fraction_start = 0.85
processparams.slope_analysis.flank_fraction_end = 0.95
mainpeak_custom = tam.get_mainpeak_params(
processparams=processparams,
show_plot=True,
plot_type="mean",
)
Alternative analysis: method="ascending"¶
For noisy or asymmetric peaks, where a symmetric flank fit struggles, the first-ascending-slope-to-fraction approach can be more robust. Instead of fitting a tangent to a defined fraction of the flank, the algorithm walks up the rising heat-flow signal until it reaches a target fraction of the peak maximum (or a target heat-flow delta) and returns the slope of that ascent.
The next example switches to the flank_detection3 dataset, which is well-suited to
demonstrating this analysis, and uses parameters tuned for first-ascending detection.
tam_flank = Measurement(
folder=datapath,
regex=r".*flank_detection3.*",
show_info=False,
)
params_ascending = ProcessingParameters()
params_ascending.cutoff.cutoff_min = 60
params_ascending.spline_interpolation.apply = True
params_ascending.spline_interpolation.smoothing_1st_deriv = 5e-12
params_ascending.slope_analysis.flank_fraction_start = 0.2
params_ascending.slope_analysis.flank_fraction_end = 0.9
params_ascending.slope_analysis.first_ascending_fraction_of_max = 0.6
params_ascending.slope_analysis.first_ascending_range_method = "delta"
params_ascending.slope_analysis.first_ascending_delta_y_w_g = 1e-3
params_ascending.slope_analysis.flexible = 0.0
mainpeak_ascending = tam_flank.get_mainpeak_params(
processparams=params_ascending,
show_plot=True,
method="ascending",
)
The DataFrame returned in "ascending" mode contains a different set of columns from the
default "mean" mode. The slope and the timestamps that bracket the detected ascending
segment are reported alongside the threshold and fraction-of-max settings used to find them.
first_ascending_cols = [
"sample_short",
"gradient_of_first_ascending_slope_to_fraction_of_max",
"onset_time_s_from_first_ascending_slope",
"first_ascending_slope_start_time_s",
"first_ascending_slope_end_time_s",
"normalized_heat_flow_w_g_threshold_for_first_ascending_slope",
]
available = [c for c in first_ascending_cols if c in mainpeak_ascending.columns]
mainpeak_ascending[available]
| sample_short | gradient_of_first_ascending_slope_to_fraction_of_max | onset_time_s_from_first_ascending_slope | first_ascending_slope_start_time_s | first_ascending_slope_end_time_s | normalized_heat_flow_w_g_threshold_for_first_ascending_slope | |
|---|---|---|---|---|---|---|
| 0 | flank_detection3 | 4.115554e-08 | 17437.940997 | 10900.0 | 42630.0 | 0.001236 |
Closing notes¶
Inspect mainpeak_custom.columns or mainpeak_ascending.columns to see the full set of
values returned for your data — many additional intermediate quantities are available
alongside the slopes and timestamps.
Further customization is available through ProcessingParameters.plotting (figure size,
axis limits, legend position, plot titles drawn from metadata columns, monochrome styling)
and through save_plot=True / plotpath=... for batch plot export. Sample-specific
parameter overrides are supported via processparams.add_sample_param_rule(regex, {...}).
Related methods for other aspects of the heat-flow signal:
Measurement.get_peaks()— peak detection across the full time series.Measurement.get_peak_onsets()— gradient-threshold-based onset detection.Measurement.get_maximum_slope()— maximum-slope detection only, without the combined output.