Skip to content

Commit 50e8345

Browse files
committed
Improved formatting in various test files
1 parent 12c053e commit 50e8345

18 files changed

Lines changed: 303 additions & 194 deletions

File tree

CONTRIBUTING.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,11 @@ External contributors:
100100
Before submitting a pull request, test your modifications by running the FEAScript library from a local directory. For example, you can load the library in your HTML file as follows:
101101

102102
```javascript
103-
import { FEAScriptModel, plotSolution, printVersion } from "[USER_DIRECTORY]/FEAScript-core/src/index.js";
103+
import {
104+
FEAScriptModel,
105+
plotSolution,
106+
printVersion,
107+
} from "[USER_DIRECTORY]/FEAScript-core/src/index.js";
104108
```
105109

106110
FEAScript can be run on a local server. You **must** start the server from the workspace root directory (the folder that contains both `FEAScript-core/` and `FEAScript-website/`), not from inside either subfolder. The HTML files use relative paths such as `../feascript-website.css` and `../../FEAScript-core/src/index.js` that only resolve correctly from that root.
@@ -127,4 +131,4 @@ Testing can be also performed at the Node.js environment. In this case you can a
127131
npm test
128132
```
129133

130-
These tests compare the numerical results against stored reference solutions at selected points.
134+
This command uses the Node.js test runner to discover all test files under `tests/`. The tests compare numerical results against stored reference solutions and verify individual solver and assembler behavior.

examples/eulerBernoulliBeamScript/README.md

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<img src="https://feascript.github.io/FEAScript-website/assets/feascript-structural-mechanics.png" width="80" alt="FEAScript Beam1DFEM Logo">
1+
<img src="https://feascript.github.io/FEAScript-website/assets/feascript-structural-mechanics.png" width="80" alt="FEAScript Euler-Bernoulli beam logo">
22

33
# 1D Euler-Bernoulli Beam Examples
44

@@ -7,7 +7,7 @@ This directory contains Node.js examples demonstrating how to use the FEAScript
77

88
## Examples
99

10-
#### 1. Clamped and Spring-Supported Beam (`Beam1DEuler_Bernoulli.js`)
10+
#### 1. Clamped and Spring-Supported Beam (`clampedSpringSupportedBeam1D.js`)
1111

1212
Reproduces the "Bending of a Beam" example from J.N. Reddy, _An Introduction to the Finite Element
1313
Method_, 3rd ed., McGraw-Hill, 2006 (FEM1D example problems, Chapter 7). A 10 m beam is clamped at
@@ -69,17 +69,20 @@ plus a point load):
6969
```javascript
7070
model.addBoundaryCondition("1", [["fixed"]]); // w=0, theta=0 (clamped)
7171
model.addBoundaryCondition("2", [["pinned"], ["moment", 1250]]); // w=0, plus an applied moment
72-
model.addBoundaryCondition("3", [["spring", 200], ["force", -2500]]); // elastic support, plus a point load
72+
model.addBoundaryCondition("3", [
73+
["spring", 200],
74+
["force", -2500],
75+
]); // elastic support, plus a point load
7376
```
7477

75-
| Condition type | Kind | Effect |
76-
| ------------------------------------ | ---------------- | ----------------------------------------------------------- |
77-
| `["fixed"]` | Essential | `w = 0` and `theta = 0` (clamped support) |
78-
| `["pinned"]` / `["deflection", v]` | Essential | `w = v` (default `v = 0`; roller/pin support) |
79-
| `["rotationFixed"]` / `["rotation", v]` | Essential | `theta = v` (default `v = 0`) |
80-
| `["force", v]` | Natural | Applies a concentrated transverse force `v` at the node |
81-
| `["moment", v]` | Natural | Applies a concentrated moment `v` at the node |
82-
| `["spring", k, uRef]` | Mixed (Robin) | Transverse elastic support of stiffness `k` about `uRef` (default `uRef = 0`) |
78+
| Condition type | Kind | Effect |
79+
| --------------------------------------- | ------------- | ----------------------------------------------------------------------------- |
80+
| `["fixed"]` | Essential | `w = 0` and `theta = 0` (clamped support) |
81+
| `["pinned"]` / `["deflection", v]` | Essential | `w = v` (default `v = 0`; roller/pin support) |
82+
| `["rotationFixed"]` / `["rotation", v]` | Essential | `theta = v` (default `v = 0`) |
83+
| `["force", v]` | Natural | Applies a concentrated transverse force `v` at the node |
84+
| `["moment", v]` | Natural | Applies a concentrated moment `v` at the node |
85+
| `["spring", k, uRef]` | Mixed (Robin) | Transverse elastic support of stiffness `k` about `uRef` (default `uRef = 0`) |
8386

8487
## Running the Node.js Examples
8588

@@ -98,5 +101,5 @@ npm install feascript
98101
#### 3. Run the example:
99102

100103
```bash
101-
node Beam1DEuler_Bernoulli.js
104+
node clampedSpringSupportedBeam1D/clampedSpringSupportedBeam1D.js
102105
```

examples/eulerBernoulliBeamScript/clampedSpringSupportedBeam1D/clampedSpringSupportedBeam1D.js

Lines changed: 14 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,6 @@
77
* ════════════════════════════════════════════════════════════════
88
*/
99

10-
/**
11-
* Clamped and spring-supported Euler-Bernoulli beam
12-
*
13-
* Reproduces the "Bending of a Beam" example from J.N. Reddy, "An Introduction to
14-
* the Finite Element Method", 3rd ed., McGraw-Hill, 2006 (FEM1D example problems,
15-
* Chapter 7), solved there with the reference FEM1D Fortran program (MODEL=3,
16-
* NTYPE=0, IELEM=0 i.e. Hermite cubic elements).
17-
*
18-
* Geometry: a 10 m beam, clamped at x=0, resting on a roller at midspan (x=5 m),
19-
* and connected to a linear transverse spring at the free end (x=10 m).
20-
* Mesh: 2 Hermite cubic beam elements of 5 m each -> 3 nodes: [1, 2, 3]
21-
*
22-
* 1,000 N/m (on 0 <= x <= 5) 2,500 N (down, at node 3)
23-
* v v v v v v v v v v |
24-
* /////|--------------------|-------------------| ~~~~ spring, k = 1e-4*EI
25-
* ///// 1 (clamped) 2 (roller) 3 (free end, spring)
26-
* |<-------- 5 m ----->|<------- 5 m ------>|
27-
* ^ moment 1,250 N-m applied at node 2
28-
*
29-
* EI = 2e6 N-m^2 (constant), k_spring = 1e-4 * EI = 200 N/m
30-
*
31-
* Boundary conditions (see beamBoundaryConditions.js for the condition syntax):
32-
* - Node 1 (x=0): fixed -> w=0, theta=0 (clamped support)
33-
* - Node 2 (x=5): pinned + moment -> w=0, applied moment M=1250 N-m
34-
* - Node 3 (x=10): spring + force -> k=200 N/m, applied point load P=-2500 N
35-
*/
36-
3710
// Import Math.js
3811
import * as math from "mathjs";
3912
global.math = math;
@@ -49,37 +22,37 @@ const model = new FEAScriptModel();
4922
// Select physics/PDE
5023
model.setModelConfig("eulerBernoulliBeamScript", {
5124
coefficientFunctions: {
52-
EI: (x) => 2.0e6, // Bending stiffness E*I (N-m^2), constant along the beam
53-
// Distributed transverse load: -1,000 N/m over the first (clamped) span only
25+
EI: (x) => 2.0e6, // Bending stiffness
5426
q: (x) => (x <= 5 ? -1000 : 0),
55-
// c0 defaults to 0 (no elastic foundation) when omitted
5627
},
5728
});
5829

5930
// Define mesh configuration
60-
// elementOrder is 'linear' because that only describes the 2-node beam geometry;
61-
// the field itself is always interpolated with cubic Hermite shape functions internally
6231
model.setMeshConfig({
6332
meshDimension: "1D",
6433
elementOrder: "linear",
6534
numElementsX: 2,
6635
maxX: 10,
6736
});
6837

69-
// Define boundary conditions (keyed by 1-based global node number)
38+
// Define boundary conditions
7039
model.addBoundaryCondition("1", [["fixed"]]); // Clamped support
7140
model.addBoundaryCondition("2", [["pinned"], ["moment", 1250]]); // Roller + applied moment
72-
model.addBoundaryCondition("3", [["spring", 200], ["force", -2500]]); // Spring support + point load
41+
model.addBoundaryCondition("3", [
42+
["spring", 200],
43+
["force", -2500],
44+
]); // Spring support + point load
7345

7446
// Set solver method
7547
model.setSolverMethod("lusolve");
7648

7749
// Solve the problem
7850
const { solutionVector } = model.solve();
7951

80-
// The solution vector is ordered [w_0, theta_0, w_1, theta_1, w_2, theta_2, ...]
81-
// (mathjs' lusolve returns a nested array, so flatten defensively before reading it)
82-
const flatSolution = solutionVector.map((entry) => (Array.isArray(entry) ? entry[0] : entry));
52+
// Print results
53+
const flatSolution = solutionVector.map((entry) =>
54+
Array.isArray(entry) ? entry[0] : entry
55+
);
8356

8457
const nodeXCoordinates = [0, 5, 10];
8558
console.log("\nNode | x (m) | Deflection w (m) | Rotation theta (rad)");
@@ -88,8 +61,10 @@ for (let nodeIndex = 0; nodeIndex < nodeXCoordinates.length; nodeIndex++) {
8861
const w = flatSolution[2 * nodeIndex];
8962
const theta = flatSolution[2 * nodeIndex + 1];
9063
console.log(
91-
` ${nodeIndex + 1} | ${nodeXCoordinates[nodeIndex].toFixed(2).padStart(8)} | ${w
64+
` ${nodeIndex + 1} | ${nodeXCoordinates[nodeIndex]
65+
.toFixed(2)
66+
.padStart(8)} | ${w.toExponential(4).padStart(17)} | ${theta
9267
.toExponential(4)
93-
.padStart(17)} | ${theta.toExponential(4).padStart(20)}`,
68+
.padStart(20)}`
9469
);
9570
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"build": "rollup -c",
2121
"prepare": "npm run build",
2222
"prepublishOnly": "npm run build",
23-
"test": "node tests/run-all-tests.js",
23+
"test": "node --test tests",
2424
"format": "prettier --write ."
2525
},
2626
"repository": {

src/mesh/meshUtils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ export function performIsoparametricMapping2D(params) {
238238

239239
/**
240240
* Function to test if a point is inside a triangle using barycentric coordinates,
241-
* also returning the natural coordinates (ksi, eta).
241+
* also returning the natural coordinates (ksi, eta)
242242
* @param {number} x - X-coordinate of the point
243243
* @param {number} y - Y-coordinate of the point
244244
* @param {array} vertices - Triangle vertices [[x0,y0],[x1,y1],[x2,y2]]

src/models/beamBoundaryConditions.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,7 @@ export class BeamBoundaryConditions {
121121
} else if (conditionType === "rotationFixed" || conditionType === "rotation") {
122122
applyDirichlet(rotationDOF, value ?? 0);
123123
debugLog(`Node ${nodeKey}: Applied rotation theta=${value ?? 0} (essential BC)`);
124-
} else if (
125-
conditionType !== "force" &&
126-
conditionType !== "moment" &&
127-
conditionType !== "spring"
128-
) {
124+
} else if (conditionType !== "force" && conditionType !== "moment" && conditionType !== "spring") {
129125
errorLog(`Unknown beam boundary condition type: "${conditionType}"`);
130126
}
131127
});

src/models/eulerBernoulliBeam.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ export function assembleEulerBernoulliBeamMat(meshData, boundaryConditions, coef
8181

8282
// Cubic Hermite basis functions for the field, with a 4-point Gauss quadrature rule
8383
const basisFunctions = new BasisFunctions({ meshDimension: "1D", elementOrder: "hermiteCubic" });
84-
const numericalIntegration = new NumericalIntegration({ meshDimension: "1D", elementOrder: "hermiteCubic" });
84+
const numericalIntegration = new NumericalIntegration({
85+
meshDimension: "1D",
86+
elementOrder: "hermiteCubic",
87+
});
8588
const { gaussPoints, gaussWeights } = numericalIntegration.getGaussPointsAndWeights();
8689

8790
// Matrix assembly

src/visualization/vtkPlot.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ function convertElementNodesToLinearCell(elementNodes) {
492492
return [indices[0], indices[6], indices[8], indices[2]];
493493
}
494494

495-
// Generic fallback for polygonal/high-order cells.
495+
// Generic fallback for polygonal/high-order cells
496496
return indices.slice(0, Math.min(4, indices.length));
497497
}
498498

@@ -546,15 +546,23 @@ function buildVTPString(vtkData) {
546546
'<?xml version="1.0"?>',
547547
'<VTKFile type="PolyData" version="0.1" byte_order="LittleEndian">',
548548
" <PolyData>",
549-
` <Piece NumberOfPoints="${numberOfPoints}" NumberOfVerts="0" NumberOfLines="${isLine ? offsets.length : 0}" NumberOfStrips="0" NumberOfPolys="${isLine ? 0 : offsets.length}">`,
549+
` <Piece NumberOfPoints="${numberOfPoints}" NumberOfVerts="0" NumberOfLines="${
550+
isLine ? offsets.length : 0
551+
}" NumberOfStrips="0" NumberOfPolys="${isLine ? 0 : offsets.length}">`,
550552
' <PointData Scalars="solution">',
551-
` <DataArray type="Float32" Name="solution" NumberOfComponents="1" format="ascii">${Array.from(vtkData.scalars).join(" ")}</DataArray>`,
553+
` <DataArray type="Float32" Name="solution" NumberOfComponents="1" format="ascii">${Array.from(
554+
vtkData.scalars,
555+
).join(" ")}</DataArray>`,
552556
" </PointData>",
553557
" <Points>",
554-
` <DataArray type="Float32" NumberOfComponents="3" format="ascii">${Array.from(vtkData.points).join(" ")}</DataArray>`,
558+
` <DataArray type="Float32" NumberOfComponents="3" format="ascii">${Array.from(
559+
vtkData.points,
560+
).join(" ")}</DataArray>`,
555561
" </Points>",
556562
` <${topologyTag}>`,
557-
` <DataArray type="Int32" Name="connectivity" format="ascii">${connectivity.join(" ")}</DataArray>`,
563+
` <DataArray type="Int32" Name="connectivity" format="ascii">${connectivity.join(
564+
" ",
565+
)}</DataArray>`,
558566
` <DataArray type="Int32" Name="offsets" format="ascii">${offsets.join(" ")}</DataArray>`,
559567
` </${topologyTag}>`,
560568
" </Piece>",

src/workers/worker.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import * as Comlink from "../vendor/comlink.mjs";
1616
export class FEAScriptWorker {
1717
/**
1818
* Constructor to initialize the FEAScriptWorker class
19-
* Sets up the worker and initializes the workerWrapper.
19+
* Sets up the worker and initializes the workerWrapper
2020
*/
2121
constructor() {
2222
this.worker = null;

tests/regression/EulerBernoulliBeam/REGRESSION.md

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,35 @@ This test guards the numerical output of the 1D Euler-Bernoulli beam example aga
66
unintended changes to the beam solver, assembler, or mesh-generation logic.
77

88
It replicates exactly the problem set up in
9-
[`Beam1DEuler_Bernoulli.js`](../../../examples/Beam1DFEM/Beam1DEuler_Bernoulli.js) — the
9+
[`clampedSpringSupportedBeam1D.js`](../../../examples/eulerBernoulliBeamScript/clampedSpringSupportedBeam1D/clampedSpringSupportedBeam1D.js) — the
1010
"Bending of a Beam" example from J.N. Reddy, _An Introduction to the Finite Element Method_,
1111
3rd ed., McGraw-Hill, 2006 (FEM1D example problems, Chapter 7) — and asserts both a set of
1212
known-good baseline values and, independently of those baseline numbers, that the resulting
1313
finite element solution satisfies global static equilibrium.
1414

1515
## Problem setup
1616

17-
| Parameter | Value |
18-
| ----------------------------- | -------------------------------------------------------- |
19-
| Domain | 1D beam, 0 – 10 m |
20-
| Mesh | 2 cubic Hermite beam elements of 5 m each (3 nodes) |
21-
| Bending stiffness EI | 2.0 × 10⁶ N·m² (constant) |
22-
| Distributed load | −1,000 N/m over 0 ≤ x ≤ 5 m only |
23-
| Node 1 (x = 0) | Fixed (clamped): w = 0, theta = 0 |
24-
| Node 2 (x = 5) | Pinned (roller): w = 0, plus an applied moment M = 1,250 N·m |
25-
| Node 3 (x = 10) | Transverse spring k = 200 N/m, plus a point load P = −2,500 N |
26-
| Solver | LU decomposition (`lusolve`) |
17+
| Parameter | Value |
18+
| -------------------- | ------------------------------------------------------------- |
19+
| Domain | 1D beam, 0 – 10 m |
20+
| Mesh | 2 cubic Hermite beam elements of 5 m each (3 nodes) |
21+
| Bending stiffness EI | 2.0 × 10⁶ N·m² (constant) |
22+
| Distributed load | −1,000 N/m over 0 ≤ x ≤ 5 m only |
23+
| Node 1 (x = 0) | Fixed (clamped): w = 0, theta = 0 |
24+
| Node 2 (x = 5) | Pinned (roller): w = 0, plus an applied moment M = 1,250 N·m |
25+
| Node 3 (x = 10) | Transverse spring k = 200 N/m, plus a point load P = −2,500 N |
26+
| Solver | LU decomposition (`lusolve`) |
2727

2828
## Expected values
2929

30-
| Quantity | Value |
31-
| ------------------------ | ------------------------- |
32-
| w₁ (deflection, node 1) | 0 m |
33-
| θ₁ (rotation, node 1) | 0 rad |
34-
| w₂ (deflection, node 2) | 0 m |
35-
| θ₂ (rotation, node 2) | −5.6790761806 × 10⁻³ rad |
36-
| w₃ (deflection, node 3) | −8.0144777663 × 10⁻² m |
37-
| θ₃ (rotation, node 3) | −2.1203895209 × 10⁻² rad |
30+
| Quantity | Value |
31+
| ----------------------- | ------------------------ |
32+
| w₁ (deflection, node 1) | 0 m |
33+
| θ₁ (rotation, node 1) | 0 rad |
34+
| w₂ (deflection, node 2) | 0 m |
35+
| θ₂ (rotation, node 2) | −5.6790761806 × 10⁻³ rad |
36+
| w₃ (deflection, node 3) | −8.0144777663 × 10⁻² m |
37+
| θ₃ (rotation, node 3) | −2.1203895209 × 10⁻² rad |
3838

3939
Tolerance used in the baseline assertions: `1e-8`.
4040

@@ -77,16 +77,24 @@ node tests/regression/EulerBernoulliBeam/regression.test.js
7777

7878
The `test` script in `package.json` also runs this file, so `npm test` works too.
7979

80+
A passing run prints a `PASS:` line for each check, followed by a summary line:
81+
82+
```
83+
8 passed, 0 failed.
84+
```
85+
86+
A failing run prints one or more `FAIL:` lines, ends with the same summary line format, and exits with code 1.
87+
8088
## After modifying the code
8189

82-
| Situation | Action |
83-
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
84-
| Bug fix that should not change results | Run the test — it must still pass. |
85-
| Intentional algorithm change (new integration rule, new element type, etc.) | Re-derive the expected values, update `EXPECTED` in `regression.test.js`, and document the reason here. |
86-
| New boundary condition type | Update both the test and `Beam1DEuler_Bernoulli.js` together. |
90+
| Situation | Action |
91+
| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
92+
| Bug fix that should not change results | Run the test — it must still pass. |
93+
| Intentional algorithm change (new integration rule, new element type, etc.) | Re-derive the expected values, update `EXPECTED` in `regression.test.js`, and document the reason here. |
94+
| New boundary condition type | Update both the test and `clampedSpringSupportedBeam1D.js` together. |
8795

8896
## Change log
8997

9098
| Date | Change | New expected values |
91-
| ---------- | ---------------------------- | -------------------- |
92-
| 2026-07-17 | Initial regression baseline | See table above |
99+
| ---------- | --------------------------- | ------------------- |
100+
| 2026-07-17 | Initial regression baseline | See table above |

0 commit comments

Comments
 (0)