Tuning a parameter against measured data

August 28, 2026

"Recovering structure from data" and its follow-up covered a model you don't know the shape of yet, letting a CSV propose the nodes and equations themselves. This page is the opposite situation: you already know the equations, you're just not sure about a couple of constants in them. That's a much more common gap in practice, and Konjugate has a dedicated feature for closing it: mark a parameter "Tunable," import measured data, and let a real optimizer search for the values that fit, on an edge's own parameters and a node's own source-term parameters alike, together in one pass.

The example reuses "Getting started"'s hot plate and room air, hand-built the same way, except this time the cooling relationship is written as a genuine Newton's-law-of-cooling term, rate × (plate − room), Room air also gets its own constant heat-gain source term, and both get a deliberately wrong starting guess.

Marking a parameter tunable

Every edge parameter already has a Mode of Constant or Live. A second, independent checkbox sits below it in the same relationship editor:

The Cooling relationship's parameter row, with a wrong initial guess and Tunable unchecked
k starts at 0.02. The equation itself, k · (sourcePlateTemperature − targetRoomTemperature), is a real cooling law, not a placeholder.

Checking Tunable (fitting target) reveals fitting bounds, a minimum and maximum the optimizer is allowed to search between:

The same parameter row with Tunable checked and fitting bounds set
Bounds of [0, 0.2] give the optimizer plenty of room to move away from the wrong 0.02 guess.

Tunable is unrelated to Live: a live parameter is meant to be dragged on a slider mid-run, so it has no fixed baseline worth calibrating beforehand, and the editor disables Tunable while Mode is Live for exactly that reason. A parameter can be tunable, live, both or neither.

Source-term parameters can be tunable too

Tunable isn't limited to a relationship's own parameters. A node's own source term, the same place a self-contained physics term lives, independent of any edge, gets an identical Parameters section in its own editor, with the identical Tunable checkbox:

Room air's own source term, with a wrong initial guess and Tunable unchecked
Ambient gain starts at 0: no assumed background heating at all, inside a source term whose equation is just ambientGain, a constant contribution to Room air's own temperature.
The same source-term parameter row with Tunable checked and fitting bounds set
Bounds of [-1, 1] cover both a heat gain and a heat loss, since the wrong 0 guess gives no hint which direction the real value lies in.

Room air's Cooling edge already pulls heat from the hot plate; this source term adds a second, independent effect: a constant ambient gain unrelated to the plate at all, the kind of background term real measured data often needs to account for. Nothing about marking it tunable differs from an edge parameter: same checkbox, same fitting-bounds fields, same underlying mechanism.

What the wrong guesses predict

Before touching the tuning feature at all, it's worth seeing what the wrong guesses actually produce. Running the model for 40 seconds:

The 40-second run with both wrong guesses: Room air reaches only 58.5
Room air: 58.5495. With ambientGain still at its wrong guess of 0, this run is identical to a Cooling-only model: the source term contributes nothing yet.

The Tuning panel

Tuning, its own button in the toolbar, opens Digital twin tuning. Choosing a CSV of measured data shows what it found:

The Tuning panel after loading a CSV: column mapping and both tunable parameters
roomTemperature matched Room air's Temperature state by exact name. Both Cooling · k and Room air · source term · ambientGain are listed, each with its own current value and bounds.

Mapping here is existing-states-only: unlike causal inference, there's no "create a new node" option, since fitting a parameter requires a model that already exists to fit it against. A CSV column with no matching state by that exact name simply isn't usable, rather than prompting to create something new. Tunable parameters lists every tunable parameter found anywhere in the model, on a relationship or a source term alike, not just one at a time; the optimizer fits all of them together against the same measured signal. The Optimizer dropdown offers nine real backends, five derivative-free (BOBYQA, COBYLA, Nelder-Mead, PRAXIS, Subplex, plus the global ISRES) and three gradient-based (SLSQP, MMA, CCSAQ), all running through NLopt against the actual simulator, not a surrogate model.

Running the fit

Run fit simulates the model over and over, each time with different values for k and ambientGain together, scoring each attempt against the measured CSV until it converges:

The review step after a completed fit: k moved from 0.02 to 0.0498876, ambientGain from 0 to 0.299326
Converged (parameterToleranceReached), final loss 4.441e-21. The CSV was generated from true values of 0.05 and 0.3, so both recovered values are a genuine joint recovery, not a coincidence.

Both parameters get their own row with their own before/after values, checked independently. An unchecked row (which happens automatically when a fit diverges to a non-finite value for that specific parameter) is left out of the apply step rather than blocking the others. The comparison plot underneath shows measured against simulated for the fitted run, with a residual sub-plot beneath it: consistent blue for simulated and orange for measured throughout, so the two are easy to tell apart even when they nearly overlap.

Apply fitted values commits the checked rows through the same validate-then-apply pipeline every other model edit in this series has used, as one undoable step: both parameters land together, not one edit each. Konjugate is explicit about what that does and doesn't touch: "Fitted parameters applied. Use Save As to keep this as a new project -- the original is untouched until you do."

The result, with the real constants

Running the identical 40-second simulation again, changed only by those two parameters:

The 40-second run with the fitted values: Room air now reaches 85.7
Room air: 85.6733, matching the measured data this was fitted against far more closely than the original guesses ever did.

Where to go from here

"Getting started" covers building this same hot plate and room air model from nothing. "Recovering structure from data" covers the other end of working with measured data: not knowing the equation at all, rather than knowing it and needing a few numbers inside it.


Follow Konjugate on LinkedIn for updates, or subscribe via RSS.

Comments