← All examples
52

Canonical Grid model

Materials starter - Transformation fit and residuals

Fit synthetic noisy transformed fractions, expose residual checks, and compare initial-guess scenarios.

Scale
Small
Source
materials-transformation-fit.grid
Length
108 lines
Collection
Advanced language
Level
Intermediate
Runtime
Portable
Version
1.0.0
materials-transformation-fit.grid
Get Grid
MODEL "Materials starter - Transformation fit and residuals"
DESCRIPTION "Fit synthetic noisy transformed fractions, expose residual checks, and compare initial-guess scenarios."
VERSION "1.0.0"
TAGS "materials", "starter", "kinetics", "regression", "residuals"

strict

input InitialTau_s = 80
  VALIDATE BETWEEN 1 AND 1000 MESSAGE "Use a positive initial characteristic time from 1 to 1000 seconds."
input InitialExponent = 2
  VALIDATE BETWEEN 0.1 AND 8 MESSAGE "Use a positive initial exponent from 0.1 to 8."
input PredictionTime_s = 150
  VALIDATE BETWEEN 0 AND 10000 MESSAGE "Prediction time must be nonnegative; this example accepts up to 10000 seconds."

# Eight synthetic observations; not a material calibration or independent replication.
# Baseline checkpoint: tau about 100.037954 s, exponent about 2.490370,
# SSE about 5.713470247e-5; compare residuals as well as coefficients.
Times_s = [20, 40, 60, 80, 100, 120, 160, 200]
Fractions = [0.019729493624221495, 0.093241305536341912, 0.24735023991690364, 0.4338490391627452, 0.63512055882855767, 0.78949712943053385, 0.96176446053896381, 0.99550651072335383]

# Fit logarithms so the recovered tau and exponent stay positive. This changes
# parameter coordinates, not the unweighted squared-residual objective.
# All observations have appreciable transformed fractions; explicit grouping
# gives a negated positive power instead of a negative fractional-power base.
LogFit = CURVE_FIT(LAMBDA(time_s, log_tau, log_exponent, 1 - EXP(-((time_s / EXP(log_tau))^EXP(log_exponent)))), Times_s, Fractions, [LN(InitialTau_s), LN(InitialExponent)], 1e-12, 400)
FittedTau_s = EXP(INDEX(LogFit, 1, 1))
FittedExponent = EXP(INDEX(LogFit, 1, 2))
PredictedFractions = MAP(Times_s, (time_s) => -EXPM1(-((time_s / FittedTau_s)^FittedExponent)))
Residuals = MAP(SEQUENCE(1, 8), (observation) => INDEX(Fractions, 1, observation) - INDEX(PredictedFractions, 1, observation))
ResidualSSE = SUMSQ(Residuals)

output CharacteristicTime_s = FittedTau_s
output AvramiExponent = FittedExponent
output SumSquaredResiduals = ResidualSSE
output RootMeanSquaredResidual = SQRT(ResidualSSE / 8)
output ResidualStandardError = SQRT(ResidualSSE / 6)
output Prediction = -EXPM1(-((PredictionTime_s / FittedTau_s)^FittedExponent))
output HalfTransformationTime_s = FittedTau_s * (LN(2)^(1 / FittedExponent))
output ResidualValues = Residuals
output Assumptions = "Unweighted least squares for X(t)=1-exp(-((t/tau)^n)); positive tau and n; synthetic data in seconds and fractions. Residuals are measured minus fitted. RMSE divides SSE by 8; residual standard error divides by 8-2. Neither is coefficient uncertainty or proof of a transformation mechanism."
output ScenarioGuide = "Save Baseline, then try InitialTau_s=120 and InitialExponent=3 as a Different initial guess scenario. Compare the fitted coefficients and SSE, not just a smooth curve. Restore Baseline or reset inputs. Predictions outside the observed 20-200 s window are extrapolation."

FitData = MAP(SEQUENCE(1, 8), (observation) => {
  time_s: INDEX(Times_s, 1, observation),
  measured: INDEX(Fractions, 1, observation),
  fitted: INDEX(PredictedFractions, 1, observation),
  residual: INDEX(Residuals, 1, observation)
})

A1 = "Fitted characteristic time (s)"
B1 = CharacteristicTime_s
A2 = "Fitted exponent"
B2 = AvramiExponent
A3 = "Sum of squared residuals"
B3 = SumSquaredResiduals
A4 = "RMSE (fraction; divisor 8)"
B4 = RootMeanSquaredResidual
A5 = "Residual standard error (fraction; divisor 6)"
B5 = ResidualStandardError
A6 = "Predicted fraction at selected time"
B6 = Prediction
A7 = "Half-transformation time (s)"
B7 = HalfTransformationTime_s
A9 = Assumptions
A10 = ScenarioGuide

Charts_1!type = "charts"
Charts_1!config = <toml>
  version = 1
  kind = "charts"
  title = "Transformation fit"
  [layout]
  title = "Synthetic transformed fractions"
  x_axis = "Time (s)"
  y_axis = "Transformed fraction"
  [[charts]]
  type = "scatter"
  bind = "FitData"
  x = "time_s"
  y = "measured"
  label = "Synthetic observations"
  [[charts]]
  type = "line"
  smooth = false
  bind = "FitData"
  x = "time_s"
  y = "fitted"
  label = "Fit at observation times"
</toml>

Charts_2!type = "charts"
Charts_2!config = <toml>
  version = 1
  kind = "charts"
  title = "Transformation residuals"
  [layout]
  title = "Measured minus fitted fraction"
  x_axis = "Time (s)"
  y_axis = "Residual (fraction)"
  [[charts]]
  type = "scatter"
  bind = "FitData"
  x = "time_s"
  y = "residual"
  label = "Residual"
</toml>

END MODEL