Skip to content

Commit 09d148e

Browse files
committed
Add benchmark test for NAFEMS T4 (2D heat conduction with convection)
1 parent b736666 commit 09d148e

2 files changed

Lines changed: 161 additions & 0 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Benchmark Test — NAFEMS T4 (2D Heat Conduction with Convection)
2+
3+
## Purpose
4+
5+
This test replicates the NAFEMS T4 benchmark, also documented as a DIANA FEA tutorial
6+
([2DHeatTransferConvection.pdf](https://tutorials.dianafea.com/2DHeatTransferConvection.pdf))
7+
and by [Altair](https://help.altair.com/hwsolvers/os/topics/solvers/os/nafems_test_problem_t4_r.htm),
8+
and checks that `heatConductionScript` reproduces the published target temperature for a 2D
9+
steady-state conduction problem with a convective (Robin) boundary condition.
10+
11+
This is an independent reproduction of the NAFEMS T4 problem and is not sponsored, endorsed, or
12+
affiliated with NAFEMS.
13+
14+
## Problem setup
15+
16+
A 0.6 m by 1.0 m rectangular plate `ABCD` (`A` at the origin, `B` at `(0.6, 0)`, `C` at
17+
`(0.6, 1.0)`, `D` at `(0, 1.0)`), thermal conductivity `k = 52 W/(m·K)`, no internal heat
18+
generation:
19+
20+
| Edge | Boundary condition |
21+
| ----------- | ------------------------------------------------- |
22+
| AB (bottom) | Constant temperature, T = 100 °C |
23+
| AD (left) | Insulated (natural, zero-flux — left unspecified) |
24+
| CD (top) | Convection, h = 750 W/(m²·K), ambient T = 0 °C |
25+
| BC (right) | Convection, h = 750 W/(m²·K), ambient T = 0 °C |
26+
27+
The published target is the temperature at point E, located on edge BC at `(x = 0.6, y = 0.2)`:
28+
**18.3 °C**.
29+
30+
## Expected values
31+
32+
| Case | Temperature at E | Compared against | Tolerance |
33+
| -------------- | ---------------- | ----------------------- | --------- |
34+
| Mesh (12 × 20) | ≈ 18.26 °C | NAFEMS target (18.3 °C) | `0.1` °C |
35+
36+
## How to run
37+
38+
From the repository root:
39+
40+
```bash
41+
node tests/verification/benchmark/heatConduction2DNafemsT4/benchmark.test.js
42+
```
43+
44+
The `test` script in `package.json` also runs this file, so `npm test` works too.
45+
46+
A passing run prints two `PASS:` lines followed by `2 passed, 0 failed.`. A failing run prints
47+
one or more `FAIL:` lines, ends with the same summary line format, and exits with code 1.
48+
49+
## After modifying the code
50+
51+
| Situation | Action |
52+
| ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
53+
| Bug fix that should not change results | Run the test — it must still pass. |
54+
| Intentional algorithm change (new integration rule, mesh ordering) | Re-check the result stays close to the NAFEMS target and adjust the tolerance if needed. |
55+
| Adding 8-node serendipity elements to FEAScript | Add a case using 8-node elements on the original 3 × 5 mesh for a tighter comparison against the NAFEMS target. |
56+
| New boundary condition API (e.g. convection parameter order) | Update both this test and the reference JavaScript examples together. |
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* ════════════════════════════════════════════════════════════════
3+
* FEAScript Core Library
4+
* Lightweight Finite Element Simulation in JavaScript
5+
* Version: 0.3.0 (RC) | https://feascript.com
6+
* MIT License © 2023–2026 FEAScript
7+
* ════════════════════════════════════════════════════════════════
8+
*/
9+
10+
/**
11+
* Benchmark test for 2D steady-state heat conduction with convection (NAFEMS T4)
12+
*
13+
* Replicates the NAFEMS T4 benchmark, also documented as a DIANA FEA tutorial
14+
* (https://tutorials.dianafea.com/2DHeatTransferConvection.pdf) and by Altair
15+
* (https://help.altair.com/hwsolvers/os/topics/solvers/os/nafems_test_problem_t4_r.htm):
16+
* a 0.6 m x 1.0 m rectangular plate ABCD with a fixed 100 degC temperature on the bottom edge
17+
* AB, an insulated left edge AD, and convection (h = 750 W/(m2.K), ambient = 0 degC) on the top
18+
* edge CD and right edge BC. The published target temperature at point E (x = 0.6 m, y = 0.2 m,
19+
* on the convective right edge) is 18.3 degC.
20+
*
21+
* This test is an independent reproduction of the NAFEMS T4 problem and is not sponsored,
22+
* endorsed, or affiliated with NAFEMS.
23+
*
24+
* Run: node tests/verification/benchmark/heatConduction2DNafemsT4/benchmark.test.js (or npm test)
25+
*/
26+
27+
import * as mathjs from "mathjs";
28+
import { FEAScriptModel } from "../../../../src/FEAScript.js";
29+
import { basicLog, errorLog } from "../../../../src/utilities/logging.js";
30+
31+
// FEAScript.js references `math` as a global (loaded via CDN in browser).
32+
// Set it here before any solve() call.
33+
globalThis.math = mathjs;
34+
35+
const POINT_E = { x: 0.6, y: 0.2 };
36+
const NAFEMS_TARGET_T = 18.3;
37+
38+
function runSimulation(numElementsX, numElementsY) {
39+
const model = new FEAScriptModel();
40+
41+
model.setModelConfig("heatConductionScript", { coefficientFunctions: { thermalConductivity: 52 } });
42+
model.setMeshConfig({
43+
meshDimension: "2D",
44+
elementOrder: "quadratic",
45+
numElementsX,
46+
numElementsY,
47+
maxX: 0.6,
48+
maxY: 1.0,
49+
});
50+
51+
model.addBoundaryCondition("0", ["constantTemperature", 100]); // Bottom edge (AB), fixed 100 degC
52+
// Left edge (AD) is insulated and left unspecified (natural, zero-flux boundary)
53+
model.addBoundaryCondition("2", ["convection", 750, 0]); // Top edge (CD), convection to 0 degC
54+
model.addBoundaryCondition("3", ["convection", 750, 0]); // Right edge (BC), convection to 0 degC
55+
model.setSolverMethod("lusolve");
56+
57+
const { solutionVector, nodesCoordinates } = model.solve();
58+
const { nodesXCoordinates, nodesYCoordinates } = nodesCoordinates;
59+
60+
const nodeIndex = nodesXCoordinates.findIndex(
61+
(x, i) => Math.abs(x - POINT_E.x) < 1e-10 && Math.abs(nodesYCoordinates[i] - POINT_E.y) < 1e-10,
62+
);
63+
64+
// solutionVector from math.lusolve is a nested array: [[T0], [T1], ...]
65+
const temperatureAtE = Array.isArray(solutionVector[nodeIndex])
66+
? solutionVector[nodeIndex][0]
67+
: solutionVector[nodeIndex];
68+
69+
return { nodeIndex, temperatureAtE };
70+
}
71+
72+
let passed = 0;
73+
let failed = 0;
74+
75+
function assert(condition, message) {
76+
if (!condition) {
77+
errorLog(`FAIL: ${message}`);
78+
failed++;
79+
} else {
80+
basicLog(`PASS: ${message}`);
81+
passed++;
82+
}
83+
}
84+
85+
basicLog("");
86+
basicLog("================================");
87+
basicLog("Starting benchmark test for NAFEMS T4 (2D heat conduction with convection)...");
88+
89+
const CONVERGENCE_TOLERANCE = 0.1; // degC
90+
const { nodeIndex, temperatureAtE } = runSimulation(12, 20);
91+
92+
assert(nodeIndex !== -1, `Found node E at (x=${POINT_E.x}, y=${POINT_E.y})`);
93+
assert(
94+
Math.abs(temperatureAtE - NAFEMS_TARGET_T) < CONVERGENCE_TOLERANCE,
95+
`Mesh (12x20): temperature at E expected close to NAFEMS target ${NAFEMS_TARGET_T}, got ${temperatureAtE} (tolerance ${CONVERGENCE_TOLERANCE})`,
96+
);
97+
98+
basicLog("");
99+
if (failed > 0) {
100+
errorLog(`${passed} passed, ${failed} failed.`);
101+
} else {
102+
basicLog(`${passed} passed, ${failed} failed.`);
103+
}
104+
basicLog("================================");
105+
if (failed > 0) process.exit(1);

0 commit comments

Comments
 (0)