diff --git a/src/modifiers/modifier.ts b/src/modifiers/modifier.ts index 4c0e43bf..9ba7690b 100644 --- a/src/modifiers/modifier.ts +++ b/src/modifiers/modifier.ts @@ -1,10 +1,106 @@ import { ModifierInput, ModifierOutput } from "./types"; +import { LMPModifier, Data1D } from "../types"; interface ModifierProps { name: string; active: boolean; } +export interface ProcessedData1D { + data1D: Data1D | undefined; + hasData1D: boolean; + clearPerSync: boolean; +} + +/** + * Shared helper function to process Data1D from LMPModifier. + * Extracts common logic for syncing computes, fixes, and variables. + */ +export function processData1D( + lmpModifier: LMPModifier, + data1D: Data1D | undefined, + input: ModifierInput, + everything: boolean, + syncDataPoints: boolean +): ProcessedData1D { + // Get data1DNamesWrapper and extract size, then delete immediately + const data1DNamesWrapper = lmpModifier.getData1DNames(); + const data1DNamesSize = data1DNamesWrapper.size(); + const hasData1D = data1DNamesSize > 0; + data1DNamesWrapper.delete(); // Delete WASM wrapper to prevent memory leak + + if (data1DNamesSize === 0) { + return { data1D, hasData1D, clearPerSync: false }; + } + + const clearPerSync = lmpModifier.getClearPerSync(); + + if (data1D == null) { + data1D = { + data: [], + labels: [], + }; + } + + if (everything || syncDataPoints) { + // Data points is only for plotting figures + if (clearPerSync) { + // For histograms (compute rdf etc) we don't have time as x axis, so we clear every time + data1D.data = []; + } + + const lengthBeforeWeStart = data1D.data.length; // Used to avoid copying all data every time + + if (data1D.labels.length === 0) { + // First label is never visible + data1D.labels.push("x"); + } + + // Get data1DVector once before the loop for better performance + const data1DVector = lmpModifier.getData1D(); + + for (let j = 0; j < data1DNamesSize; j++) { + const lmpData = data1DVector.get(j); + + if (data1D.labels.length - 1 === j) { + // Add missing labels + data1D.labels.push(lmpData.getLabel()); + } + + const numPoints = lmpData.getNumPoints(); + const yValuesPointer = lmpData.getYValuesPointer() / 4; + const yValues = input.wasm.HEAPF32.subarray( + yValuesPointer, + yValuesPointer + numPoints, + ); + + if (j === 0) { + const xValuesPointer = lmpData.getXValuesPointer() / 4; + const xValues = input.wasm.HEAPF32.subarray( + xValuesPointer, + xValuesPointer + numPoints, + ); + for (let k = lengthBeforeWeStart; k < numPoints; k++) { + data1D.data.push([xValues[k]]); + data1D.data[k].push(yValues[k]); + } + } else { + for (let k = lengthBeforeWeStart; k < numPoints; k++) { + data1D.data[k].push(yValues[k]); + } + } + + // Delete the Data1D copy to prevent memory leak + lmpData.delete(); + } + + // Delete the vector wrapper after the loop to prevent memory leak + data1DVector.delete(); + } + + return { data1D, hasData1D, clearPerSync }; +} + class Modifier { public name: string; public key: string; diff --git a/src/modifiers/synccomputesmodifier.ts b/src/modifiers/synccomputesmodifier.ts index 069df3c9..17f8a003 100644 --- a/src/modifiers/synccomputesmodifier.ts +++ b/src/modifiers/synccomputesmodifier.ts @@ -1,5 +1,6 @@ import Modifier from "./modifier"; import { ModifierInput, ModifierOutput } from "./types"; +import { processData1D } from "./modifier"; interface SyncComputesModifierProps { name: string; @@ -52,72 +53,16 @@ class SyncComputesModifier extends Modifier { compute.yLabel = compute.lmpCompute.getYLabel(); compute.scalarValue = compute.lmpCompute.getScalarValue(); - // Get data1DNamesWrapper and extract size, then delete immediately - const data1DNamesWrapper = compute.lmpCompute.getData1DNames(); - compute.hasData1D = data1DNamesWrapper.size() > 0; - const data1DNamesSize = data1DNamesWrapper.size(); - data1DNamesWrapper.delete(); // Delete WASM wrapper to prevent memory leak - - if (data1DNamesSize > 0) { - compute.clearPerSync = compute.lmpCompute.getClearPerSync(); - - if (compute.data1D == null) { - compute.data1D = { - data: [], - labels: [], - }; - } - - if ((everything || compute.syncDataPoints) && compute.data1D) { - // Data points is only for plotting figures - if (compute.clearPerSync) { - // For histograms (compute rdf etc) we don't have time as x axis, so we clear every time - compute.data1D.data = []; - } - - const lengthBeforeWeStart = compute.data1D.data.length; // Used to avoid coping all data every time - - if (compute.data1D.labels.length === 0) { - // First label is never visible - compute.data1D.labels.push("x"); - } - - // Get data1DVector once before the loop for better performance - const data1DVector = compute.lmpCompute.getData1D(); - - for (let j = 0; j < data1DNamesSize; j++) { - const lmpData = data1DVector.get(j); - - if (compute.data1D.labels.length - 1 === j) { - // Add missing labels - compute.data1D.labels.push(lmpData.getLabel()); - } - - const xValuesPointer = lmpData.getXValuesPointer() / 4; - const yValuesPointer = lmpData.getYValuesPointer() / 4; - const xValues = input.wasm.HEAPF32.subarray( - xValuesPointer, - xValuesPointer + lmpData.getNumPoints(), - ) as Float32Array; - const yValues = input.wasm.HEAPF32.subarray( - yValuesPointer, - yValuesPointer + lmpData.getNumPoints(), - ) as Float32Array; - for (let k = lengthBeforeWeStart; k < xValues.length; k++) { - if (j === 0) { - compute.data1D.data.push([xValues[k]]); - } - compute.data1D.data[k].push(yValues[k]); - } - - // Delete the Data1D copy to prevent memory leak - lmpData.delete(); - } - - // Delete the vector wrapper after the loop to prevent memory leak - data1DVector.delete(); - } - } + const processed = processData1D( + compute.lmpCompute, + compute.data1D, + input, + everything, + compute.syncDataPoints, + ); + compute.data1D = processed.data1D; + compute.hasData1D = processed.hasData1D; + compute.clearPerSync = processed.clearPerSync; } output.computes[name] = compute; } diff --git a/src/modifiers/syncfixesmodifier.ts b/src/modifiers/syncfixesmodifier.ts index d1d54716..d0625e80 100644 --- a/src/modifiers/syncfixesmodifier.ts +++ b/src/modifiers/syncfixesmodifier.ts @@ -1,5 +1,6 @@ import Modifier from "./modifier"; import { ModifierInput, ModifierOutput } from "./types"; +import { processData1D } from "./modifier"; interface SyncFixesModifierProps { name: string; @@ -49,71 +50,16 @@ class SyncFixesModifier extends Modifier { fix.yLabel = fix.lmpFix.getYLabel(); fix.scalarValue = fix.lmpFix.getScalarValue(); - // Get data1DNamesWrapper and extract size, then delete immediately - const data1DNamesWrapper = fix.lmpFix.getData1DNames(); - fix.hasData1D = data1DNamesWrapper.size() > 0; - const data1DNamesSize = data1DNamesWrapper.size(); - data1DNamesWrapper.delete(); // Delete WASM wrapper to prevent memory leak - - if (data1DNamesSize > 0) { - fix.clearPerSync = fix.lmpFix.getClearPerSync(); - if (fix.data1D == null) { - fix.data1D = { - data: [], - labels: [], - }; - } - - if ((everything || fix.syncDataPoints) && fix.data1D) { - // Data points is only for plotting figures - if (fix.clearPerSync) { - // For histograms (compute rdf etc) we don't have time as x axis, so we clear every time - fix.data1D.data = []; - } - - const lengthBeforeWeStart = fix.data1D.data.length; // Used to avoid coping all data every time - - if (fix.data1D.labels.length === 0) { - // First label is never visible - fix.data1D.labels.push("x"); - } - - // Get data1DVector once before the loop for better performance - const data1DVector = fix.lmpFix.getData1D(); - - for (let j = 0; j < data1DNamesSize; j++) { - const lmpData = data1DVector.get(j); - - if (fix.data1D.labels.length - 1 === j) { - // Add missing labels - fix.data1D.labels.push(lmpData.getLabel()); - } - - const xValuesPointer = lmpData.getXValuesPointer() / 4; - const yValuesPointer = lmpData.getYValuesPointer() / 4; - const xValues = input.wasm.HEAPF32.subarray( - xValuesPointer, - xValuesPointer + lmpData.getNumPoints(), - ) as Float32Array; - const yValues = input.wasm.HEAPF32.subarray( - yValuesPointer, - yValuesPointer + lmpData.getNumPoints(), - ) as Float32Array; - for (let k = lengthBeforeWeStart; k < xValues.length; k++) { - if (j === 0) { - fix.data1D.data.push([xValues[k]]); - } - fix.data1D.data[k].push(yValues[k]); - } - - // Delete the Data1D copy to prevent memory leak - lmpData.delete(); - } - - // Delete the vector wrapper after the loop to prevent memory leak - data1DVector.delete(); - } - } + const processed = processData1D( + fix.lmpFix, + fix.data1D, + input, + everything, + fix.syncDataPoints, + ); + fix.data1D = processed.data1D; + fix.hasData1D = processed.hasData1D; + fix.clearPerSync = processed.clearPerSync; output.fixes[name] = fix; } }; diff --git a/src/modifiers/syncvariablesmodifier.ts b/src/modifiers/syncvariablesmodifier.ts index f84923d6..9bd380ad 100644 --- a/src/modifiers/syncvariablesmodifier.ts +++ b/src/modifiers/syncvariablesmodifier.ts @@ -1,5 +1,6 @@ import Modifier from "./modifier"; import { ModifierInput, ModifierOutput } from "./types"; +import { processData1D } from "./modifier"; interface SyncVariablesModifierProps { name: string; @@ -51,71 +52,16 @@ class SyncVariablesModifier extends Modifier { variable.scalarValue = variable.lmpVariable.getScalarValue(); variable.hasScalarData = variable.lmpVariable.hasScalarData(); - // Get data1DNamesWrapper and extract size, then delete immediately - const data1DNamesWrapper = variable.lmpVariable.getData1DNames(); - variable.hasData1D = data1DNamesWrapper.size() > 0; - const data1DNamesSize = data1DNamesWrapper.size(); - data1DNamesWrapper.delete(); // Delete WASM wrapper to prevent memory leak - - if (data1DNamesSize > 0) { - variable.clearPerSync = variable.lmpVariable.getClearPerSync(); - if (variable.data1D == null) { - variable.data1D = { - data: [], - labels: [], - }; - } - - if ((everything || variable.syncDataPoints) && variable.data1D) { - // Data points is only for plotting figures - if (variable.clearPerSync) { - // For histograms (compute rdf etc) we don't have time as x axis, so we clear every time - variable.data1D.data = []; - } - - const lengthBeforeWeStart = variable.data1D.data.length; // Used to avoid coping all data every time - - if (variable.data1D.labels.length === 0) { - // First label is never visible - variable.data1D.labels.push("x"); - } - - // Get data1DVector once before the loop for better performance - const data1DVector = variable.lmpVariable.getData1D(); - - for (let j = 0; j < data1DNamesSize; j++) { - const lmpData = data1DVector.get(j); - - if (variable.data1D.labels.length - 1 === j) { - // Add missing labels - variable.data1D.labels.push(lmpData.getLabel()); - } - - const xValuesPointer = lmpData.getXValuesPointer() / 4; - const yValuesPointer = lmpData.getYValuesPointer() / 4; - const xValues = input.wasm.HEAPF32.subarray( - xValuesPointer, - xValuesPointer + lmpData.getNumPoints(), - ) as Float32Array; - const yValues = input.wasm.HEAPF32.subarray( - yValuesPointer, - yValuesPointer + lmpData.getNumPoints(), - ) as Float32Array; - for (let k = lengthBeforeWeStart; k < xValues.length; k++) { - if (j === 0) { - variable.data1D.data.push([xValues[k]]); - } - variable.data1D.data[k].push(yValues[k]); - } - - // Delete the Data1D copy to prevent memory leak - lmpData.delete(); - } - - // Delete the vector wrapper after the loop to prevent memory leak - data1DVector.delete(); - } - } + const processed = processData1D( + variable.lmpVariable, + variable.data1D, + input, + everything, + variable.syncDataPoints, + ); + variable.data1D = processed.data1D; + variable.hasData1D = processed.hasData1D; + variable.clearPerSync = processed.clearPerSync; output.variables[name] = variable; } };