matterviz 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/brillouin/BrillouinZone.svelte +68 -145
- package/dist/brillouin/BrillouinZone.svelte.d.ts +5 -14
- package/dist/brillouin/BrillouinZoneExportPane.svelte +43 -96
- package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +9 -32
- package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +2 -3
- package/dist/brillouin/BrillouinZoneScene.svelte +49 -203
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +3 -23
- package/dist/brillouin/ReciprocalVectors.svelte +39 -0
- package/dist/brillouin/ReciprocalVectors.svelte.d.ts +9 -0
- package/dist/brillouin/compute.d.ts +2 -0
- package/dist/brillouin/compute.js +80 -77
- package/dist/brillouin/geometry.d.ts +8 -0
- package/dist/brillouin/geometry.js +57 -0
- package/dist/brillouin/index.d.ts +2 -0
- package/dist/brillouin/index.js +2 -0
- package/dist/brillouin/types.d.ts +2 -2
- package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +1 -1
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +100 -191
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +4 -1
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +176 -464
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +7 -1
- package/dist/chempot-diagram/color.d.ts +3 -6
- package/dist/chempot-diagram/color.js +5 -5
- package/dist/chempot-diagram/compute.d.ts +3 -3
- package/dist/chempot-diagram/compute.js +3 -1
- package/dist/chempot-diagram/controls-state.svelte.d.ts +10 -0
- package/dist/chempot-diagram/controls-state.svelte.js +42 -0
- package/dist/chempot-diagram/export.d.ts +47 -0
- package/dist/chempot-diagram/export.js +133 -0
- package/dist/chempot-diagram/index.d.ts +1 -0
- package/dist/chempot-diagram/index.js +1 -0
- package/dist/chempot-diagram/pointer.d.ts +0 -10
- package/dist/chempot-diagram/pointer.js +4 -4
- package/dist/chempot-diagram/types.d.ts +3 -3
- package/dist/colors/index.js +2 -2
- package/dist/composition/FormulaFilter.svelte +6 -5
- package/dist/composition/PieChart.svelte +5 -5
- package/dist/composition/chem-sys.js +3 -2
- package/dist/composition/format.js +3 -2
- package/dist/composition/parse.d.ts +0 -1
- package/dist/composition/parse.js +17 -19
- package/dist/controls.d.ts +1 -0
- package/dist/controls.js +0 -1
- package/dist/convex-hull/ConvexHull.svelte +8 -10
- package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -4
- package/dist/convex-hull/ConvexHull2D.svelte +94 -175
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +176 -680
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +180 -680
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullChrome.svelte +268 -0
- package/dist/convex-hull/ConvexHullChrome.svelte.d.ts +30 -0
- package/dist/convex-hull/ConvexHullControls.svelte +88 -7
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +7 -6
- package/dist/convex-hull/ConvexHullInfoPane.svelte +18 -5
- package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +6 -5
- package/dist/convex-hull/ConvexHullStats.svelte +29 -168
- package/dist/convex-hull/ConvexHullStats.svelte.d.ts +3 -1
- package/dist/convex-hull/ConvexHullTooltip.svelte +11 -2
- package/dist/convex-hull/ConvexHullTooltip.svelte.d.ts +2 -1
- package/dist/convex-hull/barycentric-coords.d.ts +2 -4
- package/dist/convex-hull/barycentric-coords.js +6 -33
- package/dist/convex-hull/canvas-interactions.svelte.d.ts +79 -0
- package/dist/convex-hull/canvas-interactions.svelte.js +278 -0
- package/dist/convex-hull/helpers.d.ts +39 -7
- package/dist/convex-hull/helpers.js +154 -69
- package/dist/convex-hull/hull-state.svelte.d.ts +44 -0
- package/dist/convex-hull/hull-state.svelte.js +124 -0
- package/dist/convex-hull/index.d.ts +9 -7
- package/dist/convex-hull/index.js +7 -2
- package/dist/convex-hull/thermodynamics.js +91 -920
- package/dist/convex-hull/types.d.ts +12 -4
- package/dist/convex-hull/types.js +12 -0
- package/dist/coordination/CoordinationBarPlot.svelte +4 -11
- package/dist/element/BohrAtom.svelte +2 -1
- package/dist/element/ElementTile.svelte.d.ts +1 -1
- package/dist/element/index.d.ts +4 -0
- package/dist/element/index.js +18 -0
- package/dist/feedback/DragOverlay.svelte +3 -1
- package/dist/feedback/DragOverlay.svelte.d.ts +1 -0
- package/dist/feedback/StatusMessage.svelte +13 -3
- package/dist/fermi-surface/FermiSurface.svelte +67 -146
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +5 -14
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +72 -224
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +3 -23
- package/dist/fermi-surface/compute.js +11 -10
- package/dist/fermi-surface/export.js +4 -15
- package/dist/fermi-surface/index.d.ts +0 -1
- package/dist/fermi-surface/index.js +0 -1
- package/dist/fermi-surface/parse.d.ts +1 -1
- package/dist/fermi-surface/parse.js +64 -75
- package/dist/fermi-surface/types.d.ts +2 -2
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +55 -40
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +4 -3
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +3 -2
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +5 -5
- package/dist/heatmap-matrix/index.d.ts +3 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/io/ExportPane.svelte +166 -0
- package/dist/io/ExportPane.svelte.d.ts +17 -0
- package/dist/io/decompress.js +1 -2
- package/dist/io/export.d.ts +5 -1
- package/dist/io/export.js +32 -28
- package/dist/io/fetch.d.ts +2 -1
- package/dist/io/file-drop.d.ts +7 -0
- package/dist/io/file-drop.js +13 -0
- package/dist/io/index.d.ts +2 -0
- package/dist/io/index.js +10 -0
- package/dist/io/types.d.ts +13 -0
- package/dist/isosurface/parse.js +46 -44
- package/dist/labels.js +1 -1
- package/dist/layout/FullscreenButton.svelte +33 -0
- package/dist/layout/FullscreenButton.svelte.d.ts +10 -0
- package/dist/layout/FullscreenToggle.svelte +8 -14
- package/dist/layout/ViewerChrome.svelte +116 -0
- package/dist/layout/ViewerChrome.svelte.d.ts +17 -0
- package/dist/layout/fullscreen.d.ts +4 -0
- package/dist/layout/fullscreen.svelte.d.ts +8 -0
- package/dist/layout/fullscreen.svelte.js +37 -0
- package/dist/layout/index.d.ts +3 -0
- package/dist/layout/index.js +3 -0
- package/dist/math.d.ts +7 -3
- package/dist/math.js +18 -21
- package/dist/overlays/index.d.ts +4 -0
- package/dist/periodic-table/PeriodicTable.svelte +9 -8
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramControls.svelte +3 -2
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +4 -3
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +2 -3
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +47 -132
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +3 -4
- package/dist/phase-diagram/colors.js +1 -1
- package/dist/phase-diagram/parse.d.ts +2 -1
- package/dist/plot/bar/BarPlot.svelte +79 -316
- package/dist/plot/bar/BarPlot.svelte.d.ts +7 -15
- package/dist/plot/bar/BarPlotControls.svelte.d.ts +1 -1
- package/dist/plot/bar/SpacegroupBarPlot.svelte +2 -1
- package/dist/plot/box/BoxPlot.svelte +76 -246
- package/dist/plot/box/BoxPlot.svelte.d.ts +4 -3
- package/dist/plot/box/BoxPlotControls.svelte.d.ts +1 -1
- package/dist/plot/box/Violin.svelte.d.ts +1 -1
- package/dist/plot/box/box-plot.d.ts +3 -2
- package/dist/plot/box/box-plot.js +5 -2
- package/dist/plot/box/kde.d.ts +2 -1
- package/dist/plot/box/kde.js +4 -4
- package/dist/plot/core/auto-place.d.ts +1 -1
- package/dist/plot/core/auto-place.js +4 -1
- package/dist/plot/core/components/ColorBar.svelte +5 -5
- package/dist/plot/core/components/ColorBar.svelte.d.ts +5 -4
- package/dist/plot/core/components/Line.svelte +3 -2
- package/dist/plot/core/components/Line.svelte.d.ts +3 -2
- package/dist/plot/core/components/PlotAxis.svelte +2 -1
- package/dist/plot/core/components/PlotAxis.svelte.d.ts +2 -1
- package/dist/plot/core/components/PlotControls.svelte.d.ts +1 -1
- package/dist/plot/core/components/ReferenceLine3D.svelte +2 -2
- package/dist/plot/core/components/ReferenceLine3D.svelte.d.ts +4 -4
- package/dist/plot/core/components/ReferencePlane.svelte +2 -2
- package/dist/plot/core/components/ReferencePlane.svelte.d.ts +4 -4
- package/dist/plot/core/data-cleaning.js +18 -18
- package/dist/plot/core/fill-utils.d.ts +4 -3
- package/dist/plot/core/fill-utils.js +6 -3
- package/dist/plot/core/interactions.d.ts +5 -1
- package/dist/plot/core/interactions.js +14 -0
- package/dist/plot/core/pan-zoom.svelte.d.ts +35 -0
- package/dist/plot/core/pan-zoom.svelte.js +221 -0
- package/dist/plot/core/placed-tween.svelte.d.ts +21 -0
- package/dist/plot/core/placed-tween.svelte.js +68 -0
- package/dist/plot/core/reference-line.d.ts +10 -10
- package/dist/plot/core/reference-line.js +6 -6
- package/dist/plot/core/scales.d.ts +17 -25
- package/dist/plot/core/scales.js +10 -8
- package/dist/plot/core/svg.d.ts +2 -1
- package/dist/plot/core/types.d.ts +18 -7
- package/dist/plot/core/utils/label-placement.d.ts +1 -1
- package/dist/plot/core/utils/label-placement.js +3 -3
- package/dist/plot/core/utils.d.ts +2 -1
- package/dist/plot/histogram/Histogram.svelte +77 -314
- package/dist/plot/histogram/HistogramControls.svelte.d.ts +1 -1
- package/dist/plot/sankey/Sankey.svelte +2 -5
- package/dist/plot/sankey/Sankey.svelte.d.ts +1 -1
- package/dist/plot/sankey/sankey.js +3 -1
- package/dist/plot/scatter/BinnedScatterPlot.svelte +3 -5
- package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +4 -4
- package/dist/plot/scatter/ScatterPlot.svelte +160 -450
- package/dist/plot/scatter/ScatterPlot.svelte.d.ts +7 -15
- package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/scatter/binned-scatter-types.d.ts +4 -11
- package/dist/plot/scatter/index.d.ts +1 -1
- package/dist/plot/scatter-3d/ScatterPlot3D.svelte +15 -26
- package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +6 -14
- package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +9 -10
- package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte.d.ts +5 -5
- package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +122 -121
- package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +5 -14
- package/dist/plot/scatter-3d/Surface3D.svelte +6 -5
- package/dist/plot/scatter-3d/Surface3D.svelte.d.ts +4 -3
- package/dist/plot/sunburst/Sunburst.svelte +16 -20
- package/dist/plot/sunburst/Sunburst.svelte.d.ts +4 -3
- package/dist/plot/sunburst/SunburstControls.svelte.d.ts +1 -1
- package/dist/plot/sunburst/sunburst.js +4 -1
- package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
- package/dist/sanitize.js +13 -2
- package/dist/scene/SceneCamera.svelte +62 -0
- package/dist/scene/SceneCamera.svelte.d.ts +19 -0
- package/dist/scene/bind-renderer.svelte.d.ts +2 -0
- package/dist/scene/bind-renderer.svelte.js +14 -0
- package/dist/scene/index.d.ts +4 -0
- package/dist/scene/index.js +5 -0
- package/dist/scene/props.js +52 -0
- package/dist/scene/types.d.ts +26 -0
- package/dist/scene/types.js +1 -0
- package/dist/settings.d.ts +14 -2
- package/dist/settings.js +59 -1
- package/dist/spectral/Bands.svelte +8 -7
- package/dist/spectral/Bands.svelte.d.ts +3 -2
- package/dist/spectral/BandsAndDos.svelte +22 -24
- package/dist/spectral/BrillouinBandsDos.svelte +3 -3
- package/dist/spectral/Dos.svelte +5 -4
- package/dist/spectral/Dos.svelte.d.ts +2 -1
- package/dist/spectral/helpers.d.ts +6 -6
- package/dist/spectral/helpers.js +43 -37
- package/dist/state.svelte.d.ts +0 -7
- package/dist/state.svelte.js +0 -6
- package/dist/structure/Arrow.svelte +2 -4
- package/dist/structure/AtomLegend.svelte.d.ts +1 -1
- package/dist/structure/CanvasTooltip.svelte +1 -0
- package/dist/structure/CellSelect.svelte +11 -3
- package/dist/structure/CellSelect.svelte.d.ts +2 -1
- package/dist/structure/Lattice.svelte +2 -2
- package/dist/structure/Structure.svelte +291 -355
- package/dist/structure/Structure.svelte.d.ts +5 -15
- package/dist/structure/StructureControls.svelte +217 -2
- package/dist/structure/StructureControls.svelte.d.ts +5 -3
- package/dist/structure/StructureExportPane.svelte +54 -156
- package/dist/structure/StructureExportPane.svelte.d.ts +4 -5
- package/dist/structure/StructureInfoPane.svelte +5 -3
- package/dist/structure/StructureInfoPane.svelte.d.ts +5 -5
- package/dist/structure/StructureScene.svelte +365 -198
- package/dist/structure/StructureScene.svelte.d.ts +22 -20
- package/dist/structure/{label-placement.d.ts → atom-label-placement.d.ts} +3 -3
- package/dist/structure/{label-placement.js → atom-label-placement.js} +12 -2
- package/dist/structure/atom-properties.d.ts +1 -1
- package/dist/structure/atom-properties.js +11 -16
- package/dist/structure/bond-order-perception.js +2 -4
- package/dist/structure/bonding.d.ts +3 -0
- package/dist/structure/bonding.js +91 -48
- package/dist/structure/export.d.ts +24 -4
- package/dist/structure/export.js +64 -122
- package/dist/structure/index.d.ts +2 -0
- package/dist/structure/index.js +2 -0
- package/dist/structure/parse.d.ts +3 -2
- package/dist/structure/parse.js +333 -370
- package/dist/structure/partial-occupancy.d.ts +0 -1
- package/dist/structure/partial-occupancy.js +1 -1
- package/dist/structure/pbc.d.ts +1 -1
- package/dist/structure/pbc.js +186 -13
- package/dist/structure/polyhedra.d.ts +41 -0
- package/dist/structure/polyhedra.js +602 -0
- package/dist/structure/site.d.ts +4 -0
- package/dist/structure/site.js +1 -0
- package/dist/structure/supercell.js +3 -2
- package/dist/structure/validation.js +5 -6
- package/dist/symmetry/SymmetryElementControls.svelte +69 -0
- package/dist/symmetry/SymmetryElementControls.svelte.d.ts +9 -0
- package/dist/symmetry/SymmetryElements.svelte +354 -0
- package/dist/symmetry/SymmetryElements.svelte.d.ts +24 -0
- package/dist/symmetry/SymmetryStats.svelte +111 -6
- package/dist/symmetry/WyckoffTable.svelte +68 -7
- package/dist/symmetry/WyckoffTable.svelte.d.ts +3 -0
- package/dist/symmetry/cell-transform.js +7 -14
- package/dist/symmetry/index.d.ts +14 -4
- package/dist/symmetry/index.js +301 -80
- package/dist/symmetry/spacegroups.d.ts +5 -1
- package/dist/symmetry/spacegroups.js +15 -1
- package/dist/symmetry/symmetry-elements.d.ts +33 -0
- package/dist/symmetry/symmetry-elements.js +521 -0
- package/dist/symmetry/wyckoff-db.d.ts +9 -0
- package/dist/symmetry/wyckoff-db.js +87 -0
- package/dist/table/HeatmapTable.svelte +4 -15
- package/dist/table/HeatmapTable.svelte.d.ts +1 -1
- package/dist/trajectory/Trajectory.svelte +58 -61
- package/dist/trajectory/Trajectory.svelte.d.ts +10 -22
- package/dist/trajectory/TrajectoryExportPane.svelte +15 -24
- package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +4 -5
- package/dist/trajectory/TrajectoryInfoPane.svelte +3 -2
- package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +3 -2
- package/dist/trajectory/constants.js +6 -2
- package/dist/trajectory/extract.js +17 -37
- package/dist/trajectory/format-detect.d.ts +0 -1
- package/dist/trajectory/format-detect.js +3 -9
- package/dist/trajectory/frame-reader.d.ts +0 -1
- package/dist/trajectory/frame-reader.js +62 -128
- package/dist/trajectory/helpers.d.ts +10 -2
- package/dist/trajectory/helpers.js +56 -36
- package/dist/trajectory/parse/ase.d.ts +9 -1
- package/dist/trajectory/parse/ase.js +47 -32
- package/dist/trajectory/parse/diagnostics.d.ts +3 -0
- package/dist/trajectory/parse/diagnostics.js +14 -0
- package/dist/trajectory/parse/index.d.ts +1 -1
- package/dist/trajectory/parse/index.js +54 -102
- package/dist/trajectory/parse/lammps.d.ts +0 -2
- package/dist/trajectory/parse/lammps.js +8 -6
- package/dist/trajectory/parse/pymatgen.d.ts +2 -0
- package/dist/trajectory/parse/pymatgen.js +74 -0
- package/dist/trajectory/parse/vasp.js +4 -3
- package/dist/trajectory/parse/xyz.d.ts +9 -21
- package/dist/trajectory/parse/xyz.js +28 -33
- package/dist/trajectory/plotting.d.ts +0 -1
- package/dist/trajectory/plotting.js +3 -100
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +1 -1
- package/dist/xrd/XrdPlot.svelte +14 -29
- package/dist/xrd/broadening.d.ts +2 -1
- package/dist/xrd/calc-xrd.js +6 -11
- package/dist/xrd/index.d.ts +2 -2
- package/package.json +29 -16
- package/dist/element/data.json +0 -11864
- package/dist/fermi-surface/marching-cubes.d.ts +0 -2
- package/dist/fermi-surface/marching-cubes.js +0 -2
- package/dist/plot/core/hover-lock.svelte.d.ts +0 -14
- package/dist/plot/core/hover-lock.svelte.js +0 -45
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
// Unified frame loader for XYZ and ASE trajectories (large file indexing)
|
|
2
2
|
import * as math from '../math';
|
|
3
3
|
import { MAX_METADATA_SIZE } from './constants';
|
|
4
|
-
import {
|
|
5
|
-
import { strip_compression_extensions } from '
|
|
6
|
-
import {
|
|
4
|
+
import { copy_numeric_fields, count_xyz_frames, iter_xyz_frames, validate_3x3_matrix, } from './helpers';
|
|
5
|
+
import { strip_compression_extensions } from '../io';
|
|
6
|
+
import { decode_ase_frame, read_ase_header } from './parse/ase';
|
|
7
|
+
import { build_xyz_frame, parse_xyz_comment_metadata } from './parse/xyz';
|
|
8
|
+
// Restrict frame metadata to the requested property keys (no-op when unset)
|
|
9
|
+
const filter_properties = (metadata, properties) => {
|
|
10
|
+
if (!properties)
|
|
11
|
+
return;
|
|
12
|
+
metadata.properties = Object.fromEntries(Object.entries(metadata.properties).filter(([key]) => properties.includes(key)));
|
|
13
|
+
};
|
|
7
14
|
export class TrajFrameReader {
|
|
8
15
|
format;
|
|
9
16
|
global_numbers;
|
|
@@ -19,8 +26,7 @@ export class TrajFrameReader {
|
|
|
19
26
|
}
|
|
20
27
|
if (!(data instanceof ArrayBuffer))
|
|
21
28
|
throw new Error(`ASE loader requires binary data`);
|
|
22
|
-
|
|
23
|
-
return Number(view.getBigInt64(32, true));
|
|
29
|
+
return read_ase_header(new DataView(data)).n_items;
|
|
24
30
|
}
|
|
25
31
|
async build_frame_index(data, sample_rate, on_progress) {
|
|
26
32
|
const total_frames = await this.get_total_frames(data);
|
|
@@ -31,35 +37,25 @@ export class TrajFrameReader {
|
|
|
31
37
|
const encoder = new TextEncoder();
|
|
32
38
|
const newline_sequence = data_str.includes(`\r\n`) ? `\r\n` : `\n`;
|
|
33
39
|
const newline_byte_len = encoder.encode(newline_sequence).length;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
40
|
+
const line_bytes = (idx) => encoder.encode(lines[idx]).length + newline_byte_len;
|
|
41
|
+
// cursor = next line whose bytes haven't been added to byte_offset yet
|
|
42
|
+
let [current_frame, cursor, byte_offset] = [0, 0, 0];
|
|
43
|
+
for (const { start, num_atoms } of iter_xyz_frames(lines)) {
|
|
44
|
+
if (current_frame >= total_frames)
|
|
45
|
+
break;
|
|
46
|
+
// Accumulate bytes of blank/invalid lines skipped before this frame
|
|
47
|
+
for (; cursor < start; cursor++)
|
|
48
|
+
byte_offset += line_bytes(cursor);
|
|
49
|
+
let frame_size = 0;
|
|
50
|
+
for (; cursor < start + num_atoms + 2; cursor++)
|
|
51
|
+
frame_size += line_bytes(cursor);
|
|
47
52
|
if (current_frame % sample_rate === 0) {
|
|
48
53
|
frame_index.push({
|
|
49
54
|
frame_number: current_frame,
|
|
50
55
|
byte_offset,
|
|
51
|
-
estimated_size:
|
|
56
|
+
estimated_size: frame_size,
|
|
52
57
|
});
|
|
53
58
|
}
|
|
54
|
-
const frame_start = line_idx;
|
|
55
|
-
line_idx += 2 + num_atoms;
|
|
56
|
-
let frame_size = 0;
|
|
57
|
-
for (let idx = frame_start; idx < line_idx; idx++) {
|
|
58
|
-
frame_size += encoder.encode(lines[idx]).length + newline_byte_len;
|
|
59
|
-
}
|
|
60
|
-
if (current_frame % sample_rate === 0) {
|
|
61
|
-
frame_index[frame_index.length - 1].estimated_size = frame_size;
|
|
62
|
-
}
|
|
63
59
|
byte_offset += frame_size;
|
|
64
60
|
current_frame++;
|
|
65
61
|
if (on_progress && current_frame % 1000 === 0) {
|
|
@@ -73,7 +69,7 @@ export class TrajFrameReader {
|
|
|
73
69
|
}
|
|
74
70
|
else {
|
|
75
71
|
const view = new DataView(data);
|
|
76
|
-
const offsets_pos =
|
|
72
|
+
const { offsets_pos } = read_ase_header(view);
|
|
77
73
|
for (let idx = 0; idx < total_frames; idx += sample_rate) {
|
|
78
74
|
const frame_offset = Number(view.getBigInt64(offsets_pos + idx * 8, true));
|
|
79
75
|
frame_index.push({
|
|
@@ -111,34 +107,28 @@ export class TrajFrameReader {
|
|
|
111
107
|
const total_frames = await this.get_total_frames(data);
|
|
112
108
|
if (this.format === `xyz`) {
|
|
113
109
|
const lines = data.trim().split(/\r?\n/);
|
|
114
|
-
let
|
|
115
|
-
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
const num_atoms = parseInt(lines[line_idx].trim(), 10);
|
|
121
|
-
if (isNaN(num_atoms) || num_atoms <= 0 || line_idx + num_atoms + 1 >= lines.length) {
|
|
122
|
-
line_idx++;
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
110
|
+
let current_frame = 0;
|
|
111
|
+
for (const { start, comment } of iter_xyz_frames(lines)) {
|
|
112
|
+
if (current_frame >= total_frames)
|
|
113
|
+
break;
|
|
125
114
|
if (current_frame % sample_rate === 0) {
|
|
126
|
-
const comment = lines[line_idx + 1] || ``;
|
|
127
115
|
let frame_metadata = null;
|
|
128
116
|
try {
|
|
129
|
-
|
|
117
|
+
const { step, properties: props } = parse_xyz_comment_metadata(comment);
|
|
118
|
+
frame_metadata = {
|
|
119
|
+
frame_number: current_frame,
|
|
120
|
+
step: step ?? current_frame,
|
|
121
|
+
properties: props,
|
|
122
|
+
};
|
|
130
123
|
}
|
|
131
124
|
catch (error) {
|
|
132
|
-
console.warn(`Failed to parse XYZ metadata for frame ${current_frame} at line ${
|
|
133
|
-
}
|
|
134
|
-
if (frame_metadata && properties) {
|
|
135
|
-
const filtered = Object.fromEntries(Object.entries(frame_metadata.properties).filter(([key]) => properties.includes(key)));
|
|
136
|
-
frame_metadata.properties = filtered;
|
|
125
|
+
console.warn(`Failed to parse XYZ metadata for frame ${current_frame} at line ${start + 1}:`, error);
|
|
137
126
|
}
|
|
138
|
-
if (frame_metadata)
|
|
127
|
+
if (frame_metadata) {
|
|
128
|
+
filter_properties(frame_metadata, properties);
|
|
139
129
|
metadata_list.push(frame_metadata);
|
|
130
|
+
}
|
|
140
131
|
}
|
|
141
|
-
line_idx += 2 + num_atoms;
|
|
142
132
|
current_frame++;
|
|
143
133
|
if (on_progress && current_frame % 5000 === 0) {
|
|
144
134
|
on_progress({
|
|
@@ -151,8 +141,7 @@ export class TrajFrameReader {
|
|
|
151
141
|
}
|
|
152
142
|
else if (this.format === `ase`) {
|
|
153
143
|
const view = new DataView(data);
|
|
154
|
-
const n_items =
|
|
155
|
-
const offsets_pos = Number(view.getBigInt64(40, true));
|
|
144
|
+
const { n_items, offsets_pos } = read_ase_header(view);
|
|
156
145
|
for (let idx = 0; idx < n_items; idx += sample_rate) {
|
|
157
146
|
try {
|
|
158
147
|
const frame_offset = Number(view.getBigInt64(offsets_pos + idx * 8, true));
|
|
@@ -163,10 +152,7 @@ export class TrajFrameReader {
|
|
|
163
152
|
}
|
|
164
153
|
const frame_data = JSON.parse(new TextDecoder().decode(new Uint8Array(data, frame_offset + 8, json_length)));
|
|
165
154
|
const frame_metadata = this.parse_ase_metadata(frame_data, idx);
|
|
166
|
-
|
|
167
|
-
const filtered = Object.fromEntries(Object.entries(frame_metadata.properties).filter(([key]) => properties.includes(key)));
|
|
168
|
-
frame_metadata.properties = filtered;
|
|
169
|
-
}
|
|
155
|
+
filter_properties(frame_metadata, properties);
|
|
170
156
|
metadata_list.push(frame_metadata);
|
|
171
157
|
if (on_progress && idx % 5000 === 0) {
|
|
172
158
|
on_progress({
|
|
@@ -185,105 +171,53 @@ export class TrajFrameReader {
|
|
|
185
171
|
}
|
|
186
172
|
load_xyz_frame(data, frame_number) {
|
|
187
173
|
const lines = data.trim().split(/\r?\n/);
|
|
188
|
-
let
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
current_frame++;
|
|
174
|
+
let current_frame = 0;
|
|
175
|
+
for (const frame of iter_xyz_frames(lines)) {
|
|
176
|
+
if (current_frame++ < frame_number)
|
|
177
|
+
continue; // skip frames before the target
|
|
178
|
+
return build_xyz_frame(lines, frame, {
|
|
179
|
+
frame_label: `indexed frame ${frame_number}`,
|
|
180
|
+
default_step: frame_number,
|
|
181
|
+
});
|
|
197
182
|
}
|
|
198
|
-
|
|
199
|
-
return null;
|
|
200
|
-
const num_atoms = parseInt(lines[line_idx].trim(), 10);
|
|
201
|
-
if (isNaN(num_atoms) || line_idx + num_atoms + 1 >= lines.length)
|
|
202
|
-
return null;
|
|
203
|
-
const comment = lines[line_idx + 1] || ``;
|
|
204
|
-
const lattice_matrix = parse_extxyz_lattice(comment);
|
|
205
|
-
const { elements, positions, force_stats } = parse_xyz_atom_lines(lines, line_idx + 2, num_atoms, comment, `indexed frame ${frame_number}`);
|
|
206
|
-
const { step, properties } = this.parse_xyz_metadata(comment, frame_number);
|
|
207
|
-
const metadata = { ...properties, ...force_stats };
|
|
208
|
-
// Derive volume from the lattice (parity with the eager parse_xyz_trajectory parser)
|
|
209
|
-
if (lattice_matrix)
|
|
210
|
-
metadata.volume = math.calc_lattice_params(lattice_matrix).volume;
|
|
211
|
-
return create_trajectory_frame(positions, elements, lattice_matrix, lattice_matrix ? [true, true, true] : undefined, step, metadata);
|
|
183
|
+
return null;
|
|
212
184
|
}
|
|
213
185
|
load_ase_frame(data, frame_number) {
|
|
214
186
|
try {
|
|
215
187
|
const view = new DataView(data);
|
|
216
|
-
const n_items =
|
|
217
|
-
const offsets_pos = Number(view.getBigInt64(40, true));
|
|
188
|
+
const { n_items, offsets_pos } = read_ase_header(view);
|
|
218
189
|
if (frame_number >= n_items)
|
|
219
190
|
return null;
|
|
220
191
|
const frame_offset = Number(view.getBigInt64(offsets_pos + frame_number * 8, true));
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const positions = positions_ref?.ndarray
|
|
225
|
-
? read_ndarray_from_view(view, positions_ref)
|
|
226
|
-
: positions_ref;
|
|
227
|
-
const numbers_ref = frame_data[`numbers.`] ?? frame_data.numbers ?? this.global_numbers;
|
|
228
|
-
const numbers = numbers_ref?.ndarray
|
|
229
|
-
? read_ndarray_from_view(view, numbers_ref).flat()
|
|
230
|
-
: numbers_ref;
|
|
231
|
-
if (numbers)
|
|
232
|
-
this.global_numbers = numbers;
|
|
233
|
-
if (!numbers || !positions)
|
|
234
|
-
throw new Error(`Missing atomic numbers or positions`);
|
|
235
|
-
const cell = frame_data.cell ? validate_3x3_matrix(frame_data.cell) : undefined;
|
|
236
|
-
const metadata = {
|
|
237
|
-
step: frame_number,
|
|
238
|
-
...frame_data.calculator,
|
|
239
|
-
...frame_data.info,
|
|
240
|
-
};
|
|
241
|
-
if (cell) {
|
|
242
|
-
try {
|
|
243
|
-
metadata.volume = Math.abs(math.det_3x3(cell));
|
|
244
|
-
}
|
|
245
|
-
catch (error) {
|
|
246
|
-
console.warn(`Failed to calculate volume for frame ${frame_number}:`, error);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
return create_trajectory_frame(positions, convert_atomic_numbers(numbers), cell, frame_data.pbc ?? [true, true, true], frame_number, metadata);
|
|
192
|
+
const { frame, numbers } = decode_ase_frame(view, data, frame_offset, frame_number, this.global_numbers);
|
|
193
|
+
this.global_numbers = numbers;
|
|
194
|
+
return frame;
|
|
250
195
|
}
|
|
251
196
|
catch (error) {
|
|
252
197
|
console.warn(`Failed to load ASE frame ${frame_number}:`, error);
|
|
253
198
|
return null;
|
|
254
199
|
}
|
|
255
200
|
}
|
|
256
|
-
parse_xyz_metadata(comment, frame_number) {
|
|
257
|
-
const { step, properties } = parse_xyz_comment_metadata(comment);
|
|
258
|
-
return { frame_number, step: step ?? frame_number, properties };
|
|
259
|
-
}
|
|
260
201
|
parse_ase_metadata(frame_data, frame_number) {
|
|
261
202
|
const properties = {};
|
|
262
203
|
const step = frame_number;
|
|
263
204
|
if (frame_data.calculator && typeof frame_data.calculator === `object`) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
205
|
+
copy_numeric_fields(properties, frame_data.calculator, [
|
|
206
|
+
`energy`,
|
|
207
|
+
`potential_energy`,
|
|
208
|
+
`kinetic_energy`,
|
|
209
|
+
`total_energy`,
|
|
210
|
+
]);
|
|
271
211
|
}
|
|
272
212
|
if (frame_data.info && typeof frame_data.info === `object`) {
|
|
273
|
-
|
|
274
|
-
const info_properties = [
|
|
213
|
+
copy_numeric_fields(properties, frame_data.info, [
|
|
275
214
|
`force_max`,
|
|
276
215
|
`force_norm`,
|
|
277
216
|
`stress_max`,
|
|
278
217
|
`stress_frobenius`,
|
|
279
218
|
`pressure`,
|
|
280
219
|
`temperature`,
|
|
281
|
-
];
|
|
282
|
-
for (const prop of info_properties) {
|
|
283
|
-
if (prop in info && typeof info[prop] === `number`) {
|
|
284
|
-
properties[prop] = info[prop];
|
|
285
|
-
}
|
|
286
|
-
}
|
|
220
|
+
]);
|
|
287
221
|
}
|
|
288
222
|
if (frame_data.cell && Array.isArray(frame_data.cell)) {
|
|
289
223
|
try {
|
|
@@ -3,8 +3,6 @@ import * as math from '../math';
|
|
|
3
3
|
import type { AnyStructure } from '../structure/index';
|
|
4
4
|
import type { Pbc } from '../structure/pbc';
|
|
5
5
|
import type { TrajectoryFrame } from './index';
|
|
6
|
-
export declare const is_valid_element_symbol: (symbol: string) => symbol is ElementSymbol;
|
|
7
|
-
export declare const coerce_element_symbol: (symbol: string) => ElementSymbol | undefined;
|
|
8
6
|
export declare function validate_3x3_matrix(data: unknown): math.Matrix3x3;
|
|
9
7
|
export declare const convert_atomic_numbers: (numbers: number[]) => ElementSymbol[];
|
|
10
8
|
export declare const create_structure: (positions: number[][], elements: ElementSymbol[], lattice_matrix?: math.Matrix3x3, pbc?: Pbc, force_data?: number[][]) => AnyStructure;
|
|
@@ -12,4 +10,14 @@ export declare const create_trajectory_frame: (positions: number[][], elements:
|
|
|
12
10
|
export declare const read_ndarray_from_view: (view: DataView, ref: {
|
|
13
11
|
ndarray: unknown[];
|
|
14
12
|
}) => number[][];
|
|
13
|
+
export declare const copy_numeric_fields: (target: Record<string, number>, source: Record<string, unknown>, fields: readonly string[]) => void;
|
|
14
|
+
export declare function calc_force_stats(forces: number[][]): {
|
|
15
|
+
force_max: number;
|
|
16
|
+
force_norm: number;
|
|
17
|
+
} | null;
|
|
18
|
+
export declare function iter_xyz_frames(lines: string[]): Generator<{
|
|
19
|
+
start: number;
|
|
20
|
+
num_atoms: number;
|
|
21
|
+
comment: string;
|
|
22
|
+
}>;
|
|
15
23
|
export declare function count_xyz_frames(data: string): number;
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
// Shared utilities for trajectory parsing
|
|
2
2
|
import { ATOMIC_NUMBER_TO_SYMBOL } from '../composition/parse';
|
|
3
|
-
import {
|
|
3
|
+
import { is_elem_symbol } from '../element';
|
|
4
4
|
import * as math from '../math';
|
|
5
|
-
|
|
5
|
+
import { make_site } from '../structure/site';
|
|
6
6
|
const is_valid_row = (row) => {
|
|
7
7
|
if (!(Array.isArray(row) || (ArrayBuffer.isView(row) && `length` in row)))
|
|
8
8
|
return false;
|
|
9
9
|
return math.is_finite_vec3_like(row);
|
|
10
10
|
};
|
|
11
11
|
const is_valid_vec3 = (coords) => Array.isArray(coords) && math.is_finite_vec3_like(coords);
|
|
12
|
-
export const is_valid_element_symbol = (symbol) => element_symbol_set.has(symbol);
|
|
13
|
-
export const coerce_element_symbol = (symbol) => is_valid_element_symbol(symbol) ? symbol : undefined;
|
|
14
12
|
// Validate that data is a proper 3x3 matrix
|
|
15
13
|
// Accepts both regular arrays and typed arrays (Float32Array, Float64Array, etc.)
|
|
16
14
|
export function validate_3x3_matrix(data) {
|
|
@@ -24,7 +22,7 @@ export function validate_3x3_matrix(data) {
|
|
|
24
22
|
}
|
|
25
23
|
export const convert_atomic_numbers = (numbers) => numbers.map((num) => {
|
|
26
24
|
const symbol = ATOMIC_NUMBER_TO_SYMBOL[num];
|
|
27
|
-
if (!symbol || !
|
|
25
|
+
if (!symbol || !is_elem_symbol(symbol)) {
|
|
28
26
|
throw new Error(`Unknown atomic number in trajectory data: ${num}`);
|
|
29
27
|
}
|
|
30
28
|
return symbol;
|
|
@@ -42,13 +40,7 @@ export const create_structure = (positions, elements, lattice_matrix, pbc, force
|
|
|
42
40
|
const abc = cart_to_frac ? cart_to_frac(xyz) : [0, 0, 0];
|
|
43
41
|
const force = force_data?.[idx];
|
|
44
42
|
const properties = is_valid_vec3(force) ? { force } : {};
|
|
45
|
-
return {
|
|
46
|
-
species: [{ element: elements[idx], occu: 1, oxidation_state: 0 }],
|
|
47
|
-
abc,
|
|
48
|
-
xyz,
|
|
49
|
-
label: `${elements[idx]}${idx + 1}`,
|
|
50
|
-
properties,
|
|
51
|
-
};
|
|
43
|
+
return make_site(elements[idx], abc, xyz, `${elements[idx]}${idx + 1}`, properties);
|
|
52
44
|
});
|
|
53
45
|
return lattice_matrix
|
|
54
46
|
? {
|
|
@@ -126,39 +118,67 @@ export const read_ndarray_from_view = (view, ref) => {
|
|
|
126
118
|
throw new Error(`Unsupported shape`);
|
|
127
119
|
})();
|
|
128
120
|
};
|
|
129
|
-
//
|
|
130
|
-
export
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
121
|
+
// Copy listed fields from source to target when they hold numbers
|
|
122
|
+
export const copy_numeric_fields = (target, source, fields) => {
|
|
123
|
+
for (const field of fields) {
|
|
124
|
+
if (field in source && typeof source[field] === `number`)
|
|
125
|
+
target[field] = source[field];
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
// Max and RMS of per-atom force magnitudes, or null when no forces present. Loop-based
|
|
129
|
+
// rather than Math.max(...spread) to avoid call-stack overflow on very large frames.
|
|
130
|
+
export function calc_force_stats(forces) {
|
|
131
|
+
if (forces.length === 0)
|
|
132
|
+
return null;
|
|
133
|
+
let force_max = -Infinity;
|
|
134
|
+
let sum_sq = 0;
|
|
135
|
+
for (const force of forces) {
|
|
136
|
+
const magnitude = Math.hypot(...force);
|
|
137
|
+
if (magnitude > force_max)
|
|
138
|
+
force_max = magnitude;
|
|
139
|
+
sum_sq += magnitude ** 2;
|
|
140
|
+
}
|
|
141
|
+
return { force_max, force_norm: Math.sqrt(sum_sq / forces.length) };
|
|
142
|
+
}
|
|
143
|
+
// Walk concatenated (ext)XYZ frames in `lines`, yielding each frame's atom-count line
|
|
144
|
+
// index, parsed atom count, and comment line. A candidate frame is accepted only when its
|
|
145
|
+
// first few atom lines look like "<element> <x> <y> <z>"; otherwise we advance one line and
|
|
146
|
+
// rescan. That validation doubles as content sniffing so numeric-leading non-XYZ formats
|
|
147
|
+
// (e.g. VASP XDATCAR) aren't misread as frames, and keeps count_xyz_frames consistent with
|
|
148
|
+
// the actual parse/index walk (both go through this single source of truth).
|
|
149
|
+
export function* iter_xyz_frames(lines) {
|
|
135
150
|
let line_idx = 0;
|
|
136
151
|
while (line_idx < lines.length) {
|
|
137
|
-
|
|
138
|
-
line_idx++;
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
const num_atoms = parseInt(lines[line_idx].trim(), 10);
|
|
152
|
+
const num_atoms = parseInt(lines[line_idx]?.trim(), 10);
|
|
142
153
|
if (isNaN(num_atoms) || num_atoms <= 0 || line_idx + num_atoms + 2 > lines.length) {
|
|
143
|
-
line_idx++;
|
|
154
|
+
line_idx++; // skip blank/invalid lines until the next frame's atom-count line
|
|
144
155
|
continue;
|
|
145
156
|
}
|
|
146
|
-
// Quick validation of first few atom lines
|
|
147
157
|
let valid_coords = 0;
|
|
148
|
-
|
|
158
|
+
const sample = Math.min(num_atoms, 3);
|
|
159
|
+
for (let idx = 0; idx < sample; idx++) {
|
|
149
160
|
const parts = lines[line_idx + 2 + idx]?.trim().split(/\s+/);
|
|
150
|
-
if (parts?.length >= 4 &&
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (valid_coords >= Math.min(num_atoms, 3)) {
|
|
156
|
-
frame_count++;
|
|
157
|
-
line_idx += 2 + num_atoms;
|
|
161
|
+
if (parts?.length >= 4 &&
|
|
162
|
+
isNaN(parseInt(parts[0], 10)) &&
|
|
163
|
+
parts[0].length <= 3 &&
|
|
164
|
+
parts.slice(1, 4).every((coord) => !isNaN(parseFloat(coord))))
|
|
165
|
+
valid_coords++;
|
|
158
166
|
}
|
|
159
|
-
|
|
160
|
-
line_idx++;
|
|
167
|
+
if (valid_coords < sample) {
|
|
168
|
+
line_idx++; // count line looks valid but atom lines don't — likely non-XYZ content
|
|
169
|
+
continue;
|
|
161
170
|
}
|
|
171
|
+
yield { start: line_idx, num_atoms, comment: lines[line_idx + 1] || `` };
|
|
172
|
+
line_idx += num_atoms + 2;
|
|
162
173
|
}
|
|
174
|
+
}
|
|
175
|
+
// Count XYZ frames via iter_xyz_frames so total_frames matches what gets indexed/loaded
|
|
176
|
+
export function count_xyz_frames(data) {
|
|
177
|
+
if (!data || typeof data !== `string`)
|
|
178
|
+
return 0;
|
|
179
|
+
const frames = iter_xyz_frames(data.trim().split(/\r?\n/));
|
|
180
|
+
let frame_count = 0;
|
|
181
|
+
while (!frames.next().done)
|
|
182
|
+
frame_count += 1;
|
|
163
183
|
return frame_count;
|
|
164
184
|
}
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
import type { TrajectoryType } from '../index';
|
|
1
|
+
import type { TrajectoryFrame, TrajectoryType } from '../index';
|
|
2
|
+
export declare const read_ase_header: (view: DataView) => {
|
|
3
|
+
n_items: number;
|
|
4
|
+
offsets_pos: number;
|
|
5
|
+
};
|
|
6
|
+
export declare function decode_ase_frame(view: DataView, buffer: ArrayBuffer, frame_offset: number, step: number, fallback_numbers?: number[], max_json_length?: number): {
|
|
7
|
+
frame: TrajectoryFrame;
|
|
8
|
+
numbers: number[];
|
|
9
|
+
};
|
|
2
10
|
export declare function parse_ase_trajectory(buffer: ArrayBuffer, filename?: string): TrajectoryType;
|
|
@@ -1,18 +1,55 @@
|
|
|
1
1
|
// ASE trajectory (.traj) parsing - binary format
|
|
2
|
+
import * as math from '../../math';
|
|
2
3
|
import { MAX_SAFE_STRING_LENGTH } from '../constants';
|
|
3
4
|
import { convert_atomic_numbers, create_trajectory_frame, read_ndarray_from_view, validate_3x3_matrix, } from '../helpers';
|
|
5
|
+
// ULM header: frame count lives at byte 32, frame-offsets table position at byte 40
|
|
6
|
+
export const read_ase_header = (view) => ({
|
|
7
|
+
n_items: Number(view.getBigInt64(32, true)),
|
|
8
|
+
offsets_pos: Number(view.getBigInt64(40, true)),
|
|
9
|
+
});
|
|
10
|
+
// Decode a single ASE/ULM frame (JSON header + optional ndarray payloads) into a
|
|
11
|
+
// TrajectoryFrame. Returns the atomic numbers actually used so callers can cache them
|
|
12
|
+
// as fallback for later frames that omit `numbers` (ASE stores them only once).
|
|
13
|
+
export function decode_ase_frame(view, buffer, frame_offset, step, fallback_numbers, max_json_length) {
|
|
14
|
+
const json_length = Number(view.getBigInt64(frame_offset, true));
|
|
15
|
+
if (max_json_length !== undefined && json_length > max_json_length) {
|
|
16
|
+
throw new Error(`frame JSON too large: ${json_length} bytes`);
|
|
17
|
+
}
|
|
18
|
+
const frame_data = JSON.parse(new TextDecoder().decode(new Uint8Array(buffer, frame_offset + 8, json_length)));
|
|
19
|
+
const positions_ref = frame_data[`positions.`] ?? frame_data.positions;
|
|
20
|
+
const positions = positions_ref?.ndarray
|
|
21
|
+
? read_ndarray_from_view(view, positions_ref)
|
|
22
|
+
: positions_ref;
|
|
23
|
+
const numbers_ref = frame_data[`numbers.`] ?? frame_data.numbers ?? fallback_numbers;
|
|
24
|
+
const numbers = numbers_ref?.ndarray
|
|
25
|
+
? read_ndarray_from_view(view, numbers_ref).flat()
|
|
26
|
+
: numbers_ref;
|
|
27
|
+
if (!numbers || !positions) {
|
|
28
|
+
throw new Error(`missing ${!numbers ? `numbers` : `positions`}`);
|
|
29
|
+
}
|
|
30
|
+
const cell = frame_data.cell ? validate_3x3_matrix(frame_data.cell) : undefined;
|
|
31
|
+
const metadata = {
|
|
32
|
+
step,
|
|
33
|
+
...frame_data.calculator,
|
|
34
|
+
...frame_data.info,
|
|
35
|
+
};
|
|
36
|
+
if (cell) {
|
|
37
|
+
try {
|
|
38
|
+
metadata.volume = Math.abs(math.det_3x3(cell));
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.warn(`Failed to calculate volume for frame ${step}:`, error);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const frame = create_trajectory_frame(positions, convert_atomic_numbers(numbers), cell, frame_data.pbc ?? [true, true, true], step, metadata);
|
|
45
|
+
return { frame, numbers };
|
|
46
|
+
}
|
|
4
47
|
export function parse_ase_trajectory(buffer, filename) {
|
|
5
48
|
const view = new DataView(buffer);
|
|
6
|
-
let offset = 0;
|
|
7
49
|
const signature = new TextDecoder().decode(new Uint8Array(buffer, 0, 8));
|
|
8
50
|
if (signature !== `- of Ulm`)
|
|
9
51
|
throw new Error(`Invalid ASE trajectory`);
|
|
10
|
-
|
|
11
|
-
// Skip ASE/Ulm version field; current parsing logic is version-independent.
|
|
12
|
-
offset += 8;
|
|
13
|
-
const n_items = Number(view.getBigInt64(offset, true));
|
|
14
|
-
offset += 8;
|
|
15
|
-
const offsets_pos = Number(view.getBigInt64(offset, true));
|
|
52
|
+
const { n_items, offsets_pos } = read_ase_header(view);
|
|
16
53
|
if (n_items <= 0)
|
|
17
54
|
throw new Error(`Invalid frame count`);
|
|
18
55
|
if (offsets_pos < 0 || offsets_pos + n_items * 8 > buffer.byteLength) {
|
|
@@ -23,31 +60,9 @@ export function parse_ase_trajectory(buffer, filename) {
|
|
|
23
60
|
let global_numbers;
|
|
24
61
|
for (let idx = 0; idx < n_items; idx++) {
|
|
25
62
|
try {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (json_length > MAX_SAFE_STRING_LENGTH) {
|
|
30
|
-
console.warn(`Skipping frame ${idx + 1}/${n_items}: too large`);
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
const frame_data = JSON.parse(new TextDecoder().decode(new Uint8Array(buffer, offset, json_length)));
|
|
34
|
-
const positions_ref = frame_data[`positions.`] ?? frame_data.positions;
|
|
35
|
-
const positions = positions_ref?.ndarray
|
|
36
|
-
? read_ndarray_from_view(view, positions_ref)
|
|
37
|
-
: positions_ref;
|
|
38
|
-
const numbers_ref = frame_data[`numbers.`] ?? frame_data.numbers ?? global_numbers;
|
|
39
|
-
const numbers = numbers_ref?.ndarray
|
|
40
|
-
? read_ndarray_from_view(view, numbers_ref).flat()
|
|
41
|
-
: numbers_ref;
|
|
42
|
-
if (numbers)
|
|
43
|
-
global_numbers = numbers;
|
|
44
|
-
if (!numbers || !positions) {
|
|
45
|
-
console.warn(`Skipping ASE frame ${idx + 1}/${n_items}: missing ${!numbers ? `numbers` : `positions`}`);
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
const elements = convert_atomic_numbers(numbers);
|
|
49
|
-
const metadata = { step: idx, ...frame_data.calculator, ...frame_data.info };
|
|
50
|
-
frames.push(create_trajectory_frame(positions, elements, frame_data.cell ? validate_3x3_matrix(frame_data.cell) : undefined, frame_data.pbc ?? [true, true, true], idx, metadata));
|
|
63
|
+
const { frame, numbers } = decode_ase_frame(view, buffer, frame_offsets[idx], idx, global_numbers, MAX_SAFE_STRING_LENGTH);
|
|
64
|
+
global_numbers = numbers;
|
|
65
|
+
frames.push(frame);
|
|
51
66
|
}
|
|
52
67
|
catch (error) {
|
|
53
68
|
console.warn(`Error processing frame ${idx + 1}/${n_items}:`, error);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { to_error } from '../../utils';
|
|
2
|
+
// Collector for non-fatal trajectory parse warnings (skipped atoms, dropped frames, plot-metadata extraction failures, ...) so they reach the UI not just the console. Reset at the start of each top-level parse call; format parsers append via traj_warn (mirrored to console.warn). Fatal failures throw — see parse_trajectory_data.
|
|
3
|
+
let traj_parse_warnings = [];
|
|
4
|
+
// Read-only snapshot of warnings since the last top-level parse (attached to trajectory metadata by parse/index.ts for UI surfacing)
|
|
5
|
+
export const get_traj_parse_warnings = () => [...traj_parse_warnings];
|
|
6
|
+
export const reset_traj_parse_warnings = () => void (traj_parse_warnings = []);
|
|
7
|
+
export const traj_warn = (message, error) => {
|
|
8
|
+
const detail = error === undefined ? `` : `: ${to_error(error).message}`;
|
|
9
|
+
traj_parse_warnings.push(`${message}${detail}`);
|
|
10
|
+
if (error === undefined)
|
|
11
|
+
console.warn(message);
|
|
12
|
+
else
|
|
13
|
+
console.warn(`${message}:`, error);
|
|
14
|
+
};
|
|
@@ -2,7 +2,7 @@ import { is_trajectory_file } from '../format-detect';
|
|
|
2
2
|
import { TrajFrameReader } from '../frame-reader';
|
|
3
3
|
import type { FrameLoader, ParseProgress, TrajectoryType } from '../index';
|
|
4
4
|
import type { AtomTypeMapping, LoadingOptions } from '../types';
|
|
5
|
-
export {
|
|
5
|
+
export { LARGE_FILE_THRESHOLD, MAX_BIN_FILE_SIZE, MAX_TEXT_FILE_SIZE, } from '../constants';
|
|
6
6
|
export type { AtomTypeMapping, LoadingOptions } from '../types';
|
|
7
7
|
export { is_trajectory_file, TrajFrameReader };
|
|
8
8
|
export declare function parse_trajectory_data(data: unknown, filename?: string, atom_type_mapping?: AtomTypeMapping): Promise<TrajectoryType>;
|