Skip to content

Commit f65bff1

Browse files
authored
Refactor CSDA range (#1202)
* Refactor parameter validation and memory allocation in AT_CSDA_range_g_cm2_multi function * Enhance documentation and improve memory management in AT_CSDA_range_g_cm2_multi function
1 parent ab5f36b commit f65bff1

File tree

1 file changed

+93
-51
lines changed

1 file changed

+93
-51
lines changed
Lines changed: 93 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,102 @@
1+
/**
2+
* Calculates the Continuous Slowing Down Approximation (CSDA) range in g/cm2 for multiple particles.
3+
* This function serves as a JavaScript wrapper for the C function AT_CSDA_range_g_cm2_multi from libAT.
4+
*
5+
* The CSDA range represents the expected path length a charged particle travels until it comes to rest,
6+
* calculated by integrating the reciprocal of the stopping power with respect to energy.
7+
*
8+
* @param {Object} parameters - Input parameters object
9+
* @param {number} parameters.n - Number of particles to calculate CSDA range for
10+
* @param {Array<number>} parameters.E_initial_MeV_u - Initial energies in MeV per nucleon
11+
* @param {Array<number>} parameters.particle_no - Particle type identifiers
12+
* @param {number} parameters.material_no - Material identifier
13+
* @returns {Array<number>} CSDA ranges in g/cm2 for each particle
14+
*/
115
export default function AT_CSDA_range_g_cm2_multi(parameters) {
2-
let at_csda_range_g_cm2_multi = Module.cwrap('AT_CSDA_range_g_cm2_multi', 'null', ['number', 'array', 'array', 'array', 'number', 'number']);
316

4-
/*********************STANDARD PARAMETER*************************/
5-
if (typeof parameters.n === "undefined") {
6-
alert("MESSAGE TO DEVELOPER: NO PARAMETER n IN OBJECT PASSED TO THIS FUNCTIONS");
7-
return "error";
8-
}
9-
let n = parameters.n;
17+
// Validate required parameters
18+
if (!parameters) {
19+
throw new Error("Parameters object is required.");
20+
}
21+
const requiredParams = ['n', 'E_initial_MeV_u', 'particle_no', 'material_no'];
22+
for (const param of requiredParams) {
23+
if (typeof parameters[param] === "undefined") {
24+
throw new Error(`Missing parameter: ${param}`);
25+
}
26+
}
1027

11-
/*********************INPUT ARRAY********************************/
12-
if (typeof parameters.E_initial_MeV_u === "undefined") {
13-
alert("MESSAGE TO DEVELOPER: NO PARAMETER E_initial_MeV_u IN OBJECT PASSED TO THIS FUNCTIONS");
14-
return "error";
15-
}
16-
let E_initial_MeV_u = parameters.E_initial_MeV_u;
17-
let E_initial_MeV_uData = new Float64Array(E_initial_MeV_u);
18-
let E_initial_MeV_uDataBytesNumber = E_initial_MeV_uData.length * E_initial_MeV_uData.BYTES_PER_ELEMENT;
19-
let E_initial_MeV_uDataPointer = Module._malloc(E_initial_MeV_uDataBytesNumber);
20-
let E_initial_MeV_uHeap = new Uint8Array(Module.HEAPF64.buffer, E_initial_MeV_uDataPointer, E_initial_MeV_uDataBytesNumber);
21-
E_initial_MeV_uHeap.set(new Uint8Array(E_initial_MeV_uData.buffer));
28+
// Extract parameters from the input object
29+
const n = parameters.n;
30+
const E_initial_MeV_u = parameters.E_initial_MeV_u;
31+
if (!Array.isArray(E_initial_MeV_u)) {
32+
throw new Error("Parameter 'E_initial_MeV_u' must be an array.");
33+
}
34+
const particle_no = parameters.particle_no;
35+
const material_no = parameters.material_no;
2236

23-
/*********************INPUT ARRAY********************************/
24-
if (typeof parameters.particle_no === "undefined") {
25-
alert("MESSAGE TO DEVELOPER: NO PARAMETER particle_no IN OBJECT PASSED TO THIS FUNCTIONS");
26-
return "error";
27-
}
28-
let particle_no = parameters.particle_no;
29-
let particle_noData = new Int32Array(particle_no);
30-
let particle_noDataBytesNumber = particle_noData.length * particle_noData.BYTES_PER_ELEMENT;
31-
let particle_noDataPointer = Module._malloc(particle_noDataBytesNumber);
32-
let particle_noHeap = new Uint8Array(Module.HEAP32.buffer, particle_noDataPointer, particle_noDataBytesNumber);
33-
particle_noHeap.set(new Uint8Array(particle_noData.buffer));
37+
// Create a JavaScript wrapper for the C function using Emscripten's cwrap
38+
// null indicates no return type (the function returns values through pointers)
39+
const at_csda_range_g_cm2_multi = Module.cwrap('AT_CSDA_range_g_cm2_multi', null, [
40+
'number', // n
41+
'number', // E_initial_MeV_u pointer
42+
'number', // E_final_MeV_u pointer
43+
'number', // particle_no pointer
44+
'number', // material_no
45+
'number' // CSDA_range_g_cm2 pointer
46+
]);
47+
48+
// Allocate memory for the initial energy array and copy data to Emscripten's heap
49+
const E_initial_MeV_uData = new Float64Array(E_initial_MeV_u);
50+
const E_initial_MeV_uDataBytesNumber = E_initial_MeV_uData.length * E_initial_MeV_uData.BYTES_PER_ELEMENT;
51+
const E_initial_MeV_uDataPointer = Module._malloc(E_initial_MeV_uDataBytesNumber);
52+
const E_initial_MeV_uHeap = new Uint8Array(Module.HEAPF64.buffer, E_initial_MeV_uDataPointer, E_initial_MeV_uDataBytesNumber);
53+
E_initial_MeV_uHeap.set(new Uint8Array(E_initial_MeV_uData.buffer));
3454

35-
/*********************STANDARD PARAMETER*************************/
36-
if (typeof parameters.material_no === "undefined") {
37-
alert("MESSAGE TO DEVELOPER: NO PARAMETER material_no IN OBJECT PASSED TO THIS FUNCTIONS");
38-
return "error";
39-
}
40-
let material_no = parameters.material_no;
55+
// Create final energy array (all zeros for complete stopping)
56+
// The final energy is set to 0 MeV/u to calculate the full CSDA range
57+
const E_final_MeV_uData = new Float64Array(n);
58+
for (let i = 0; i < E_final_MeV_uData.length; i++) {
59+
E_final_MeV_uData[i] = 0.0;
60+
}
61+
const E_final_MeV_uDataBytesNumber = E_final_MeV_uData.length * E_final_MeV_uData.BYTES_PER_ELEMENT;
62+
const E_final_MeV_uDataPointer = Module._malloc(E_final_MeV_uDataBytesNumber);
63+
const E_final_MeV_uHeap = new Uint8Array(Module.HEAPF64.buffer, E_final_MeV_uDataPointer, E_final_MeV_uDataBytesNumber);
64+
E_final_MeV_uHeap.set(new Uint8Array(E_final_MeV_uData.buffer));
4165

42-
/*********************OUTPUT ARRAY*******************************/
43-
let CSDA_range_g_cm2ReturnData = new Float64Array(new Array(n));
44-
let CSDA_range_g_cm2ReturnDataBytesNumber = CSDA_range_g_cm2ReturnData.length * CSDA_range_g_cm2ReturnData.BYTES_PER_ELEMENT;
45-
let CSDA_range_g_cm2ReturnDataPointer = Module._malloc(CSDA_range_g_cm2ReturnDataBytesNumber);
46-
let CSDA_range_g_cm2ReturnHeap = new Uint8Array(Module.HEAPF64.buffer, CSDA_range_g_cm2ReturnDataPointer, CSDA_range_g_cm2ReturnDataBytesNumber);
66+
// Create array for particle_no, using Int32Array for particle identifiers
67+
const particle_noData = new Int32Array(particle_no);
68+
const particle_noDataBytesNumber = particle_noData.length * particle_noData.BYTES_PER_ELEMENT;
69+
const particle_noDataPointer = Module._malloc(particle_noDataBytesNumber);
70+
const particle_noHeap = new Uint8Array(Module.HEAP32.buffer, particle_noDataPointer, particle_noDataBytesNumber);
71+
particle_noHeap.set(new Uint8Array(particle_noData.buffer));
4772

48-
/*********************CALL FUNCTION******************************/
73+
// Create output array to store the calculated CSDA ranges
74+
const CSDA_range_g_cm2ReturnData = new Float64Array(n);
75+
const CSDA_range_g_cm2ReturnDataBytesNumber = CSDA_range_g_cm2ReturnData.length * CSDA_range_g_cm2ReturnData.BYTES_PER_ELEMENT;
76+
const CSDA_range_g_cm2ReturnDataPointer = Module._malloc(CSDA_range_g_cm2ReturnDataBytesNumber);
4977

50-
// calculate CSDA range for completely slowing down ions
51-
let E_final_MeV_uData = new Float64Array(n);
52-
for (let i = 0; i < E_final_MeV_uData.length; i++) {
53-
E_final_MeV_uData[i] = 0.0;
54-
}
55-
56-
let result = at_csda_range_g_cm2_multi(n, E_initial_MeV_uHeap, E_final_MeV_uData, particle_noHeap, material_no, CSDA_range_g_cm2ReturnHeap.byteOffset);
57-
let resultFromArray = new Float64Array(CSDA_range_g_cm2ReturnHeap.buffer, CSDA_range_g_cm2ReturnHeap.byteOffset, CSDA_range_g_cm2ReturnData.length);
58-
59-
return [].slice.call(resultFromArray);
78+
try {
79+
// Call the C function with pointers to the allocated memory
80+
at_csda_range_g_cm2_multi(
81+
n,
82+
E_initial_MeV_uDataPointer,
83+
E_final_MeV_uDataPointer,
84+
particle_noDataPointer,
85+
material_no,
86+
CSDA_range_g_cm2ReturnDataPointer
87+
);
88+
89+
// Create a view of the results in the Emscripten heap
90+
const resultFromArray = new Float64Array(Module.HEAPF64.buffer, CSDA_range_g_cm2ReturnDataPointer, n);
91+
92+
// Return a JavaScript array with the results
93+
return Array.from(resultFromArray);
94+
95+
} finally {
96+
// // Free the allocated memory to prevent memory leaks
97+
// Module._free(E_initial_MeV_uDataPointer);
98+
// Module._free(E_final_MeV_uDataPointer);
99+
// Module._free(particle_noDataPointer);
100+
// Module._free(CSDA_range_g_cm2ReturnDataPointer);
101+
}
60102
}

0 commit comments

Comments
 (0)