Heat-source Calibration
This tutorial calibrates the projected axial distribution of an SS316L projectedGaussian source against melt-pool depths measured from five SS316L single tracks on a bare plate. It demonstrates the complete calibrateHeatSource workflow: render and run trial cases, extract maximum liquidus melt-pool depth, infer a local shape parameter for each condition, fit the global \(A,B\) closure, and generate uncertainty and report products.
Read Projected Heat-source Calibration for the derivation, likelihood, robust global fit, bootstrap interval, cache behavior, and transfer rules used by the command.
Physical setup
The calibration data are SS316L single tracks produced on a bare plate. experiments.yml contains two measured melt-pool depths at each of five powers; scan speed and measured D4σ spot diameter are constant:
| Power (W) | Speed (mm/s) | D4σ spot diameter (µm) | Measured depth (µm) |
|---|---|---|---|
| 187.5 | 500 | 109.69 | 87.05, 98.10 |
| 300.0 | 500 | 109.69 | 168.57, 190.67 |
| 412.5 | 500 | 109.69 | 272.19, 264.07 |
| 525.0 | 500 | 109.69 | 359.24, 353.71 |
| 637.5 | 500 | 109.69 | 464.25, 467.01 |
The AdditiveFOAM setup contains:
- SS316L properties are included from
$ADDITIVEFOAM_ETC/materials/SS316L.cfg. - The source is a transient
projectedGaussian. For each experiment, the driver converts the measured D4σ diameter to a 2σ radius and assigns that value to both lateral dimensions. The supplied 109.69 µm D4σ diameter therefore produces(54.845 54.845)µm lateral dimensions. Initial depth is 30 µm andnPointsis(10 10 10). - The cylindrical Kelly model uses
eta0 0.27,etaMin 0.27, and an effectively zero aspect-ratio switch. - A 2 mm linear track is rendered at the experimental power and 0.5 m/s speed.
targetCellLoadrefinement follows the beam, anddynamicMeshDictpermits one refinement level.- Fluid flow is disabled with
nOuterCorrectors 0. meltPoolDimensionsrecords the material-derived SS316L liquidus isotherm.decomposeParDictuses eight subdomains withscotchdecomposition.
Run
Copy the tutorial to a writable run directory, then start or resume the campaign:
mkdir -p "$FOAM_RUN/AdditiveFOAM"
cp -r "$ADDITIVEFOAM_TUTORIALS/heatSourceCalibration" \
"$FOAM_RUN/AdditiveFOAM/heatSourceCalibration"
cd "$FOAM_RUN/AdditiveFOAM/heatSourceCalibration"
calibrateHeatSource --config config.yml
Do not run the campaign inside $ADDITIVEFOAM_TUTORIALS; it writes cases, caches, and reports beneath the copied tutorial. Each AdditiveFOAM trial uses eight MPI ranks.
Important inputs
| File | Purpose |
|---|---|
experiments.yml |
Process conditions and repeated measured melt-pool depths |
config.yml |
Case paths, trial grid, posterior sampling, refinement tolerances, and reporting |
template/constant/heatSourceDict |
Projected source, absorption, dimensions, sampling, and calibration placeholders |
template/constant/scanPath |
Rendered beam power, track, and velocity |
template/constant/transportProperties |
SS316L material configuration |
template/system/controlDict |
Run time, write interval, and meltPoolDimensions output |
template/system/decomposeParDict |
Parallel decomposition for each trial |
Workflow
The calibration driver treats every distinct parameters mapping in experiments.yml as an independent process condition. Power_W, Speed_mm_s, and Spot_Size_microns are required by the renderer and normalization logic.
Rendered trial cases
The heat-source radius and trial value appear once in template/constant/heatSourceDict:
heatSourceRadius <<heatSourceRadius>>;
projectedGaussianCoeffs
{
dimensions ($heatSourceRadius $heatSourceRadius 30.0e-6);
A 0.0;
B <<B>>;
transient true;
nPoints (10 10 10);
}
Setting \(A=0\) makes every trial exponent independent of transient melt-pool depth:
\[n=B,\qquad k=2^B.\]The scan path and run controls expose the remaining renderer tokens:
Mode X Y Z Power Param
1 0.000 0.000 0 0 0
0 0.002 0.000 0 <<power>> <<velocity>>
endTime <<endTime>>;
writeInterval <<writeInterval>>;
The driver converts the full D4σ diameter from micrometres to a 2σ radius in metres and assigns it to both lateral dimensions. Measured and simulated depths use the same normalization:
\[x=\frac{d}{D_{4\sigma}/2} =\frac{d}{\min(\mathrm{dimensions}_x,\mathrm{dimensions}_y)}.\]It also converts speed to m/s, renders the track, computes its travel time, and uses that time for both run-control placeholders.
Calibration configuration
The supplied config.yml specifies:
- Trial values \(B=0,1,\ldots,9\) for each experimental condition.
- Bounds \(0\leq B\leq9\) and at most ten simulations per condition.
- A 1 µm depth-match tolerance and 0.15 local-posterior standard-deviation tolerance.
- A 1,000-point PCHIP surrogate grid.
- 2,000 retained posterior draws after 1,000 tuning steps, using eight sampling cores.
- 1,000 global uncertainty-propagation refits.
- Automatic PDF and CSV report generation.
Because the ten initial values equal the ten-simulation limit, this tutorial runs a fixed grid of 50 AdditiveFOAM cases.
Campaign stages
For each process condition, calibrateHeatSource:
- Creates
campaign/cases/<condition>/B<trial>/fromtemplate/. - Renders the 2σ lateral dimension, trial value, power, velocity, end time, and write interval.
- Runs
blockMesh,decomposePar, and paralleladditiveFoamthrough the caseAllrunscript. - Reads the liquidus CSV written by
meltPoolDimensionsand records the largest depth in the scan. - Updates
campaign/simulations.ymlimmediately so an interrupted campaign can resume. - Builds the PCHIP response surrogate and samples the local posterior.
After all local calibrations are current, it performs the robust uncertainty-weighted global fit and generates the report.
Adapt to another data set
- Copy the tutorial to a new writable directory and select a new
paths.campaignlocation. - Replace
experiments.ymlwith repeated depth measurements and complete process-condition metadata. - Enter the measured full D4σ diameter as
Spot_Size_microns; the driver renders its 2σ radius intodimensions. - Update the case template’s material, lateral source profile, absorption model, mesh, boundary conditions, and parallel decomposition.
- Preserve each required placeholder exactly once and keep \(A=0\) during local trials.
- Run the campaign and inspect every local response before adopting the fitted source slope and intercept.
Outputs
The completed directory has this structure:
campaign/
├── cases/
│ └── P187p5_V500_D109p69/
│ ├── B0/
│ ├── B1/
│ └── ...
├── simulations.yml
├── calibration_state.yml
├── calibration_fit.yml
└── reports/
├── calibration_report.pdf
└── calibration_summary.csv
| Output | Contents |
|---|---|
cases/.../log.run |
Complete solver output for one trial |
cases/.../metrics.yml |
Rendered inputs, selected isovalue, time and maximum length, width, and depth |
simulations.yml |
Trial values and simulated depths for every process condition |
calibration_state.yml |
Local posterior summaries and retained samples |
calibration_fit.yml |
Global coefficients, residual diagnostics, calibrated range, covariance, and bootstrap intervals |
calibration_summary.csv |
One row per experiment for downstream analysis |
calibration_report.pdf |
Response curves, measured depths, local posteriors, final fit, and numerical summary |
With keep_successful: false, the rendered dictionaries, log, post-processing data, and metrics remain, while processor and numeric time directories are removed after a successful trial.
Apply the fitted coefficients
Open campaign/calibration_fit.yml and inspect A, B, the bootstrap intervals, x_min, x_max, and weighted residuals. Also inspect the local response and posterior plots in the PDF; a narrow-looking global fit does not repair a non-monotonic or poorly resolved AdditiveFOAM response curve.
The coefficients are used in a production projected source as:
projectedGaussianCoeffs
{
dimensions (54.845e-6 54.845e-6 30e-6);
A <source-slope>;
B <source-intercept>;
transient true;
nPoints (10 10 10);
}
The command uses Spot_Size_microns: 109.69 as the full measured D4σ diameter, computes the 54.845 µm 2σ heat-source radius, and renders it into both runtime lateral dimensions. Measured and simulated depths are normalized with this same radius:
Insert A and B from calibration_fit.yml directly as <source-slope> and <source-intercept>.