matterviz 0.3.0 → 0.3.2

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.
Files changed (286) hide show
  1. package/dist/FilePicker.svelte +37 -20
  2. package/dist/Icon.svelte +2 -2
  3. package/dist/MillerIndexInput.svelte +60 -0
  4. package/dist/MillerIndexInput.svelte.d.ts +7 -0
  5. package/dist/app.css +38 -2
  6. package/dist/brillouin/BrillouinZone.svelte +20 -62
  7. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  8. package/dist/brillouin/BrillouinZoneExportPane.svelte +12 -20
  9. package/dist/brillouin/BrillouinZoneScene.svelte +2 -2
  10. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  11. package/dist/chempot-diagram/ChemPotDiagram.svelte +192 -0
  12. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
  13. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +677 -0
  14. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
  15. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2688 -0
  16. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
  17. package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -0
  18. package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
  19. package/dist/chempot-diagram/color.d.ts +10 -0
  20. package/dist/chempot-diagram/color.js +33 -0
  21. package/dist/chempot-diagram/compute.d.ts +38 -0
  22. package/dist/chempot-diagram/compute.js +650 -0
  23. package/dist/chempot-diagram/index.d.ts +5 -0
  24. package/dist/chempot-diagram/index.js +5 -0
  25. package/dist/chempot-diagram/pointer.d.ts +16 -0
  26. package/dist/chempot-diagram/pointer.js +40 -0
  27. package/dist/chempot-diagram/temperature.d.ts +15 -0
  28. package/dist/chempot-diagram/temperature.js +37 -0
  29. package/dist/chempot-diagram/types.d.ts +83 -0
  30. package/dist/chempot-diagram/types.js +27 -0
  31. package/dist/colors/index.d.ts +3 -1
  32. package/dist/colors/index.js +4 -0
  33. package/dist/composition/BarChart.svelte +13 -22
  34. package/dist/composition/BubbleChart.svelte +5 -3
  35. package/dist/composition/FormulaFilter.svelte +770 -90
  36. package/dist/composition/FormulaFilter.svelte.d.ts +37 -1
  37. package/dist/composition/PieChart.svelte +43 -18
  38. package/dist/composition/PieChart.svelte.d.ts +1 -1
  39. package/dist/constants.d.ts +1 -0
  40. package/dist/constants.js +2 -0
  41. package/dist/convex-hull/ConvexHull.svelte +14 -1
  42. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  43. package/dist/convex-hull/ConvexHull2D.svelte +14 -45
  44. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  45. package/dist/convex-hull/ConvexHull3D.svelte +396 -134
  46. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  47. package/dist/convex-hull/ConvexHull4D.svelte +93 -42
  48. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  49. package/dist/convex-hull/ConvexHullControls.svelte +94 -31
  50. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +4 -2
  51. package/dist/convex-hull/ConvexHullStats.svelte +697 -128
  52. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +6 -1
  53. package/dist/convex-hull/ConvexHullTooltip.svelte +1 -0
  54. package/dist/convex-hull/GasPressureControls.svelte +72 -38
  55. package/dist/convex-hull/GasPressureControls.svelte.d.ts +2 -1
  56. package/dist/convex-hull/TemperatureSlider.svelte +46 -19
  57. package/dist/convex-hull/TemperatureSlider.svelte.d.ts +2 -1
  58. package/dist/convex-hull/demo-temperature.d.ts +6 -0
  59. package/dist/convex-hull/demo-temperature.js +36 -0
  60. package/dist/convex-hull/gas-thermodynamics.js +16 -5
  61. package/dist/convex-hull/helpers.d.ts +7 -1
  62. package/dist/convex-hull/helpers.js +45 -15
  63. package/dist/convex-hull/index.d.ts +15 -1
  64. package/dist/convex-hull/index.js +1 -0
  65. package/dist/convex-hull/thermodynamics.d.ts +8 -21
  66. package/dist/convex-hull/thermodynamics.js +106 -17
  67. package/dist/convex-hull/types.d.ts +7 -0
  68. package/dist/convex-hull/types.js +11 -0
  69. package/dist/coordination/CoordinationBarPlot.svelte +29 -46
  70. package/dist/element/BohrAtom.svelte +1 -1
  71. package/dist/element/data.js +2 -14
  72. package/dist/element/data.json.gz +0 -0
  73. package/dist/element/index.d.ts +1 -1
  74. package/dist/element/index.js +1 -0
  75. package/dist/element/types.d.ts +1 -0
  76. package/dist/fermi-surface/FermiSurface.svelte +21 -65
  77. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  78. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  79. package/dist/fermi-surface/FermiSurfaceScene.svelte +1 -1
  80. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  81. package/dist/fermi-surface/compute.js +1 -21
  82. package/dist/fermi-surface/marching-cubes.d.ts +2 -13
  83. package/dist/fermi-surface/marching-cubes.js +2 -519
  84. package/dist/fermi-surface/parse.js +17 -23
  85. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1273 -0
  86. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
  87. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +171 -0
  88. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +31 -0
  89. package/dist/heatmap-matrix/index.d.ts +53 -0
  90. package/dist/heatmap-matrix/index.js +100 -0
  91. package/dist/heatmap-matrix/shared.d.ts +2 -0
  92. package/dist/heatmap-matrix/shared.js +4 -0
  93. package/dist/icons.d.ts +119 -0
  94. package/dist/icons.js +119 -0
  95. package/dist/index.d.ts +6 -1
  96. package/dist/index.js +6 -1
  97. package/dist/io/export.js +15 -3
  98. package/dist/io/file-drop.d.ts +7 -0
  99. package/dist/io/file-drop.js +43 -0
  100. package/dist/io/index.d.ts +2 -2
  101. package/dist/io/index.js +2 -112
  102. package/dist/io/types.d.ts +1 -0
  103. package/dist/io/url-drop.d.ts +2 -0
  104. package/dist/io/url-drop.js +118 -0
  105. package/dist/isosurface/Isosurface.svelte +231 -0
  106. package/dist/isosurface/Isosurface.svelte.d.ts +8 -0
  107. package/dist/isosurface/IsosurfaceControls.svelte +273 -0
  108. package/dist/isosurface/IsosurfaceControls.svelte.d.ts +9 -0
  109. package/dist/isosurface/index.d.ts +5 -0
  110. package/dist/isosurface/index.js +6 -0
  111. package/dist/isosurface/parse.d.ts +6 -0
  112. package/dist/isosurface/parse.js +548 -0
  113. package/dist/isosurface/slice.d.ts +11 -0
  114. package/dist/isosurface/slice.js +145 -0
  115. package/dist/isosurface/types.d.ts +55 -0
  116. package/dist/isosurface/types.js +178 -0
  117. package/dist/labels.d.ts +2 -1
  118. package/dist/labels.js +1 -0
  119. package/dist/layout/InfoTag.svelte +62 -62
  120. package/dist/layout/SubpageGrid.svelte +74 -0
  121. package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
  122. package/dist/layout/index.d.ts +1 -0
  123. package/dist/layout/index.js +1 -0
  124. package/dist/layout/json-tree/JsonNode.svelte +226 -53
  125. package/dist/layout/json-tree/JsonTree.svelte +425 -51
  126. package/dist/layout/json-tree/JsonTree.svelte.d.ts +1 -1
  127. package/dist/layout/json-tree/JsonValue.svelte +218 -97
  128. package/dist/layout/json-tree/types.d.ts +27 -2
  129. package/dist/layout/json-tree/utils.d.ts +14 -1
  130. package/dist/layout/json-tree/utils.js +254 -0
  131. package/dist/marching-cubes.d.ts +14 -0
  132. package/dist/marching-cubes.js +519 -0
  133. package/dist/math.d.ts +8 -0
  134. package/dist/math.js +374 -7
  135. package/dist/overlays/ContextMenu.svelte +3 -2
  136. package/dist/overlays/DraggablePane.svelte +163 -58
  137. package/dist/overlays/DraggablePane.svelte.d.ts +2 -0
  138. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +232 -77
  139. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -2
  140. package/dist/phase-diagram/PhaseDiagramControls.svelte +32 -11
  141. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +3 -2
  142. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +103 -0
  143. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
  144. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +102 -95
  145. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +7 -0
  146. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +100 -26
  147. package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +6 -3
  148. package/dist/phase-diagram/index.d.ts +2 -0
  149. package/dist/phase-diagram/index.js +2 -0
  150. package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
  151. package/dist/phase-diagram/svg-to-diagram.js +865 -0
  152. package/dist/phase-diagram/types.d.ts +10 -0
  153. package/dist/phase-diagram/utils.d.ts +7 -4
  154. package/dist/phase-diagram/utils.js +149 -59
  155. package/dist/plot/AxisLabel.svelte +26 -0
  156. package/dist/plot/AxisLabel.svelte.d.ts +16 -0
  157. package/dist/plot/BarPlot.svelte +473 -228
  158. package/dist/plot/BarPlot.svelte.d.ts +3 -3
  159. package/dist/plot/BarPlotControls.svelte +3 -2
  160. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  161. package/dist/plot/ColorBar.svelte +54 -54
  162. package/dist/plot/ColorBar.svelte.d.ts +1 -1
  163. package/dist/plot/ElementScatter.svelte +4 -3
  164. package/dist/plot/FillArea.svelte +4 -1
  165. package/dist/plot/Histogram.svelte +320 -230
  166. package/dist/plot/Histogram.svelte.d.ts +2 -2
  167. package/dist/plot/HistogramControls.svelte +29 -10
  168. package/dist/plot/HistogramControls.svelte.d.ts +6 -2
  169. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +2 -2
  170. package/dist/plot/PlotControls.svelte +109 -27
  171. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  172. package/dist/plot/PlotLegend.svelte +1 -1
  173. package/dist/plot/PortalSelect.svelte +2 -1
  174. package/dist/plot/ReferenceLine.svelte +2 -1
  175. package/dist/plot/ReferenceLine.svelte.d.ts +1 -0
  176. package/dist/plot/ReferencePlane.svelte +1 -3
  177. package/dist/plot/ScatterPlot.svelte +343 -209
  178. package/dist/plot/ScatterPlot.svelte.d.ts +3 -3
  179. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  180. package/dist/plot/ScatterPlot3DControls.svelte +203 -250
  181. package/dist/plot/ScatterPlot3DScene.svelte +4 -7
  182. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  183. package/dist/plot/ScatterPlotControls.svelte +95 -55
  184. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  185. package/dist/plot/ZeroLines.svelte +44 -0
  186. package/dist/plot/ZeroLines.svelte.d.ts +32 -0
  187. package/dist/plot/ZoomRect.svelte +21 -0
  188. package/dist/plot/ZoomRect.svelte.d.ts +8 -0
  189. package/dist/plot/axis-utils.d.ts +1 -1
  190. package/dist/plot/data-cleaning.js +1 -5
  191. package/dist/plot/index.d.ts +6 -2
  192. package/dist/plot/index.js +6 -2
  193. package/dist/plot/interactions.d.ts +8 -10
  194. package/dist/plot/interactions.js +10 -19
  195. package/dist/plot/layout.d.ts +7 -1
  196. package/dist/plot/layout.js +12 -4
  197. package/dist/plot/reference-line.d.ts +4 -21
  198. package/dist/plot/reference-line.js +7 -81
  199. package/dist/plot/types.d.ts +42 -17
  200. package/dist/plot/types.js +10 -0
  201. package/dist/plot/utils/label-placement.js +14 -11
  202. package/dist/plot/utils.d.ts +1 -0
  203. package/dist/plot/utils.js +14 -0
  204. package/dist/rdf/RdfPlot.svelte +55 -66
  205. package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
  206. package/dist/rdf/index.d.ts +1 -1
  207. package/dist/rdf/index.js +1 -1
  208. package/dist/settings.d.ts +5 -0
  209. package/dist/settings.js +37 -3
  210. package/dist/spectral/Bands.svelte +515 -143
  211. package/dist/spectral/Bands.svelte.d.ts +22 -2
  212. package/dist/spectral/helpers.d.ts +23 -1
  213. package/dist/spectral/helpers.js +65 -9
  214. package/dist/spectral/types.d.ts +2 -0
  215. package/dist/structure/AtomLegend.svelte +31 -10
  216. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  217. package/dist/structure/CellSelect.svelte +92 -22
  218. package/dist/structure/Lattice.svelte +2 -0
  219. package/dist/structure/Structure.svelte +716 -173
  220. package/dist/structure/Structure.svelte.d.ts +7 -2
  221. package/dist/structure/StructureControls.svelte +26 -14
  222. package/dist/structure/StructureControls.svelte.d.ts +5 -1
  223. package/dist/structure/StructureInfoPane.svelte +7 -1
  224. package/dist/structure/StructureScene.svelte +386 -95
  225. package/dist/structure/StructureScene.svelte.d.ts +15 -4
  226. package/dist/structure/atom-properties.d.ts +6 -2
  227. package/dist/structure/atom-properties.js +38 -25
  228. package/dist/structure/export.js +10 -7
  229. package/dist/structure/ferrox-wasm-types.d.ts +3 -2
  230. package/dist/structure/ferrox-wasm-types.js +0 -3
  231. package/dist/structure/ferrox-wasm.d.ts +3 -2
  232. package/dist/structure/ferrox-wasm.js +1 -2
  233. package/dist/structure/index.d.ts +7 -0
  234. package/dist/structure/index.js +22 -0
  235. package/dist/structure/parse.js +19 -16
  236. package/dist/structure/partial-occupancy.d.ts +25 -0
  237. package/dist/structure/partial-occupancy.js +102 -0
  238. package/dist/structure/validation.js +6 -3
  239. package/dist/symmetry/SymmetryStats.svelte +18 -4
  240. package/dist/symmetry/WyckoffTable.svelte +18 -10
  241. package/dist/symmetry/index.d.ts +7 -4
  242. package/dist/symmetry/index.js +83 -18
  243. package/dist/table/HeatmapTable.svelte +468 -69
  244. package/dist/table/HeatmapTable.svelte.d.ts +13 -1
  245. package/dist/table/ToggleMenu.svelte +291 -44
  246. package/dist/table/ToggleMenu.svelte.d.ts +4 -1
  247. package/dist/table/index.d.ts +3 -0
  248. package/dist/tooltip/index.d.ts +1 -1
  249. package/dist/tooltip/index.js +1 -0
  250. package/dist/trajectory/Trajectory.svelte +147 -145
  251. package/dist/trajectory/TrajectoryExportPane.svelte +13 -9
  252. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +1 -1
  253. package/dist/trajectory/constants.d.ts +6 -0
  254. package/dist/trajectory/constants.js +7 -0
  255. package/dist/trajectory/extract.js +3 -5
  256. package/dist/trajectory/format-detect.d.ts +9 -0
  257. package/dist/trajectory/format-detect.js +76 -0
  258. package/dist/trajectory/frame-reader.d.ts +17 -0
  259. package/dist/trajectory/frame-reader.js +339 -0
  260. package/dist/trajectory/helpers.d.ts +15 -0
  261. package/dist/trajectory/helpers.js +187 -0
  262. package/dist/trajectory/index.d.ts +1 -0
  263. package/dist/trajectory/index.js +11 -4
  264. package/dist/trajectory/parse/ase.d.ts +2 -0
  265. package/dist/trajectory/parse/ase.js +76 -0
  266. package/dist/trajectory/parse/hdf5.d.ts +2 -0
  267. package/dist/trajectory/parse/hdf5.js +121 -0
  268. package/dist/trajectory/parse/index.d.ts +12 -0
  269. package/dist/trajectory/parse/index.js +304 -0
  270. package/dist/trajectory/parse/lammps.d.ts +5 -0
  271. package/dist/trajectory/parse/lammps.js +169 -0
  272. package/dist/trajectory/parse/vasp.d.ts +2 -0
  273. package/dist/trajectory/parse/vasp.js +65 -0
  274. package/dist/trajectory/parse/xyz.d.ts +2 -0
  275. package/dist/trajectory/parse/xyz.js +109 -0
  276. package/dist/trajectory/types.d.ts +11 -0
  277. package/dist/trajectory/types.js +1 -0
  278. package/dist/utils.d.ts +2 -0
  279. package/dist/utils.js +4 -0
  280. package/dist/xrd/XrdPlot.svelte +6 -4
  281. package/dist/xrd/calc-xrd.js +0 -1
  282. package/package.json +33 -23
  283. package/readme.md +4 -4
  284. package/dist/trajectory/parse.d.ts +0 -42
  285. package/dist/trajectory/parse.js +0 -1267
  286. /package/dist/element/{data.json.d.ts → data.json.gz.d.ts} +0 -0
@@ -0,0 +1,339 @@
1
+ import * as math from '../math';
2
+ import { MAX_METADATA_SIZE } from './constants';
3
+ import { coerce_element_symbol, convert_atomic_numbers, count_xyz_frames, create_trajectory_frame, read_ndarray_from_view, validate_3x3_matrix, } from './helpers';
4
+ import { strip_compression_extensions } from './format-detect';
5
+ export class TrajFrameReader {
6
+ format;
7
+ global_numbers;
8
+ constructor(filename) {
9
+ const base_filename = strip_compression_extensions(filename);
10
+ this.format = base_filename.endsWith(`.traj`) ? `ase` : `xyz`;
11
+ }
12
+ // deno-lint-ignore require-await
13
+ async get_total_frames(data) {
14
+ if (this.format === `xyz`) {
15
+ if (data instanceof ArrayBuffer)
16
+ throw new Error(`XYZ loader requires text data`);
17
+ return count_xyz_frames(data);
18
+ }
19
+ if (!(data instanceof ArrayBuffer))
20
+ throw new Error(`ASE loader requires binary data`);
21
+ const view = new DataView(data);
22
+ return Number(view.getBigInt64(32, true));
23
+ }
24
+ async build_frame_index(data, sample_rate, on_progress) {
25
+ const total_frames = await this.get_total_frames(data);
26
+ const frame_index = [];
27
+ if (this.format === `xyz`) {
28
+ const data_str = data;
29
+ const lines = data_str.trim().split(/\r?\n/);
30
+ const encoder = new TextEncoder();
31
+ const newline_sequence = data_str.includes(`\r\n`) ? `\r\n` : `\n`;
32
+ const newline_byte_len = encoder.encode(newline_sequence).length;
33
+ let [current_frame, line_idx, byte_offset] = [0, 0, 0];
34
+ while (line_idx < lines.length && current_frame < total_frames) {
35
+ if (!lines[line_idx]?.trim()) {
36
+ byte_offset += encoder.encode(lines[line_idx]).length + newline_byte_len;
37
+ line_idx++;
38
+ continue;
39
+ }
40
+ const num_atoms = parseInt(lines[line_idx].trim(), 10);
41
+ if (isNaN(num_atoms) || num_atoms <= 0 || line_idx + num_atoms + 1 >= lines.length) {
42
+ byte_offset += encoder.encode(lines[line_idx]).length + newline_byte_len;
43
+ line_idx++;
44
+ continue;
45
+ }
46
+ if (current_frame % sample_rate === 0) {
47
+ frame_index.push({
48
+ frame_number: current_frame,
49
+ byte_offset,
50
+ estimated_size: 0,
51
+ });
52
+ }
53
+ const frame_start = line_idx;
54
+ line_idx += 2 + num_atoms;
55
+ let frame_size = 0;
56
+ for (let idx = frame_start; idx < line_idx; idx++) {
57
+ frame_size += encoder.encode(lines[idx]).length + newline_byte_len;
58
+ }
59
+ if (current_frame % sample_rate === 0) {
60
+ frame_index[frame_index.length - 1].estimated_size = frame_size;
61
+ }
62
+ byte_offset += frame_size;
63
+ current_frame++;
64
+ if (on_progress && current_frame % 1000 === 0) {
65
+ on_progress({
66
+ current: (current_frame / total_frames) * 100,
67
+ total: 100,
68
+ stage: `Indexing: ${current_frame}`,
69
+ });
70
+ }
71
+ }
72
+ }
73
+ else {
74
+ const view = new DataView(data);
75
+ const offsets_pos = Number(view.getBigInt64(40, true));
76
+ for (let idx = 0; idx < total_frames; idx += sample_rate) {
77
+ const frame_offset = Number(view.getBigInt64(offsets_pos + idx * 8, true));
78
+ frame_index.push({
79
+ frame_number: idx,
80
+ byte_offset: frame_offset,
81
+ estimated_size: 0,
82
+ });
83
+ if (on_progress && idx % 10000 === 0) {
84
+ on_progress({
85
+ current: (idx / total_frames) * 100,
86
+ total: 100,
87
+ stage: `Indexing ASE: ${idx}`,
88
+ });
89
+ }
90
+ }
91
+ }
92
+ return frame_index;
93
+ }
94
+ // deno-lint-ignore require-await
95
+ async load_frame(data, frame_number) {
96
+ const actual_data_type = data instanceof ArrayBuffer ? `ArrayBuffer` : typeof data;
97
+ if (this.format === `xyz`) {
98
+ if (typeof data !== `string`) {
99
+ throw new TypeError(`load_frame expected string data for xyz format, received ${actual_data_type}`);
100
+ }
101
+ return this.load_xyz_frame(data, frame_number);
102
+ }
103
+ if (!(data instanceof ArrayBuffer)) {
104
+ throw new TypeError(`load_frame expected ArrayBuffer data for ase format, received ${actual_data_type}`);
105
+ }
106
+ return this.load_ase_frame(data, frame_number);
107
+ }
108
+ async extract_plot_metadata(data, options, on_progress) {
109
+ const { sample_rate = 1, properties } = options || {};
110
+ const metadata_list = [];
111
+ const total_frames = await this.get_total_frames(data);
112
+ if (this.format === `xyz`) {
113
+ const lines = data.trim().split(/\r?\n/);
114
+ let [current_frame, line_idx] = [0, 0];
115
+ while (line_idx < lines.length && current_frame < total_frames) {
116
+ if (!lines[line_idx]?.trim()) {
117
+ line_idx++;
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
+ }
125
+ if (current_frame % sample_rate === 0) {
126
+ const comment = lines[line_idx + 1] || ``;
127
+ let frame_metadata = null;
128
+ try {
129
+ frame_metadata = this.parse_xyz_metadata(comment, current_frame);
130
+ }
131
+ catch (error) {
132
+ console.warn(`Failed to parse XYZ metadata for frame ${current_frame} at line ${line_idx + 1}:`, error);
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;
137
+ }
138
+ if (frame_metadata)
139
+ metadata_list.push(frame_metadata);
140
+ }
141
+ line_idx += 2 + num_atoms;
142
+ current_frame++;
143
+ if (on_progress && current_frame % 5000 === 0) {
144
+ on_progress({
145
+ current: (current_frame / total_frames) * 100,
146
+ total: 100,
147
+ stage: `Extracting: ${current_frame}`,
148
+ });
149
+ }
150
+ }
151
+ }
152
+ else if (this.format === `ase`) {
153
+ const view = new DataView(data);
154
+ const n_items = Number(view.getBigInt64(32, true));
155
+ const offsets_pos = Number(view.getBigInt64(40, true));
156
+ for (let idx = 0; idx < n_items; idx += sample_rate) {
157
+ try {
158
+ const frame_offset = Number(view.getBigInt64(offsets_pos + idx * 8, true));
159
+ const json_length = Number(view.getBigInt64(frame_offset, true));
160
+ if (json_length > MAX_METADATA_SIZE) {
161
+ console.warn(`Skipping large frame ${idx}: ${Math.round(json_length / 1024 / 1024)}MB`);
162
+ continue;
163
+ }
164
+ const frame_data = JSON.parse(new TextDecoder().decode(new Uint8Array(data, frame_offset + 8, json_length)));
165
+ const frame_metadata = this.parse_ase_metadata(frame_data, idx);
166
+ if (properties) {
167
+ const filtered = Object.fromEntries(Object.entries(frame_metadata.properties).filter(([key]) => properties.includes(key)));
168
+ frame_metadata.properties = filtered;
169
+ }
170
+ metadata_list.push(frame_metadata);
171
+ if (on_progress && idx % 5000 === 0) {
172
+ on_progress({
173
+ current: (idx / n_items) * 100,
174
+ total: 100,
175
+ stage: `Extracting ASE: ${idx}/${n_items}`,
176
+ });
177
+ }
178
+ }
179
+ catch (error) {
180
+ console.warn(`Failed to extract metadata from ASE frame ${idx}:`, error);
181
+ }
182
+ }
183
+ }
184
+ return metadata_list;
185
+ }
186
+ load_xyz_frame(data, frame_number) {
187
+ const lines = data.trim().split(/\r?\n/);
188
+ let [current_frame, line_idx] = [0, 0];
189
+ while (line_idx < lines.length && current_frame < frame_number) {
190
+ if (!lines[line_idx]?.trim()) {
191
+ line_idx++;
192
+ continue;
193
+ }
194
+ const num_atoms = parseInt(lines[line_idx].trim(), 10);
195
+ if (isNaN(num_atoms) || num_atoms <= 0) {
196
+ line_idx++;
197
+ continue;
198
+ }
199
+ line_idx += 2 + num_atoms;
200
+ current_frame++;
201
+ }
202
+ if (line_idx >= lines.length)
203
+ return null;
204
+ const num_atoms = parseInt(lines[line_idx].trim(), 10);
205
+ if (isNaN(num_atoms) || line_idx + num_atoms + 1 >= lines.length)
206
+ return null;
207
+ const comment = lines[line_idx + 1] || ``;
208
+ const positions = [];
209
+ const elements = [];
210
+ for (let idx = 0; idx < num_atoms; idx++) {
211
+ const parts = lines[line_idx + 2 + idx]?.trim().split(/\s+/);
212
+ if (parts?.length >= 4) {
213
+ const x_coord = parseFloat(parts[1]);
214
+ const y_coord = parseFloat(parts[2]);
215
+ const z_coord = parseFloat(parts[3]);
216
+ if (!Number.isFinite(x_coord) || !Number.isFinite(y_coord) ||
217
+ !Number.isFinite(z_coord)) {
218
+ console.warn(`Skipping XYZ atom with invalid coordinates in indexed frame ${frame_number} at line ${line_idx + 2 + idx}`);
219
+ continue;
220
+ }
221
+ const raw_symbol = parts[0];
222
+ const element_symbol = coerce_element_symbol(raw_symbol);
223
+ if (!element_symbol) {
224
+ console.warn(`Skipping XYZ atom with unknown element symbol "${raw_symbol}" in indexed frame ${frame_number}`);
225
+ continue;
226
+ }
227
+ elements.push(element_symbol);
228
+ positions.push([x_coord, y_coord, z_coord]);
229
+ }
230
+ }
231
+ const metadata = this.parse_xyz_metadata(comment, frame_number);
232
+ return create_trajectory_frame(positions, elements, undefined, undefined, metadata.step, metadata.properties);
233
+ }
234
+ load_ase_frame(data, frame_number) {
235
+ try {
236
+ const view = new DataView(data);
237
+ const n_items = Number(view.getBigInt64(32, true));
238
+ const offsets_pos = Number(view.getBigInt64(40, true));
239
+ if (frame_number >= n_items)
240
+ return null;
241
+ const frame_offset = Number(view.getBigInt64(offsets_pos + frame_number * 8, true));
242
+ const json_length = Number(view.getBigInt64(frame_offset, true));
243
+ const frame_data = JSON.parse(new TextDecoder().decode(new Uint8Array(data, frame_offset + 8, json_length)));
244
+ const positions_ref = frame_data[`positions.`] || frame_data.positions;
245
+ const positions = positions_ref?.ndarray
246
+ ? read_ndarray_from_view(view, positions_ref)
247
+ : positions_ref;
248
+ const numbers_ref = frame_data[`numbers.`] || frame_data.numbers ||
249
+ this.global_numbers;
250
+ const numbers = numbers_ref?.ndarray
251
+ ? read_ndarray_from_view(view, numbers_ref).flat()
252
+ : numbers_ref;
253
+ if (numbers)
254
+ this.global_numbers = numbers;
255
+ if (!numbers || !positions)
256
+ throw new Error(`Missing atomic numbers or positions`);
257
+ const cell = frame_data.cell ? validate_3x3_matrix(frame_data.cell) : undefined;
258
+ const metadata = {
259
+ step: frame_number,
260
+ ...(frame_data.calculator || {}),
261
+ ...(frame_data.info || {}),
262
+ };
263
+ if (cell) {
264
+ try {
265
+ metadata.volume = Math.abs(math.det_3x3(cell));
266
+ }
267
+ catch (error) {
268
+ console.warn(`Failed to calculate volume for frame ${frame_number}:`, error);
269
+ }
270
+ }
271
+ return create_trajectory_frame(positions, convert_atomic_numbers(numbers), cell, frame_data.pbc || [true, true, true], frame_number, metadata);
272
+ }
273
+ catch (error) {
274
+ console.warn(`Failed to load ASE frame ${frame_number}:`, error);
275
+ return null;
276
+ }
277
+ }
278
+ parse_xyz_metadata(comment, frame_number) {
279
+ const properties = {};
280
+ const patterns = {
281
+ energy: /(?:energy|E|etot)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
282
+ volume: /(?:volume|vol|V)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
283
+ pressure: /(?:pressure|press|P)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
284
+ force_max: /(?:max_force|fmax)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
285
+ };
286
+ Object.entries(patterns).forEach(([key, pattern]) => {
287
+ const match = pattern.exec(comment);
288
+ if (match)
289
+ properties[key] = parseFloat(match[1]);
290
+ });
291
+ const step_match = comment.match(/(?:step|frame)\s*[=:]?\s*(\d+)/i);
292
+ const step = step_match ? parseInt(step_match[1]) : frame_number;
293
+ return { frame_number, step, properties };
294
+ }
295
+ parse_ase_metadata(frame_data, frame_number) {
296
+ const properties = {};
297
+ const step = frame_number;
298
+ if (frame_data.calculator && typeof frame_data.calculator === `object`) {
299
+ const calculator = frame_data.calculator;
300
+ const calc_properties = [
301
+ `energy`,
302
+ `potential_energy`,
303
+ `kinetic_energy`,
304
+ `total_energy`,
305
+ ];
306
+ for (const prop of calc_properties) {
307
+ if (prop in calculator && typeof calculator[prop] === `number`) {
308
+ properties[prop] = calculator[prop];
309
+ }
310
+ }
311
+ }
312
+ if (frame_data.info && typeof frame_data.info === `object`) {
313
+ const info = frame_data.info;
314
+ const info_properties = [
315
+ `force_max`,
316
+ `force_norm`,
317
+ `stress_max`,
318
+ `stress_frobenius`,
319
+ `pressure`,
320
+ `temperature`,
321
+ ];
322
+ for (const prop of info_properties) {
323
+ if (prop in info && typeof info[prop] === `number`) {
324
+ properties[prop] = info[prop];
325
+ }
326
+ }
327
+ }
328
+ if (frame_data.cell && Array.isArray(frame_data.cell)) {
329
+ try {
330
+ const validated_cell = validate_3x3_matrix(frame_data.cell);
331
+ properties.volume = Math.abs(math.det_3x3(validated_cell));
332
+ }
333
+ catch (error) {
334
+ console.warn(`Failed to calculate volume for ASE frame ${frame_number}:`, error);
335
+ }
336
+ }
337
+ return { frame_number, step, properties };
338
+ }
339
+ }
@@ -0,0 +1,15 @@
1
+ import type { ElementSymbol } from '../element';
2
+ import * as math from '../math';
3
+ import type { AnyStructure, Pbc } from '../structure';
4
+ import type { TrajectoryFrame } from './index';
5
+ export declare function is_valid_element_symbol(symbol: string): symbol is ElementSymbol;
6
+ export declare function coerce_element_symbol(symbol: string): ElementSymbol | undefined;
7
+ export declare function validate_3x3_matrix(data: unknown): math.Matrix3x3;
8
+ export declare const convert_atomic_numbers: (numbers: number[]) => ElementSymbol[];
9
+ export declare const get_inverse_matrix: (matrix: math.Matrix3x3) => math.Matrix3x3;
10
+ export declare const create_structure: (positions: number[][], elements: ElementSymbol[], lattice_matrix?: math.Matrix3x3, pbc?: Pbc, force_data?: number[][]) => AnyStructure;
11
+ export declare const create_trajectory_frame: (positions: number[][], elements: ElementSymbol[], lattice_matrix: math.Matrix3x3 | undefined, pbc: Pbc | undefined, step: number, metadata?: Record<string, unknown>) => TrajectoryFrame;
12
+ export declare const read_ndarray_from_view: (view: DataView, ref: {
13
+ ndarray: unknown[];
14
+ }) => number[][];
15
+ export declare function count_xyz_frames(data: string): number;
@@ -0,0 +1,187 @@
1
+ // Shared utilities for trajectory parsing
2
+ import { ATOMIC_NUMBER_TO_SYMBOL } from '../composition/parse';
3
+ import { ELEM_SYMBOLS } from '../labels';
4
+ import * as math from '../math';
5
+ const element_symbol_set = new Set(ELEM_SYMBOLS);
6
+ export function is_valid_element_symbol(symbol) {
7
+ return element_symbol_set.has(symbol);
8
+ }
9
+ export function coerce_element_symbol(symbol) {
10
+ return is_valid_element_symbol(symbol) ? symbol : undefined;
11
+ }
12
+ // Validate that data is a proper 3x3 matrix
13
+ // Accepts both regular arrays and typed arrays (Float32Array, Float64Array, etc.)
14
+ export function validate_3x3_matrix(data) {
15
+ if (!Array.isArray(data) || data.length !== 3) {
16
+ throw new Error(`Expected 3x3 matrix, got array of length ${Array.isArray(data) ? data.length : `non-array`}`);
17
+ }
18
+ const is_valid_row = (row) => {
19
+ if (Array.isArray(row))
20
+ return row.length === 3;
21
+ if (!ArrayBuffer.isView(row))
22
+ return false;
23
+ return `length` in row && typeof row.length === `number` && row.length === 3;
24
+ };
25
+ if (!data.every(is_valid_row)) {
26
+ throw new Error(`Invalid 3x3 matrix structure`);
27
+ }
28
+ return data;
29
+ }
30
+ export const convert_atomic_numbers = (numbers) => numbers.map((num) => {
31
+ const symbol = ATOMIC_NUMBER_TO_SYMBOL[num];
32
+ if (!symbol || !is_valid_element_symbol(symbol)) {
33
+ throw new Error(`Unknown atomic number in trajectory data: ${num}`);
34
+ }
35
+ return symbol;
36
+ });
37
+ // Cache inverse matrices by original matrix reference for performance
38
+ // IMPORTANT: This cache assumes lattice matrices are immutable. Mutating a cached
39
+ // matrix in place yields incorrect inverses. Always create new matrix instances
40
+ // if modifications are needed.
41
+ const matrix_cache = new WeakMap();
42
+ export const get_inverse_matrix = (matrix) => {
43
+ const cached = matrix_cache.get(matrix);
44
+ if (cached)
45
+ return cached;
46
+ const inverse = math.matrix_inverse_3x3(matrix);
47
+ matrix_cache.set(matrix, inverse);
48
+ return inverse;
49
+ };
50
+ export const create_structure = (positions, elements, lattice_matrix, pbc, force_data) => {
51
+ if (positions.length !== elements.length) {
52
+ throw new Error(`create_structure requires matching positions and elements lengths, got positions=${positions.length}, elements=${elements.length}`);
53
+ }
54
+ const inv_matrix = lattice_matrix ? get_inverse_matrix(lattice_matrix) : null;
55
+ const is_valid_vec3 = (coords) => Array.isArray(coords) &&
56
+ coords.length === 3 &&
57
+ coords.every((value) => typeof value === `number` && Number.isFinite(value));
58
+ const sites = positions.map((pos, idx) => {
59
+ if (!is_valid_vec3(pos)) {
60
+ throw new Error(`Invalid position at index ${idx}: expected 3 finite coordinates`);
61
+ }
62
+ const xyz = pos;
63
+ const abc = inv_matrix
64
+ ? math.mat3x3_vec3_multiply(inv_matrix, xyz)
65
+ : [0, 0, 0];
66
+ const force = force_data?.[idx];
67
+ const properties = is_valid_vec3(force) ? { force } : {};
68
+ return {
69
+ species: [{ element: elements[idx], occu: 1, oxidation_state: 0 }],
70
+ abc,
71
+ xyz,
72
+ label: `${elements[idx]}${idx + 1}`,
73
+ properties,
74
+ };
75
+ });
76
+ return lattice_matrix
77
+ ? {
78
+ sites,
79
+ lattice: {
80
+ matrix: lattice_matrix,
81
+ ...math.calc_lattice_params(lattice_matrix),
82
+ pbc: pbc || [true, true, true],
83
+ },
84
+ }
85
+ : { sites };
86
+ };
87
+ export const create_trajectory_frame = (positions, elements, lattice_matrix, pbc, step, metadata = {}) => ({
88
+ structure: create_structure(positions, elements, lattice_matrix, pbc),
89
+ step,
90
+ metadata,
91
+ });
92
+ // Shared utility to read ndarray data from binary format
93
+ export const read_ndarray_from_view = (view, ref) => {
94
+ const [shape, dtype, array_offset] = ref.ndarray;
95
+ const total = shape.reduce((product, dim_size) => product * dim_size, 1);
96
+ const data = [];
97
+ let pos = array_offset;
98
+ const readers = {
99
+ int64: {
100
+ bytes_per_element: 8,
101
+ read: () => {
102
+ const value = Number(view.getBigInt64(pos, true));
103
+ pos += 8;
104
+ return value;
105
+ },
106
+ },
107
+ int32: {
108
+ bytes_per_element: 4,
109
+ read: () => {
110
+ const value = view.getInt32(pos, true);
111
+ pos += 4;
112
+ return value;
113
+ },
114
+ },
115
+ float64: {
116
+ bytes_per_element: 8,
117
+ read: () => {
118
+ const value = view.getFloat64(pos, true);
119
+ pos += 8;
120
+ return value;
121
+ },
122
+ },
123
+ float32: {
124
+ bytes_per_element: 4,
125
+ read: () => {
126
+ const value = view.getFloat32(pos, true);
127
+ pos += 4;
128
+ return value;
129
+ },
130
+ },
131
+ };
132
+ const reader_config = readers[dtype];
133
+ if (!reader_config)
134
+ throw new Error(`Unsupported dtype: ${dtype}`);
135
+ if (!Number.isInteger(array_offset) || array_offset < 0) {
136
+ throw new Error(`Invalid array_offset: expected non-negative integer, got ${array_offset}`);
137
+ }
138
+ const bytes_needed = total * reader_config.bytes_per_element;
139
+ if (array_offset + bytes_needed > view.byteLength) {
140
+ throw new Error(`Out-of-bounds read: array_offset + bytesNeeded exceeds view.byteLength`);
141
+ }
142
+ for (let idx = 0; idx < total; idx++)
143
+ data.push(reader_config.read());
144
+ return shape.length === 1
145
+ ? [data]
146
+ : shape.length === 2
147
+ ? Array.from({ length: shape[0] }, (_, idx) => data.slice(idx * shape[1], (idx + 1) * shape[1]))
148
+ : (() => {
149
+ throw new Error(`Unsupported shape`);
150
+ })();
151
+ };
152
+ // Unified frame counting for XYZ
153
+ export function count_xyz_frames(data) {
154
+ if (!data || typeof data !== `string`)
155
+ return 0;
156
+ const lines = data.trim().split(/\r?\n/);
157
+ let frame_count = 0;
158
+ let line_idx = 0;
159
+ while (line_idx < lines.length) {
160
+ if (!lines[line_idx]?.trim()) {
161
+ line_idx++;
162
+ continue;
163
+ }
164
+ const num_atoms = parseInt(lines[line_idx].trim(), 10);
165
+ if (isNaN(num_atoms) || num_atoms <= 0 || line_idx + num_atoms + 2 > lines.length) {
166
+ line_idx++;
167
+ continue;
168
+ }
169
+ // Quick validation of first few atom lines
170
+ let valid_coords = 0;
171
+ for (let idx = 0; idx < Math.min(num_atoms, 3); idx++) {
172
+ const parts = lines[line_idx + 2 + idx]?.trim().split(/\s+/);
173
+ if (parts?.length >= 4 && isNaN(parseInt(parts[0])) && parts[0].length <= 3) {
174
+ if (parts.slice(1, 4).every((coord) => !isNaN(parseFloat(coord))))
175
+ valid_coords++;
176
+ }
177
+ }
178
+ if (valid_coords >= Math.min(num_atoms, 3)) {
179
+ frame_count++;
180
+ line_idx += 2 + num_atoms;
181
+ }
182
+ else {
183
+ line_idx++;
184
+ }
185
+ }
186
+ return frame_count;
187
+ }
@@ -6,6 +6,7 @@ export { default as TrajectoryError } from './TrajectoryError.svelte';
6
6
  export { default as TrajectoryExportPane } from './TrajectoryExportPane.svelte';
7
7
  export { default as TrajectoryInfoPane } from './TrajectoryInfoPane.svelte';
8
8
  export type TrajectoryFormat = `hdf5` | `json` | `xyz` | `xdatcar` | `traj` | `unknown`;
9
+ export type { AtomTypeMapping } from './types';
9
10
  export interface ParseProgress {
10
11
  current: number;
11
12
  total: number;
@@ -20,8 +20,11 @@ export function validate_trajectory(trajectory) {
20
20
  if (typeof total_frames !== `number` || total_frames < 1) {
21
21
  errors.push(`total_frames must be a positive number, got ${total_frames}`);
22
22
  }
23
- else if (indexed_frames && total_frames !== indexed_frames.length) {
24
- errors.push(`total_frames (${total_frames}) inconsistent with indexed_frames length (${indexed_frames.length})`);
23
+ else if (indexed_frames?.length) {
24
+ const last_indexed_frame = indexed_frames.at(-1);
25
+ if (last_indexed_frame && last_indexed_frame.frame_number >= total_frames) {
26
+ errors.push(`indexed_frames contains frame_number >= total_frames (${total_frames})`);
27
+ }
25
28
  }
26
29
  }
27
30
  if (is_indexed === true && !indexed_frames?.length) {
@@ -36,8 +39,12 @@ export function validate_trajectory(trajectory) {
36
39
  if (typeof frame_idx.frame_number !== `number`) {
37
40
  errors.push(`indexed_frames[${idx}] missing or invalid frame_number`);
38
41
  }
39
- else if (frame_idx.frame_number !== idx) {
40
- errors.push(`indexed_frames[${idx}] frame_number (${frame_idx.frame_number}) should equal index (${idx})`);
42
+ else if (frame_idx.frame_number < 0) {
43
+ errors.push(`indexed_frames[${idx}] frame_number (${frame_idx.frame_number}) must be non-negative`);
44
+ }
45
+ else if (idx > 0 &&
46
+ frame_idx.frame_number <= indexed_frames[idx - 1].frame_number) {
47
+ errors.push(`indexed_frames[${idx}] frame_number (${frame_idx.frame_number}) must be strictly increasing`);
41
48
  }
42
49
  if (typeof frame_idx.byte_offset !== `number`) {
43
50
  errors.push(`indexed_frames[${idx}] missing or invalid byte_offset`);
@@ -0,0 +1,2 @@
1
+ import type { TrajectoryType } from '../index';
2
+ export declare function parse_ase_trajectory(buffer: ArrayBuffer, filename?: string): TrajectoryType;
@@ -0,0 +1,76 @@
1
+ // ASE trajectory (.traj) parsing - binary format
2
+ import { MAX_SAFE_STRING_LENGTH } from '../constants';
3
+ import { convert_atomic_numbers, create_trajectory_frame, read_ndarray_from_view, validate_3x3_matrix, } from '../helpers';
4
+ export function parse_ase_trajectory(buffer, filename) {
5
+ const view = new DataView(buffer);
6
+ let offset = 0;
7
+ const signature = new TextDecoder().decode(new Uint8Array(buffer, 0, 8));
8
+ if (signature !== `- of Ulm`)
9
+ throw new Error(`Invalid ASE trajectory`);
10
+ offset += 24;
11
+ const _version = Number(view.getBigInt64(offset, true));
12
+ offset += 8;
13
+ const n_items = Number(view.getBigInt64(offset, true));
14
+ offset += 8;
15
+ const offsets_pos = Number(view.getBigInt64(offset, true));
16
+ if (n_items <= 0)
17
+ throw new Error(`Invalid frame count`);
18
+ if (offsets_pos < 0 ||
19
+ offsets_pos + n_items * 8 > buffer.byteLength) {
20
+ throw new Error(`Invalid ASE frame offsets table bounds: offsets_pos=${offsets_pos}, n_items=${n_items}, byte_length=${buffer.byteLength}`);
21
+ }
22
+ const frame_offsets = Array.from({ length: n_items }, (_, idx) => Number(view.getBigInt64(offsets_pos + idx * 8, true)));
23
+ const frames = [];
24
+ let global_numbers;
25
+ for (let idx = 0; idx < n_items; idx++) {
26
+ try {
27
+ offset = frame_offsets[idx];
28
+ const json_length = Number(view.getBigInt64(offset, true));
29
+ offset += 8;
30
+ if (json_length > MAX_SAFE_STRING_LENGTH) {
31
+ console.warn(`Skipping frame ${idx + 1}/${n_items}: too large`);
32
+ continue;
33
+ }
34
+ const frame_data = JSON.parse(new TextDecoder().decode(new Uint8Array(buffer, offset, json_length)));
35
+ const positions_ref = frame_data[`positions.`] || frame_data.positions;
36
+ const positions = positions_ref?.ndarray
37
+ ? read_ndarray_from_view(view, positions_ref)
38
+ : positions_ref;
39
+ const numbers_ref = frame_data[`numbers.`] || frame_data.numbers || global_numbers;
40
+ const numbers = numbers_ref?.ndarray
41
+ ? read_ndarray_from_view(view, numbers_ref).flat()
42
+ : numbers_ref;
43
+ if (numbers)
44
+ global_numbers = numbers;
45
+ if (!numbers || !positions) {
46
+ console.warn(`Skipping ASE frame ${idx + 1}/${n_items}: missing ${!numbers ? `numbers` : `positions`}`);
47
+ continue;
48
+ }
49
+ const elements = convert_atomic_numbers(numbers);
50
+ const metadata = {
51
+ step: idx,
52
+ ...(frame_data.calculator || {}),
53
+ ...(frame_data.info || {}),
54
+ };
55
+ 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));
56
+ }
57
+ catch (error) {
58
+ console.warn(`Error processing frame ${idx + 1}/${n_items}:`, error);
59
+ }
60
+ }
61
+ if (frames.length === 0)
62
+ throw new Error(`No valid frames found`);
63
+ const first_struct = frames[0]?.structure;
64
+ const periodic_boundary_conditions = first_struct !== null && first_struct !== undefined &&
65
+ typeof first_struct === `object` && `lattice` in first_struct
66
+ ? first_struct.lattice.pbc
67
+ : [true, true, true];
68
+ const metadata = {
69
+ filename,
70
+ source_format: `ase_trajectory`,
71
+ frame_count: frames.length,
72
+ total_atoms: global_numbers?.length || 0,
73
+ periodic_boundary_conditions,
74
+ };
75
+ return { frames, metadata };
76
+ }
@@ -0,0 +1,2 @@
1
+ import type { TrajectoryType } from '../index';
2
+ export declare function parse_torch_sim_hdf5(buffer: ArrayBuffer, filename?: string): Promise<TrajectoryType>;