Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,11 @@ External contributors:
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:

```javascript
import { FEAScriptModel, plotSolution, printVersion } from "[USER_DIRECTORY]/FEAScript-core/src/index.js";
import {
FEAScriptModel,
plotSolution,
printVersion,
} from "[USER_DIRECTORY]/FEAScript-core/src/index.js";
```

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.
Expand All @@ -127,4 +131,4 @@ Testing can be also performed at the Node.js environment. In this case you can a
npm test
```

These tests compare the numerical results against stored reference solutions at selected points.
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.
95 changes: 0 additions & 95 deletions examples/Beam1DFEM/Beam1DEuler_Bernoulli.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<img src="https://feascript.github.io/FEAScript-website/assets/feascript-structural-mechanics.png" width="80" alt="FEAScript Beam1DFEM Logo">
<img src="https://feascript.github.io/FEAScript-website/assets/feascript-structural-mechanics.png" width="80" alt="FEAScript Euler-Bernoulli beam logo">

# 1D Euler-Bernoulli Beam Examples

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

## Examples

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

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

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

## Running the Node.js Examples

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

```bash
node Beam1DEuler_Bernoulli.js
node clampedSpringSupportedBeam1D/clampedSpringSupportedBeam1D.js
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* ════════════════════════════════════════════════════════════════
* FEAScript Core Library
* Lightweight Finite Element Simulation in JavaScript
* Version: 0.3.0 (RC) | https://feascript.com
* MIT License © 2023–2026 FEAScript
* ════════════════════════════════════════════════════════════════
*/

// Import Math.js
import * as math from "mathjs";
global.math = math;

// Import FEAScript library
import { FEAScriptModel, printVersion } from "feascript";

console.log("FEAScript Version:", printVersion);

// Create a new FEAScript model
const model = new FEAScriptModel();

// Select physics/PDE
model.setModelConfig("eulerBernoulliBeamScript", {
coefficientFunctions: {
EI: (x) => 2.0e6, // Bending stiffness
q: (x) => (x <= 5 ? -1000 : 0),
},
});

// Define mesh configuration
model.setMeshConfig({
meshDimension: "1D",
elementOrder: "linear",
numElementsX: 2,
maxX: 10,
});

// Define boundary conditions
model.addBoundaryCondition("1", [["fixed"]]); // Clamped support
model.addBoundaryCondition("2", [["pinned"], ["moment", 1250]]); // Roller + applied moment
model.addBoundaryCondition("3", [
["spring", 200],
["force", -2500],
]); // Spring support + point load

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

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

// Print results
const flatSolution = solutionVector.map((entry) =>
Array.isArray(entry) ? entry[0] : entry
);

const nodeXCoordinates = [0, 5, 10];
console.log("\nNode | x (m) | Deflection w (m) | Rotation theta (rad)");
console.log("-----|----------|-------------------|----------------------");
for (let nodeIndex = 0; nodeIndex < nodeXCoordinates.length; nodeIndex++) {
const w = flatSolution[2 * nodeIndex];
const theta = flatSolution[2 * nodeIndex + 1];
console.log(
` ${nodeIndex + 1} | ${nodeXCoordinates[nodeIndex]
.toFixed(2)
.padStart(8)} | ${w.toExponential(4).padStart(17)} | ${theta
.toExponential(4)
.padStart(20)}`
);
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"build": "rollup -c",
"prepare": "npm run build",
"prepublishOnly": "npm run build",
"test": "node tests/run-all-tests.js",
"test": "node --test tests",
"format": "prettier --write ."
},
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion src/mesh/meshUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ export function performIsoparametricMapping2D(params) {

/**
* Function to test if a point is inside a triangle using barycentric coordinates,
* also returning the natural coordinates (ksi, eta).
* also returning the natural coordinates (ksi, eta)
* @param {number} x - X-coordinate of the point
* @param {number} y - Y-coordinate of the point
* @param {array} vertices - Triangle vertices [[x0,y0],[x1,y1],[x2,y2]]
Expand Down
6 changes: 1 addition & 5 deletions src/models/beamBoundaryConditions.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,7 @@ export class BeamBoundaryConditions {
} else if (conditionType === "rotationFixed" || conditionType === "rotation") {
applyDirichlet(rotationDOF, value ?? 0);
debugLog(`Node ${nodeKey}: Applied rotation theta=${value ?? 0} (essential BC)`);
} else if (
conditionType !== "force" &&
conditionType !== "moment" &&
conditionType !== "spring"
) {
} else if (conditionType !== "force" && conditionType !== "moment" && conditionType !== "spring") {
errorLog(`Unknown beam boundary condition type: "${conditionType}"`);
}
});
Expand Down
5 changes: 4 additions & 1 deletion src/models/eulerBernoulliBeam.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ export function assembleEulerBernoulliBeamMat(meshData, boundaryConditions, coef

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

// Matrix assembly
Expand Down
18 changes: 13 additions & 5 deletions src/visualization/vtkPlot.js
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ function convertElementNodesToLinearCell(elementNodes) {
return [indices[0], indices[6], indices[8], indices[2]];
}

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

Expand Down Expand Up @@ -546,15 +546,23 @@ function buildVTPString(vtkData) {
'<?xml version="1.0"?>',
'<VTKFile type="PolyData" version="0.1" byte_order="LittleEndian">',
" <PolyData>",
` <Piece NumberOfPoints="${numberOfPoints}" NumberOfVerts="0" NumberOfLines="${isLine ? offsets.length : 0}" NumberOfStrips="0" NumberOfPolys="${isLine ? 0 : offsets.length}">`,
` <Piece NumberOfPoints="${numberOfPoints}" NumberOfVerts="0" NumberOfLines="${
isLine ? offsets.length : 0
}" NumberOfStrips="0" NumberOfPolys="${isLine ? 0 : offsets.length}">`,
' <PointData Scalars="solution">',
` <DataArray type="Float32" Name="solution" NumberOfComponents="1" format="ascii">${Array.from(vtkData.scalars).join(" ")}</DataArray>`,
` <DataArray type="Float32" Name="solution" NumberOfComponents="1" format="ascii">${Array.from(
vtkData.scalars,
).join(" ")}</DataArray>`,
" </PointData>",
" <Points>",
` <DataArray type="Float32" NumberOfComponents="3" format="ascii">${Array.from(vtkData.points).join(" ")}</DataArray>`,
` <DataArray type="Float32" NumberOfComponents="3" format="ascii">${Array.from(
vtkData.points,
).join(" ")}</DataArray>`,
" </Points>",
` <${topologyTag}>`,
` <DataArray type="Int32" Name="connectivity" format="ascii">${connectivity.join(" ")}</DataArray>`,
` <DataArray type="Int32" Name="connectivity" format="ascii">${connectivity.join(
" ",
)}</DataArray>`,
` <DataArray type="Int32" Name="offsets" format="ascii">${offsets.join(" ")}</DataArray>`,
` </${topologyTag}>`,
" </Piece>",
Expand Down
2 changes: 1 addition & 1 deletion src/workers/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import * as Comlink from "../vendor/comlink.mjs";
export class FEAScriptWorker {
/**
* Constructor to initialize the FEAScriptWorker class
* Sets up the worker and initializes the workerWrapper.
* Sets up the worker and initializes the workerWrapper
*/
constructor() {
this.worker = null;
Expand Down
Loading
Loading