Skip to content

Commit ff22b39

Browse files
committed
Refactor solver functions to accept options object for parameters and improve documentation
1 parent 431495a commit ff22b39

File tree

6 files changed

+55
-86
lines changed

6 files changed

+55
-86
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Thank you for your interest in contributing! FEAScript is in early development,
3232

3333
All files in the FEAScript-core codebase should follow this structure:
3434

35-
1. Banner: All files start with the FEAScript ASCII art banner
35+
1. Banner: All files start with the FEAScript banner
3636
2. Imports:
3737
- External imports first, alphabetically ordered
3838
- Internal imports next, grouped by module/folder

src/FEAScript.js

Lines changed: 36 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,17 @@ export class FEAScriptModel {
4646
this.solverConfig = solverConfig;
4747

4848
// Store coefficient functions if provided
49-
if (options && options.coefficientFunctions) {
49+
if (options?.coefficientFunctions) {
5050
this.coefficientFunctions = options.coefficientFunctions;
5151
debugLog("Coefficient functions set");
5252
}
53+
// Only update if a value is provided, otherwise keep the default
54+
if (options?.maxIterations !== undefined) {
55+
this.maxIterations = options.maxIterations;
56+
}
57+
if (options?.tolerance !== undefined) {
58+
this.tolerance = options.tolerance;
59+
}
5360

5461
debugLog(`Solver config set to: ${solverConfig}`);
5562
}
@@ -69,67 +76,15 @@ export class FEAScriptModel {
6976
debugLog(`Solver method set to: ${solverMethod}`);
7077
}
7178

72-
async solveWithWebgpu(computeEngine) {
73-
if (!this.solverConfig || !this.meshConfig || !this.boundaryConditions) {
74-
errorLog("Solver config, mesh config, and boundary conditions must be set before solving.");
75-
}
76-
77-
let jacobianMatrix = [];
78-
let residualVector = [];
79-
let solutionVector = [];
80-
let nodesCoordinates = {};
81-
82-
// Prepare the mesh
83-
basicLog("Preparing mesh...");
84-
const meshData = prepareMesh(this.meshConfig);
85-
basicLog("Mesh preparation completed");
86-
87-
// Extract node coordinates from meshData
88-
nodesCoordinates = {
89-
nodesXCoordinates: meshData.nodesXCoordinates,
90-
nodesYCoordinates: meshData.nodesYCoordinates,
91-
};
92-
93-
// Assembly matrices
94-
basicLog("Beginning matrix assembly...");
95-
console.time("assemblyMatrices");
96-
basicLog(`Using solver: ${this.solverConfig}`);
97-
if (this.solverConfig === "solidHeatTransferScript") {
98-
({ jacobianMatrix, residualVector } = assembleHeatConductionMat(
99-
meshData,
100-
this.boundaryConditions
101-
));
102-
}
103-
console.timeEnd("assemblyMatrices");
104-
basicLog("Matrix assembly completed");
105-
106-
// System solving with WebGPU Jacobi
107-
basicLog("Solving system using WebGPU Jacobi...");
108-
console.time("systemSolving");
109-
110-
// Convert matrices to arrays for WebGPU
111-
const A = Array.isArray(jacobianMatrix) ? jacobianMatrix : jacobianMatrix.toArray();
112-
const b = Array.isArray(residualVector) ? residualVector : residualVector.toArray();
113-
114-
// For heat conduction FEM, the matrix might be negative definite
115-
console.log("Matrix diagonal sample:", A.slice(0, 5).map((row, i) => row[i]));
116-
console.log("RHS sample:", b.slice(0, 5));
117-
118-
// Use WebGPU Jacobi method
119-
const initialGuess = new Array(b.length).fill(0);
120-
solutionVector = await computeEngine.webgpuJacobiSolver(A, b, initialGuess, 10000, 1e-3);
121-
122-
console.timeEnd("systemSolving");
123-
basicLog("System solved successfully with WebGPU Jacobi");
124-
125-
return { solutionVector, nodesCoordinates };
126-
}
127-
128-
solve() {
79+
/**
80+
* Function to solve the finite element problem synchronously
81+
* @param {object} [options] - Additional parameters for the solver, such as `maxIterations` and `tolerance`
82+
* @returns {object} An object containing the solution vector and the coordinates of the mesh nodes
83+
*/
84+
solve(options = {}) {
12985
if (!this.solverConfig || !this.meshConfig || !this.boundaryConditions) {
13086
errorLog("Solver config, mesh config, and boundary conditions must be set before solving.");
13187
}
132-
13388
/**
13489
* For consistency across both linear and nonlinear formulations,
13590
* this project always refers to the assembled right-hand side vector
@@ -172,7 +127,10 @@ export class FEAScriptModel {
172127
} else {
173128
// Use regular linear solver methods
174129
({ jacobianMatrix, residualVector } = assembleHeatConductionMat(meshData, this.boundaryConditions));
175-
const linearSystemResult = solveLinearSystem(this.solverMethod, jacobianMatrix, residualVector);
130+
const linearSystemResult = solveLinearSystem(this.solverMethod, jacobianMatrix, residualVector, {
131+
maxIterations: options.maxIterations ?? this.maxIterations,
132+
tolerance: options.tolerance ?? this.tolerance,
133+
});
176134
solutionVector = linearSystemResult.solutionVector;
177135
}
178136
} else if (this.solverConfig === "frontPropagationScript") {
@@ -187,6 +145,9 @@ export class FEAScriptModel {
187145
eikonalActivationFlag: eikonalActivationFlag,
188146
solverMethod: this.solverMethod,
189147
initialSolution,
148+
// TODO: Consider using different maxIterations/tolerance for Newton-Raphson and linear solver
149+
maxIterations: options.maxIterations ?? this.maxIterations,
150+
tolerance: options.tolerance ?? this.tolerance,
190151
};
191152

192153
while (eikonalActivationFlag <= 1) {
@@ -199,7 +160,7 @@ export class FEAScriptModel {
199160
}
200161

201162
// Solve the assembled non-linear system
202-
const newtonRaphsonResult = newtonRaphson(assembleFrontPropagationMat, context, 100, 1e-4);
163+
const newtonRaphsonResult = newtonRaphson(assembleFrontPropagationMat, context);
203164

204165
// Extract results
205166
jacobianMatrix = newtonRaphsonResult.jacobianMatrix;
@@ -223,7 +184,10 @@ export class FEAScriptModel {
223184
this.coefficientFunctions
224185
));
225186

226-
const linearSystemResult = solveLinearSystem(this.solverMethod, jacobianMatrix, residualVector);
187+
const linearSystemResult = solveLinearSystem(this.solverMethod, jacobianMatrix, residualVector, {
188+
maxIterations: options.maxIterations ?? this.maxIterations,
189+
tolerance: options.tolerance ?? this.tolerance,
190+
});
227191
solutionVector = linearSystemResult.solutionVector;
228192
}
229193
}
@@ -233,6 +197,12 @@ export class FEAScriptModel {
233197
return { solutionVector, nodesCoordinates };
234198
}
235199

200+
/**
201+
* Function to solve the finite element problem asynchronously
202+
* @param {object} computeEngine - The compute engine to use for the asynchronous solver (e.g., a worker or a WebGPU context)
203+
* @param {object} [options] - Additional parameters for the solver, such as `maxIterations` and `tolerance`
204+
* @returns {Promise<object>} A promise that resolves to an object containing the solution vector and the coordinates of the mesh nodes
205+
*/
236206
async solveAsync(computeEngine, options = {}) {
237207
if (!this.solverConfig || !this.meshConfig || !this.boundaryConditions) {
238208
errorLog("Solver config, mesh config, and boundary conditions must be set before solving.");
@@ -260,18 +230,18 @@ export class FEAScriptModel {
260230
if (this.solverMethod === "jacobi-gpu") {
261231
const { solutionVector: x } = await solveLinearSystemAsync("jacobi-gpu", jacobianMatrix, residualVector, {
262232
computeEngine,
263-
maxIterations: options.maxIterations,
264-
tolerance: options.tolerance,
233+
maxIterations: options.maxIterations ?? this.maxIterations,
234+
tolerance: options.tolerance ?? this.tolerance,
265235
});
266236
solutionVector = x;
267237
} else {
268-
const { solutionVector: x } = solveLinearSystem(this.solverMethod, jacobianMatrix, residualVector);
269-
solutionVector = x;
238+
// Other async solver
270239
}
271240
}
272241
console.timeEnd("totalSolvingTime");
273242
basicLog("Solving process completed");
274243

275244
return { solutionVector, nodesCoordinates };
276245
}
246+
277247
}

src/methods/jacobiSolverScript.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@
1111
* @param {array} A - The system matrix
1212
* @param {array} b - The right-hand side vector
1313
* @param {array} x0 - Initial guess for solution vector
14-
* @param {object} [options] - Additional options for the solver
15-
* @param {number} [options.maxIterations=1000] - Maximum number of iterations
16-
* @param {number} [options.tolerance=1e-6] - Convergence tolerance
14+
* @param {object} [options] - Optional parameters for the solver, such as `maxIterations` and `tolerance`
1715
* @returns {object} An object containing:
1816
* - solutionVector: The solution vector
1917
* - iterations: The number of iterations performed
2018
* - converged: Boolean indicating whether the method converged
2119
*/
2220
export function jacobiSolver(A, b, x0, options = {}) {
23-
const { maxIterations = 1000, tolerance = 1e-6 } = options;
21+
// Extract options
22+
const { maxIterations, tolerance } = options;
23+
2424
const n = A.length;
2525
let x = [...x0];
2626
let xNew = new Array(n);

src/methods/linearSystemSolverScript.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@ import * as Comlink from "../vendor/comlink.mjs";
1616
* @param {string} solverMethod - The solver method to use ("lusolve" or "jacobi")
1717
* @param {Array} jacobianMatrix - The coefficient matrix
1818
* @param {Array} residualVector - The right-hand side vector
19-
* @param {object} [options] - Additional options for the solver
20-
* @param {number} [options.maxIterations=10000] - Maximum iterations for iterative methods
21-
* @param {number} [options.tolerance=1e-3] - Convergence tolerance for iterative methods
19+
* @param {object} [options] - Optional parameters for the solver, such as `maxIterations` and `tolerance`
2220
* @returns {object} An object containing:
2321
* - solutionVector: The solution vector
2422
* - converged: Boolean indicating whether the method converged (for iterative methods)
2523
* - iterations: Number of iterations performed (for iterative methods)
2624
*/
2725
export function solveLinearSystem(solverMethod, jacobianMatrix, residualVector, options = {}) {
28-
const { maxIterations = 10000, tolerance = 1e-3 } = options;
26+
27+
// Extract options
28+
const { maxIterations = 10000, tolerance = 1e-4 } = options;
2929

3030
let solutionVector = [];
3131
let converged = true;
@@ -85,16 +85,16 @@ async function createDefaultComputeEngine() {
8585
* @param {string} solverMethod - The solver method to use (e.g., "jacobi-gpu")
8686
* @param {array} jacobianMatrix - The coefficient matrix
8787
* @param {array} residualVector - The right-hand side vector
88-
* @param {object} [options] - Additional options for the solver
89-
* @param {number} [options.maxIterations=10000] - Maximum iterations for iterative methods
90-
* @param {number} [options.tolerance=1e-3] - Convergence tolerance for iterative methods
88+
* @param {object} [options] - Optional parameters for the solver, such as `maxIterations` and `tolerance`
9189
* @returns {Promise<object>} A promise that resolves to an object containing:
9290
* - solutionVector: The solution vector
9391
* - converged: Boolean indicating whether the method converged (for iterative methods)
9492
* - iterations: Number of iterations performed (for iterative methods)
9593
*/
9694
export async function solveLinearSystemAsync(solverMethod, jacobianMatrix, residualVector, options = {}) {
97-
const { maxIterations = 10000, tolerance = 1e-3 } = options;
95+
96+
// Extract options
97+
const { maxIterations = 10000, tolerance = 1e-4 } = options;
9898

9999
basicLog(`Solving system using ${solverMethod}...`);
100100
console.time("systemSolving");
@@ -137,7 +137,7 @@ export async function solveLinearSystemAsync(solverMethod, jacobianMatrix, resid
137137
basicLog(`System solved successfully (${solverMethod})`);
138138

139139
if (created) {
140-
await computeEngine?.destroy?.().catch(() => {});
140+
await computeEngine?.destroy?.().catch(() => { });
141141
created.worker.terminate();
142142
}
143143

src/methods/newtonRaphsonScript.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,13 @@ import { assembleFrontPropagationFront } from "../models/frontPropagationScript.
1717
* Function to solve a system of non-linear equations using the Newton-Raphson method
1818
* @param {function} assembleMat - Matrix assembler based on the physical model
1919
* @param {object} context - Context object containing simulation data and options
20-
* @param {number} [maxIterations=100] - Maximum number of iterations
21-
* @param {number} [tolerance=1e-4] - Convergence tolerance
2220
* @returns {object} An object containing:
2321
* - solutionVector: The solution vector
2422
* - iterations: The number of iterations performed
2523
* - converged: Boolean indicating whether the method converged
2624
*/
2725

28-
export function newtonRaphson(assembleMat, context, maxIterations = 100, tolerance = 1e-4) {
26+
export function newtonRaphson(assembleMat, context = {}) {
2927
let errorNorm = 0;
3028
let converged = false;
3129
let iterations = 0;
@@ -34,6 +32,9 @@ export function newtonRaphson(assembleMat, context, maxIterations = 100, toleran
3432
let jacobianMatrix = [];
3533
let residualVector = [];
3634

35+
// Extract context
36+
const { maxIterations = 100, tolerance = 1e-4 } = context;
37+
3738
// Calculate system size
3839
let totalNodes = context.meshData.nodesXCoordinates.length;
3940

src/workers/webgpuWorkerScript.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@ class webgpuFEAScriptWorker {
4949
* @param {array} A - The system matrix
5050
* @param {array} b - The right-hand side vector
5151
* @param {array} x0 - The initial guess
52-
* @param {object} [options] - Additional options for the solver
53-
* @param {number} [options.maxIterations=1000] - The maximum number of iterations
54-
* @param {number} [options.tolerance=1e-6] - The convergence tolerance
52+
* @param {object} [options] - Optional parameters for the solver, such as `maxIterations` and `tolerance`
5553
* @returns {Promise<object>} An object containing the solution vector, iterations, and convergence status
5654
*/
5755
async webgpuJacobiSolver(A, b, x0, options = {}) {

0 commit comments

Comments
 (0)