Most engineers doing single-phase convection calculations pick one correlation and move on. Usually Dittus-Boelter, because it’s in every textbook and everyone knows it. Sometimes Gnielinski, if they’re careful. Rarely anything else.
I understood why. You’re not writing a dissertation on Nusselt number correlations — you’re sizing a heat exchanger or checking a thermal margin. The correlation is a means to an end.
But that habit started bothering me. Not because Dittus-Boelter is wrong — it isn’t, within its range — but because the choice is invisible. It’s not documented. It’s not justified. And when conditions change (higher Reynolds number, different fluid, different geometry), the engineer who comes next has no idea why that correlation was used in the first place, or whether it’s still appropriate.
That’s the problem htcie is built to solve.
What the engine actually does
htcie is a correlation decision engine for single-phase forced convection heat transfer. You give it your engineering state — fluid properties, geometry, flow conditions, boundary conditions — and it runs every applicable correlation, ranks them with a deterministic scoring model, and reports how confident you should be in the result.
The output answers five questions directly:
- Which correlations are valid for these conditions?
- What Nu or h does each valid correlation predict?
- Which correlation is recommended, and why?
- How much do the candidate methods disagree?
- How confident should I be?
That last question is answered by measuring inter-method spread — not by trusting any single correlation’s stated accuracy. If Gnielinski, Petukhov, and Sieder-Tate agree to within 3%, you have a high-confidence result. If they spread across 20%, you have a medium- or low-confidence result regardless of which one you pick.
The seven-layer pipeline
Every request through htcie flows through seven stages, each independently testable, each producing a serializable output:
EngineeringState
│
▼
1. State — computes Re, Pr, Gz, x/D, S_T/D, S_L/D as Pydantic fields
2. Registry — loads correlation metadata from YAML; immutable after load
3. Applicability — filters by geometry, Re, Pr; records every exclusion reason
4. Evaluation — dispatches to domain modules; returns Nu per correlation
5. Ranking — scores each result with ScoringWeightsV1; returns sorted list
6. Confidence — analyses spread; classifies high / medium / low
7. Explanation — builds human-readable audit trail; packages into HtcieReport
The first thing I want to point out: exclusion reasons are first-class outputs. When a correlation doesn’t apply to your conditions, the engine tells you exactly why — Re out of range, wrong geometry type, Pr below minimum. You can see which correlations were considered and rejected, not just which ones made it through. That’s not a nice-to-have; it’s what separates an auditable tool from a black box.
The CLI, GUI, and REST API are all clients of the same run_evaluation pipeline. There is no separate code path per interface.
The correlation catalog
The current catalog covers 13 correlations across three families:
Internal forced convection (circular tubes): Dittus-Boelter (1930), Sieder-Tate (1936), Gnielinski (1976), Petukhov (1970), Shah laminar (1978), Churchill-Ozoe laminar (1973).
External forced convection: Churchill-Bernstein (1977, cylinder in crossflow), Hilpert (1933), Žukauskas (1972), Pohlhausen flat plate laminar (1921), turbulent flat plate (Incropera/DeWitt).
Tube banks: Žukauskas (1972), Grimison (1937).
Every correlation entry in the YAML catalog carries the same fields: formula (LaTeX), validity bounds (Re, Pr, geometry), stated assumptions, bibliographic source with DOI where available, and notes on when to prefer it. This isn’t documentation for its own sake — the applicability engine reads those validity bounds at runtime to decide whether to include or exclude the method.
The validation work, and what honesty looks like
This is the part I’m most proud of, and the part that took the most time.
Before I shipped anything, I audited every correlation against Incropera Fundamentals of Heat and Mass Transfer (7th edition) as the primary textbook reference, and then went further — tracking down five original primary-source papers to verify what the textbooks actually say.
I found four substantive formula errors:
- Žukauskas cylinder Pr exponent: hardcoded
n=0.36for all fluids. The correct value isn=0.37for Pr ≤ 10,n=0.36for Pr > 10. For gases (Pr ≈ 0.7), the previous value was simply wrong. - Žukauskas tube bank coefficients: the tabular C/m values for multiple Re bands were incorrect against Incropera Table 7.5. The Re 100–1,000 range had unsourced values that I replaced with the correct “approximate as single cylinder” behavior. The staggered S_T/S_L ≥ 2 case (C=0.40) was missing entirely.
- Dittus-Boelter Pr lower bound:
pr_minwas 0.7; Incropera Eq. 8.60 states 0.6. - Sieder-Tate Pr upper bound:
pr_maxwas 17000; Incropera Eq. 8.61 states 16700.
Finding these required actually reading the correlation entries in the original sources — not trusting what I’d written during the initial implementation.
Honest uncertainty reporting, and what it cost
The harder problem was uncertainty bounds. Every correlation reports a literature accuracy (±%). These feed into the ranking engine’s uncertainty score.
I wanted to flag honestly which of these could be traced to a primary source and which were textbook-consensus estimates. The result:
- Petukhov’s ±5–6%/±10% is the only bound I confirmed directly from a primary source (Petukhov 1970, p. 523). The bound splits by Pr: ±5–6% for Pr 0.5–200, ±10% for Pr 0.5–2,000. Earlier I had it as a uniform ±6% across all Pr — wrong.
- Churchill-Bernstein states no ±% bound anywhere in the original paper. What it says (p. 304) is that Eq. (9) “is proposed as a lower bound for computed and experimental values.” Actual Nu typically falls above this correlation. The YAML previously said “±20% reported in original paper” — incorrect. Now it correctly surfaces the lower-bound characterization in the explanation layer when this correlation is used.
- Gnielinski ±10% comes from Cengel & Ghajar 5th ed., not from the 1976 paper or the available 2013 update. Attribution corrected.
- All 13 correlations now carry explicit
UNCERTAINflags where the stated uncertainty could not be confirmed from a primary source, with notes explaining why (paywalled, not stated in original paper, analytical solution only).
This matters because the confidence class the engine reports is based on inter-method spread, not on any single correlation’s absolute accuracy claim. An UNCERTAIN flag on an uncertainty bound doesn’t break the tool — it informs the user about what is and isn’t directly verified.
The ranking model
Each applicable correlation receives a score from ScoringWeightsV1, which combines eight factors. The score is deterministic: same inputs, same weights, same ranking. No machine learning. No stochastic tie-breaking.
The score factors weight things like validity fit (how centered the current operating point is within the correlation’s stated Re/Pr range), stated literature uncertainty, pedigree (year, usage prevalence), geometry match quality, and extrapolation distance. The full breakdown is included in every HtcieReport — not just the final score, but each factor’s contribution.
Versioning the weight schema was a deliberate decision. If scoring policy changes in the future, past reports remain reproducible against the version of ScoringWeightsV1 that generated them. That’s not hypothetical future-proofing — it’s how engineering calculations should work.
Challenges along the way
The dev log is honest about what broke during development.
The most consequential bug was a Gnielinski off-by-one: the applicability check used if re <= 3000 instead of if re < 3000. At exactly Re = 3,000, the correlation passed applicability but then crashed the evaluator because Gnielinski is not defined there (it needs Re > 3,000 to keep the friction factor meaningful). A single character difference, a valid-looking input, a crash. Fixed by correcting the boundary to if re < 3000.
The tube bank arrangement field was added late, after I realized the original implementation inferred arrangement from S_T/S_L < 1.0. That heuristic made the S_T/S_L ≥ 2 staggered case permanently unreachable — a valid physical configuration that simply couldn’t be reached through the API. I added an explicit arrangement: Optional[Literal["inline", "staggered"]] to the geometry model, required when geometry_type="tube_bank". It’s more input, but the behavior is now correct and unambiguous.
The film temperature advisory gap was more subtle: the external convection correlations that require wall temperature for a Prandtl correction were silently proceeding without it. Now the pipeline emits a warnings list when external geometry is used without wall_temperature provided. Silent behavior in an engineering tool is a safety problem.
These weren’t theoretical edge cases I hunted for — they surfaced through testing, through trying to run actual inputs, through reading primary sources carefully enough to catch what was wrong.
The output: a self-contained audit trail
Every evaluation produces an HtcieReport — a Pydantic model that contains the full computation record:
- Input engineering state and all derived dimensionless groups (Re, Pr, Gz, x/D, …)
- All candidate correlations, their applicability status, and exclusion reasons
- Nu and h for each evaluated correlation, with uncertainty bands
- Full ranking breakdown per correlation (per-factor scores and weights)
- Inter-method spread statistics (mean, σ, relative spread)
- Confidence class (high / medium / low)
- Structured explanation: recommended method, why, what was excluded, key assumptions, extrapolation warnings
Reports serialize to JSON, Markdown, or self-contained HTML. The JSON payload is also what the REST API returns from POST /evaluate. There is no separate serialization path for the API.
The Python API looks like this:
from htcie.core.pipeline import run_evaluation
from htcie.core.registry import CorrelationRegistry
from htcie.core.state import EngineeringState, FluidProperties, Geometry, BoundaryConditions, FlowState
state = EngineeringState(
fluid=FluidProperties(density=998.2, viscosity=1.002e-3,
thermal_conductivity=0.598, heat_capacity=4182),
geometry=Geometry(geometry_type="circular_tube",
characteristic_length=0.025, hydraulic_diameter=0.025),
boundary=BoundaryConditions(boundary_type="constant_wall_temperature"),
flow=FlowState(velocity=0.4),
)
registry = CorrelationRegistry()
registry.load_from_dir("data/correlations")
report = run_evaluation(state, registry)
print(report.explanation.to_text()) # recommendation with reasoning
print(report.confidence) # "high", "medium", or "low"
The CLI works for anyone who doesn’t want to write code:
htcie evaluate input.json
htcie evaluate input.json --output report.md --markdown
And there’s a NiceGUI web interface for interactive use.
Who this is for
The beachhead audience is thermal and process engineers who want to make correlation selection an explicit, documented, reproducible step rather than a silent assumption. Methods engineers and standards teams who need to demonstrate that their analysis choices are defensible. Engineering automation teams building calculation pipelines that have to pass audit.
Dittus-Boelter will still be the right answer in a lot of cases — but now you’ll know it’s right because three other methods agree with it to within 5%, not because it was the first one someone remembered.
What comes next
The current catalog covers single-phase forced convection. Boiling and condensation are the primary domain targets beyond that. The confidence model will improve when benchmark-calibrated uncertainty bounds replace the textbook-consensus estimates currently flagged UNCERTAIN. A built-in fluid property library would eliminate a common source of user error — right now you supply density, viscosity, conductivity, and heat capacity explicitly.
The code is open under AGPL v3. The correlation catalog, scoring weights, and audit trail are all visible. If a result looks wrong, you can trace exactly why it was produced.
That openness is not incidental. It’s the point.
The full documentation covers the correlation catalog, API reference, CLI usage, and report format.