Skip to content

Commit f61e220

Browse files
committed
Use a 2×2 Gauss rule for linear quadrilaterals (#105)
Linear elements used a 1-point Gauss rule per direction, which under-integrates the stiffness matrix of bilinear quadrilaterals (and the convection terms on their sides), so results converged slowly; on the NAFEMS T4 benchmark a 6 × 10 linear mesh gave 28.27 °C instead of 18.25 °C. Linear elements now use a 2-point rule per direction (exact up to degree 3), giving 17.95 °C on the same mesh. Tests: the quadrature unit test checks exactness up to degree 3 for linear quadrilaterals, and the regression values of the linear cases (advection-diffusion 1D, heat conduction 2D fin, nonlinear reaction-diffusion 1D, solidification front 2D) are updated, with the reason in each README. Closes #105 Add NAFEMS T4, Couette flow and cantilever beam examples Node.js examples based on the verification tests, each printing the result next to its reference value: - heatConduction2DNafemsT4: NAFEMS T4 benchmark (heat conduction with convection), temperature at point E against the 18.3 °C target (18.25 °C converged) - couetteFlow2DCreepingFlow: Couette flow, against the exact linear velocity profile with zero pressure - cantileverBeam1D: cantilever with a tip load, against the closed-form tip deflection and rotation
1 parent b1ab4f9 commit f61e220

85 files changed

Lines changed: 330 additions & 108 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎dist/feascript-worker.esm.js‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dist/feascript-worker.esm.js.map‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dist/feascript.cjs.js‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dist/feascript.cjs.js.map‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dist/feascript.esm.js‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dist/feascript.esm.js.map‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dist/feascript.umd.js‎

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dist/feascript.umd.js.map‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎examples/creepingFlowScript/README.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ This example solves a 2D lid-driven cavity flow using the creeping flow solver w
1919
[tutorial](https://feascript.com/tutorials/lid-driven-cavity-2d-creeping-flow.html) on the
2020
FEAScript website.
2121

22+
### 2. Couette Flow in a 2D Channel (`couetteFlow2DCreepingFlow.js`)
23+
24+
A channel whose top wall moves with velocity U, with a fixed bottom wall and stress-free (open)
25+
ends. The exact solution is the linear velocity profile u = U y / H with v = 0 and zero pressure,
26+
which the Taylor-Hood elements reproduce exactly. The example prints the velocity along the left
27+
end and the largest deviation from the exact solution.
28+
2229
## Running the Node.js examples
2330

2431
### 1. Create `package.json` with ES module support
@@ -39,4 +46,6 @@ npm install feascript
3946
node lidDrivenCavity2DCreepingFlow.js
4047
```
4148

49+
(or `node couetteFlow2DCreepingFlow/couetteFlow2DCreepingFlow.js`)
50+
4251
(for the "Creeping Flow on a Lid-Driven 2D Cavity" example)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* ════════════════════════════════════════════════════════════════
3+
* FEAScript Core Library
4+
* Finite Element Simulation in JavaScript
5+
* Version: 0.3.0 | https://feascript.com
6+
* MIT License © 2023–2026 FEAScript
7+
* ════════════════════════════════════════════════════════════════
8+
*/
9+
10+
// Couette flow: a 4 × 2 channel whose top wall moves with velocity U, with stress-free (open) ends.
11+
// The exact solution is the linear velocity profile u = U y / H, v = 0, with zero pressure, which
12+
// Taylor-Hood elements reproduce exactly
13+
14+
// Import FEAScript library
15+
import { FEAScriptModel, printVersion } from "feascript";
16+
17+
console.log("FEAScript Version:", printVersion);
18+
19+
const wallVelocity = 1; // U
20+
const channelHeight = 2; // H
21+
22+
// 1. Create the model and select the physics
23+
const model = new FEAScriptModel();
24+
model.setModelConfig("creepingFlowScript", {
25+
coefficientFunctions: {
26+
viscosity: 1, // μ
27+
},
28+
});
29+
30+
// 2. Define the mesh (creeping flow needs quadratic elements)
31+
model.setMeshConfig({
32+
meshDimension: "2D",
33+
elementOrder: "quadratic",
34+
numElementsX: 4,
35+
numElementsY: 2,
36+
maxX: 4,
37+
maxY: channelHeight,
38+
});
39+
40+
// 3. Apply the boundary conditions
41+
model.addBoundaryCondition("0", ["constantVelocity", 0, 0]); // Bottom: fixed wall
42+
model.addBoundaryCondition("1", ["stressFree"]); // Left: open end
43+
model.addBoundaryCondition("2", ["constantVelocity", wallVelocity, 0]); // Top: moving wall
44+
model.addBoundaryCondition("3", ["stressFree"]); // Right: open end
45+
46+
// 4. Solve
47+
model.setSolverMethod("lusolve");
48+
const { nodesCoordinates, fields } = model.solve();
49+
50+
// 5. Print the results
51+
const { nodesYCoordinates } = nodesCoordinates;
52+
const largestDeviation = Math.max(
53+
...nodesYCoordinates.map((y, i) => Math.abs(fields.u[i] - (wallVelocity * y) / channelHeight)),
54+
);
55+
console.log("Velocity u along the left end (x = 0):");
56+
nodesYCoordinates.forEach((y, i) => {
57+
if (nodesCoordinates.nodesXCoordinates[i] === 0) {
58+
console.log(
59+
` y = ${y.toFixed(2)}: u = ${fields.u[i].toFixed(4)} (exact ${(wallVelocity * y) / channelHeight})`,
60+
);
61+
}
62+
});
63+
console.log(`Largest deviation from u = U y / H: ${largestDeviation.toExponential(2)}`);
64+
console.log(`Largest |v|: ${Math.max(...fields.v.map(Math.abs)).toExponential(2)} (exact 0)`);
65+
console.log(`Largest |p|: ${Math.max(...fields.p.map(Math.abs)).toExponential(2)} (exact 0)`);

0 commit comments

Comments
 (0)