matterviz 0.3.1 → 0.3.3

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 (358) hide show
  1. package/dist/EmptyState.svelte +10 -2
  2. package/dist/FilePicker.svelte +154 -96
  3. package/dist/Icon.svelte +20 -14
  4. package/dist/MillerIndexInput.svelte +27 -21
  5. package/dist/api/optimade.js +6 -6
  6. package/dist/app.css +216 -178
  7. package/dist/brillouin/BrillouinZone.svelte +299 -198
  8. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  9. package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
  10. package/dist/brillouin/BrillouinZoneExportPane.svelte +74 -55
  11. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
  12. package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
  13. package/dist/brillouin/BrillouinZoneScene.svelte +277 -165
  14. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  15. package/dist/brillouin/BrillouinZoneTooltip.svelte +17 -7
  16. package/dist/brillouin/compute.js +11 -6
  17. package/dist/chempot-diagram/ChemPotDiagram.svelte +327 -0
  18. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
  19. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +847 -0
  20. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
  21. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +3194 -0
  22. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
  23. package/dist/chempot-diagram/ChemPotScene3D.svelte +11 -0
  24. package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
  25. package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
  26. package/dist/chempot-diagram/async-compute.svelte.js +77 -0
  27. package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
  28. package/dist/chempot-diagram/chempot-worker.js +11 -0
  29. package/dist/chempot-diagram/color.d.ts +10 -0
  30. package/dist/chempot-diagram/color.js +32 -0
  31. package/dist/chempot-diagram/compute.d.ts +48 -0
  32. package/dist/chempot-diagram/compute.js +812 -0
  33. package/dist/chempot-diagram/index.d.ts +6 -0
  34. package/dist/chempot-diagram/index.js +6 -0
  35. package/dist/chempot-diagram/pointer.d.ts +16 -0
  36. package/dist/chempot-diagram/pointer.js +40 -0
  37. package/dist/chempot-diagram/temperature.d.ts +15 -0
  38. package/dist/chempot-diagram/temperature.js +36 -0
  39. package/dist/chempot-diagram/types.d.ts +86 -0
  40. package/dist/chempot-diagram/types.js +28 -0
  41. package/dist/colors/index.d.ts +3 -1
  42. package/dist/colors/index.js +9 -3
  43. package/dist/composition/BarChart.svelte +141 -77
  44. package/dist/composition/BubbleChart.svelte +107 -52
  45. package/dist/composition/Composition.svelte +100 -79
  46. package/dist/composition/Formula.svelte +108 -62
  47. package/dist/composition/FormulaFilter.svelte +973 -353
  48. package/dist/composition/FormulaFilter.svelte.d.ts +35 -1
  49. package/dist/composition/PieChart.svelte +199 -99
  50. package/dist/composition/PieChart.svelte.d.ts +1 -1
  51. package/dist/composition/format.d.ts +5 -0
  52. package/dist/composition/format.js +20 -3
  53. package/dist/composition/parse.js +14 -9
  54. package/dist/convex-hull/ConvexHull.svelte +93 -38
  55. package/dist/convex-hull/ConvexHull2D.svelte +551 -393
  56. package/dist/convex-hull/ConvexHull3D.svelte +1303 -825
  57. package/dist/convex-hull/ConvexHull4D.svelte +1012 -686
  58. package/dist/convex-hull/ConvexHullControls.svelte +115 -28
  59. package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
  60. package/dist/convex-hull/ConvexHullStats.svelte +821 -249
  61. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +6 -1
  62. package/dist/convex-hull/ConvexHullTooltip.svelte +41 -16
  63. package/dist/convex-hull/GasPressureControls.svelte +104 -61
  64. package/dist/convex-hull/StructurePopup.svelte +25 -4
  65. package/dist/convex-hull/TemperatureSlider.svelte +45 -25
  66. package/dist/convex-hull/barycentric-coords.js +13 -7
  67. package/dist/convex-hull/demo-temperature.d.ts +6 -0
  68. package/dist/convex-hull/demo-temperature.js +40 -0
  69. package/dist/convex-hull/gas-thermodynamics.js +17 -12
  70. package/dist/convex-hull/helpers.d.ts +10 -1
  71. package/dist/convex-hull/helpers.js +79 -38
  72. package/dist/convex-hull/index.d.ts +1 -0
  73. package/dist/convex-hull/index.js +1 -0
  74. package/dist/convex-hull/thermodynamics.d.ts +8 -21
  75. package/dist/convex-hull/thermodynamics.js +163 -69
  76. package/dist/convex-hull/types.d.ts +12 -12
  77. package/dist/convex-hull/types.js +0 -12
  78. package/dist/coordination/CoordinationBarPlot.svelte +232 -176
  79. package/dist/element/BohrAtom.svelte +56 -13
  80. package/dist/element/ElementHeading.svelte +7 -2
  81. package/dist/element/ElementPhoto.svelte +15 -9
  82. package/dist/element/ElementStats.svelte +10 -4
  83. package/dist/element/ElementTile.svelte +137 -73
  84. package/dist/element/Nucleus.svelte +39 -11
  85. package/dist/element/data.js +2 -14
  86. package/dist/element/data.json.gz +0 -0
  87. package/dist/element/types.d.ts +1 -0
  88. package/dist/feedback/ClickFeedback.svelte +16 -5
  89. package/dist/feedback/DragOverlay.svelte +10 -2
  90. package/dist/feedback/Spinner.svelte +4 -2
  91. package/dist/feedback/StatusMessage.svelte +8 -2
  92. package/dist/fermi-surface/FermiSlice.svelte +118 -88
  93. package/dist/fermi-surface/FermiSurface.svelte +336 -239
  94. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  95. package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
  96. package/dist/fermi-surface/FermiSurfaceScene.svelte +536 -343
  97. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  98. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
  99. package/dist/fermi-surface/compute.js +16 -20
  100. package/dist/fermi-surface/parse.js +37 -33
  101. package/dist/fermi-surface/symmetry.js +2 -7
  102. package/dist/fermi-surface/types.d.ts +3 -5
  103. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1527 -0
  104. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
  105. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +225 -0
  106. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +30 -0
  107. package/dist/heatmap-matrix/index.d.ts +53 -0
  108. package/dist/heatmap-matrix/index.js +100 -0
  109. package/dist/heatmap-matrix/shared.d.ts +2 -0
  110. package/dist/heatmap-matrix/shared.js +4 -0
  111. package/dist/icons.d.ts +111 -0
  112. package/dist/icons.js +158 -0
  113. package/dist/index.d.ts +5 -2
  114. package/dist/index.js +5 -2
  115. package/dist/io/decompress.js +1 -1
  116. package/dist/io/export.d.ts +3 -0
  117. package/dist/io/export.js +138 -140
  118. package/dist/io/file-drop.d.ts +7 -0
  119. package/dist/io/file-drop.js +43 -0
  120. package/dist/io/index.d.ts +2 -2
  121. package/dist/io/index.js +2 -112
  122. package/dist/io/is-binary.js +2 -3
  123. package/dist/io/types.d.ts +1 -0
  124. package/dist/io/url-drop.d.ts +2 -0
  125. package/dist/io/url-drop.js +117 -0
  126. package/dist/isosurface/Isosurface.svelte +220 -110
  127. package/dist/isosurface/IsosurfaceControls.svelte +65 -28
  128. package/dist/isosurface/parse.js +104 -56
  129. package/dist/isosurface/slice.d.ts +2 -1
  130. package/dist/isosurface/slice.js +8 -13
  131. package/dist/isosurface/types.d.ts +14 -1
  132. package/dist/isosurface/types.js +152 -5
  133. package/dist/labels.d.ts +2 -1
  134. package/dist/labels.js +12 -8
  135. package/dist/layout/FullscreenToggle.svelte +11 -2
  136. package/dist/layout/InfoCard.svelte +38 -6
  137. package/dist/layout/InfoTag.svelte +125 -94
  138. package/dist/layout/PropertyFilter.svelte +82 -37
  139. package/dist/layout/SettingsSection.svelte +85 -55
  140. package/dist/layout/SubpageGrid.svelte +82 -0
  141. package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
  142. package/dist/layout/index.d.ts +1 -0
  143. package/dist/layout/index.js +1 -0
  144. package/dist/layout/json-tree/JsonNode.svelte +266 -223
  145. package/dist/layout/json-tree/JsonTree.svelte +516 -429
  146. package/dist/layout/json-tree/JsonTree.svelte.d.ts +1 -1
  147. package/dist/layout/json-tree/JsonValue.svelte +281 -173
  148. package/dist/layout/json-tree/types.d.ts +10 -2
  149. package/dist/layout/json-tree/utils.d.ts +2 -0
  150. package/dist/layout/json-tree/utils.js +37 -2
  151. package/dist/marching-cubes.js +25 -2
  152. package/dist/math.d.ts +20 -17
  153. package/dist/math.js +474 -57
  154. package/dist/overlays/ContextMenu.svelte +66 -40
  155. package/dist/overlays/DraggablePane.svelte +331 -154
  156. package/dist/overlays/DraggablePane.svelte.d.ts +2 -0
  157. package/dist/periodic-table/PeriodicTable.svelte +278 -145
  158. package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
  159. package/dist/periodic-table/PropertySelect.svelte +25 -7
  160. package/dist/periodic-table/TableInset.svelte +8 -3
  161. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +559 -267
  162. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -2
  163. package/dist/phase-diagram/PhaseDiagramControls.svelte +131 -51
  164. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +3 -2
  165. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +126 -0
  166. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
  167. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +160 -110
  168. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +8 -1
  169. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +217 -86
  170. package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +6 -3
  171. package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
  172. package/dist/phase-diagram/build-diagram.js +9 -9
  173. package/dist/phase-diagram/colors.js +1 -3
  174. package/dist/phase-diagram/index.d.ts +2 -0
  175. package/dist/phase-diagram/index.js +2 -0
  176. package/dist/phase-diagram/parse.js +10 -9
  177. package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
  178. package/dist/phase-diagram/svg-to-diagram.js +869 -0
  179. package/dist/phase-diagram/types.d.ts +10 -0
  180. package/dist/phase-diagram/utils.d.ts +8 -4
  181. package/dist/phase-diagram/utils.js +219 -74
  182. package/dist/plot/AxisLabel.svelte +51 -0
  183. package/dist/plot/AxisLabel.svelte.d.ts +16 -0
  184. package/dist/plot/BarPlot.svelte +1461 -768
  185. package/dist/plot/BarPlot.svelte.d.ts +3 -3
  186. package/dist/plot/BarPlotControls.svelte +33 -6
  187. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  188. package/dist/plot/ColorBar.svelte +533 -383
  189. package/dist/plot/ColorBar.svelte.d.ts +1 -1
  190. package/dist/plot/ColorScaleSelect.svelte +28 -7
  191. package/dist/plot/ElementScatter.svelte +38 -16
  192. package/dist/plot/FillArea.svelte +152 -92
  193. package/dist/plot/Histogram.svelte +1162 -709
  194. package/dist/plot/Histogram.svelte.d.ts +1 -1
  195. package/dist/plot/HistogramControls.svelte +81 -18
  196. package/dist/plot/HistogramControls.svelte.d.ts +6 -2
  197. package/dist/plot/InteractiveAxisLabel.svelte +34 -11
  198. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
  199. package/dist/plot/Line.svelte +63 -28
  200. package/dist/plot/PlotControls.svelte +221 -96
  201. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  202. package/dist/plot/PlotLegend.svelte +174 -91
  203. package/dist/plot/PlotTooltip.svelte +45 -6
  204. package/dist/plot/PortalSelect.svelte +175 -146
  205. package/dist/plot/ReferenceLine.svelte +77 -22
  206. package/dist/plot/ReferenceLine.svelte.d.ts +1 -0
  207. package/dist/plot/ReferenceLine3D.svelte +132 -107
  208. package/dist/plot/ReferencePlane.svelte +146 -123
  209. package/dist/plot/ScatterPlot.svelte +1880 -1156
  210. package/dist/plot/ScatterPlot.svelte.d.ts +3 -3
  211. package/dist/plot/ScatterPlot3D.svelte +256 -131
  212. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  213. package/dist/plot/ScatterPlot3DControls.svelte +300 -297
  214. package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
  215. package/dist/plot/ScatterPlot3DScene.svelte +608 -406
  216. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  217. package/dist/plot/ScatterPlotControls.svelte +150 -70
  218. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  219. package/dist/plot/ScatterPoint.svelte +98 -26
  220. package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
  221. package/dist/plot/SpacegroupBarPlot.svelte +142 -85
  222. package/dist/plot/Surface3D.svelte +159 -108
  223. package/dist/plot/ZeroLines.svelte +96 -0
  224. package/dist/plot/ZeroLines.svelte.d.ts +32 -0
  225. package/dist/plot/ZoomRect.svelte +23 -0
  226. package/dist/plot/ZoomRect.svelte.d.ts +8 -0
  227. package/dist/plot/axis-utils.d.ts +1 -1
  228. package/dist/plot/axis-utils.js +1 -3
  229. package/dist/plot/data-cleaning.js +12 -28
  230. package/dist/plot/data-transform.js +2 -1
  231. package/dist/plot/fill-utils.js +2 -0
  232. package/dist/plot/index.d.ts +6 -2
  233. package/dist/plot/index.js +6 -2
  234. package/dist/plot/interactions.d.ts +8 -10
  235. package/dist/plot/interactions.js +2 -3
  236. package/dist/plot/layout.d.ts +11 -2
  237. package/dist/plot/layout.js +44 -17
  238. package/dist/plot/reference-line.d.ts +5 -22
  239. package/dist/plot/reference-line.js +12 -84
  240. package/dist/plot/scales.js +24 -36
  241. package/dist/plot/types.d.ts +53 -40
  242. package/dist/plot/types.js +12 -7
  243. package/dist/plot/utils/label-placement.d.ts +32 -15
  244. package/dist/plot/utils/label-placement.js +227 -63
  245. package/dist/plot/utils/series-visibility.js +2 -3
  246. package/dist/plot/utils.d.ts +1 -0
  247. package/dist/plot/utils.js +14 -0
  248. package/dist/rdf/RdfPlot.svelte +173 -132
  249. package/dist/rdf/calc-rdf.js +4 -5
  250. package/dist/sanitize.d.ts +4 -0
  251. package/dist/sanitize.js +107 -0
  252. package/dist/settings.d.ts +21 -6
  253. package/dist/settings.js +63 -19
  254. package/dist/spectral/Bands.svelte +963 -412
  255. package/dist/spectral/Bands.svelte.d.ts +22 -2
  256. package/dist/spectral/BandsAndDos.svelte +90 -49
  257. package/dist/spectral/BrillouinBandsDos.svelte +151 -93
  258. package/dist/spectral/Dos.svelte +389 -258
  259. package/dist/spectral/helpers.d.ts +23 -1
  260. package/dist/spectral/helpers.js +119 -51
  261. package/dist/spectral/types.d.ts +2 -0
  262. package/dist/state.svelte.d.ts +1 -1
  263. package/dist/state.svelte.js +3 -2
  264. package/dist/structure/Arrow.svelte +59 -20
  265. package/dist/structure/AtomLegend.svelte +231 -129
  266. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  267. package/dist/structure/Bond.svelte +73 -47
  268. package/dist/structure/CanvasTooltip.svelte +10 -2
  269. package/dist/structure/CellSelect.svelte +148 -51
  270. package/dist/structure/Cylinder.svelte +33 -17
  271. package/dist/structure/Lattice.svelte +88 -33
  272. package/dist/structure/Structure.svelte +1077 -821
  273. package/dist/structure/Structure.svelte.d.ts +1 -1
  274. package/dist/structure/StructureControls.svelte +373 -139
  275. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  276. package/dist/structure/StructureExportPane.svelte +124 -89
  277. package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
  278. package/dist/structure/StructureInfoPane.svelte +304 -231
  279. package/dist/structure/StructureScene.svelte +919 -445
  280. package/dist/structure/StructureScene.svelte.d.ts +16 -7
  281. package/dist/structure/atom-properties.d.ts +6 -2
  282. package/dist/structure/atom-properties.js +42 -29
  283. package/dist/structure/bonding.js +6 -7
  284. package/dist/structure/export.js +22 -34
  285. package/dist/structure/ferrox-wasm-types.d.ts +3 -2
  286. package/dist/structure/ferrox-wasm-types.js +0 -3
  287. package/dist/structure/ferrox-wasm.d.ts +3 -2
  288. package/dist/structure/ferrox-wasm.js +2 -3
  289. package/dist/structure/index.d.ts +16 -0
  290. package/dist/structure/index.js +88 -6
  291. package/dist/structure/measure.d.ts +2 -2
  292. package/dist/structure/measure.js +4 -44
  293. package/dist/structure/parse.js +130 -155
  294. package/dist/structure/partial-occupancy.d.ts +25 -0
  295. package/dist/structure/partial-occupancy.js +99 -0
  296. package/dist/structure/pbc.d.ts +1 -0
  297. package/dist/structure/pbc.js +16 -6
  298. package/dist/structure/supercell.d.ts +2 -2
  299. package/dist/structure/supercell.js +12 -22
  300. package/dist/structure/validation.js +5 -3
  301. package/dist/symmetry/SymmetryStats.svelte +94 -37
  302. package/dist/symmetry/WyckoffTable.svelte +42 -14
  303. package/dist/symmetry/cell-transform.js +5 -3
  304. package/dist/symmetry/index.d.ts +7 -4
  305. package/dist/symmetry/index.js +87 -21
  306. package/dist/symmetry/spacegroups.js +148 -148
  307. package/dist/table/HeatmapTable.svelte +1112 -516
  308. package/dist/table/HeatmapTable.svelte.d.ts +12 -1
  309. package/dist/table/ToggleMenu.svelte +125 -90
  310. package/dist/table/index.d.ts +2 -0
  311. package/dist/table/index.js +2 -4
  312. package/dist/theme/ThemeControl.svelte +21 -12
  313. package/dist/time.js +4 -1
  314. package/dist/tooltip/TooltipContent.svelte +33 -8
  315. package/dist/trajectory/Trajectory.svelte +889 -687
  316. package/dist/trajectory/TrajectoryError.svelte +14 -3
  317. package/dist/trajectory/TrajectoryExportPane.svelte +148 -90
  318. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +1 -1
  319. package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
  320. package/dist/trajectory/constants.d.ts +6 -0
  321. package/dist/trajectory/constants.js +7 -0
  322. package/dist/trajectory/extract.js +13 -31
  323. package/dist/trajectory/format-detect.d.ts +9 -0
  324. package/dist/trajectory/format-detect.js +76 -0
  325. package/dist/trajectory/frame-reader.d.ts +17 -0
  326. package/dist/trajectory/frame-reader.js +332 -0
  327. package/dist/trajectory/helpers.d.ts +14 -0
  328. package/dist/trajectory/helpers.js +172 -0
  329. package/dist/trajectory/index.d.ts +1 -0
  330. package/dist/trajectory/index.js +23 -14
  331. package/dist/trajectory/parse/ase.d.ts +2 -0
  332. package/dist/trajectory/parse/ase.js +77 -0
  333. package/dist/trajectory/parse/hdf5.d.ts +2 -0
  334. package/dist/trajectory/parse/hdf5.js +129 -0
  335. package/dist/trajectory/parse/index.d.ts +12 -0
  336. package/dist/trajectory/parse/index.js +299 -0
  337. package/dist/trajectory/parse/lammps.d.ts +5 -0
  338. package/dist/trajectory/parse/lammps.js +179 -0
  339. package/dist/trajectory/parse/vasp.d.ts +2 -0
  340. package/dist/trajectory/parse/vasp.js +68 -0
  341. package/dist/trajectory/parse/xyz.d.ts +2 -0
  342. package/dist/trajectory/parse/xyz.js +110 -0
  343. package/dist/trajectory/plotting.js +13 -8
  344. package/dist/trajectory/types.d.ts +11 -0
  345. package/dist/trajectory/types.js +1 -0
  346. package/dist/utils.d.ts +3 -0
  347. package/dist/utils.js +17 -0
  348. package/dist/xrd/XrdPlot.svelte +337 -245
  349. package/dist/xrd/broadening.js +14 -9
  350. package/dist/xrd/calc-xrd.js +12 -19
  351. package/dist/xrd/parse.d.ts +1 -1
  352. package/dist/xrd/parse.js +17 -17
  353. package/package.json +103 -101
  354. package/readme.md +4 -4
  355. package/dist/trajectory/parse.d.ts +0 -42
  356. package/dist/trajectory/parse.js +0 -1267
  357. /package/dist/element/{data.json.d.ts → data.json.gz.d.ts} +0 -0
  358. /package/dist/theme/{themes.js → themes.mjs} +0 -0
@@ -2,9 +2,8 @@ import { COMPRESSION_EXTENSIONS_REGEX, CONFIG_DIRS_REGEX, STRUCT_KEYWORDS_REGEX,
2
2
  import { ELEM_SYMBOLS } from '../labels';
3
3
  import * as math from '../math';
4
4
  import { wrap_to_unit_cell } from './pbc';
5
+ import { normalize_scientific_notation } from '../utils';
5
6
  import { load as yaml_load } from 'js-yaml';
6
- // Normalize scientific notation in coordinate strings (handles eEdD and *^ notation variants)
7
- const normalize_scientific_notation = (str) => str.toLowerCase().replace(/d/g, `e`).replace(/\*\^/g, `e`);
8
7
  // Parse a coordinate value that might be in various scientific notation formats
9
8
  function parse_coordinate(str) {
10
9
  const normalized = normalize_scientific_notation(str.trim());
@@ -44,6 +43,19 @@ function validate_element_symbol(symbol, index) {
44
43
  console.warn(`Invalid element symbol '${symbol}', using fallback '${fallback}'`);
45
44
  return fallback;
46
45
  }
46
+ const try_create_cart_to_frac = (lattice_matrix) => {
47
+ try {
48
+ return math.create_cart_to_frac(lattice_matrix);
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ };
54
+ const approximate_cart_to_frac = (xyz, axis_lengths) => [
55
+ Math.abs(axis_lengths[0]) > math.EPS ? xyz[0] / axis_lengths[0] : 0,
56
+ Math.abs(axis_lengths[1]) > math.EPS ? xyz[1] / axis_lengths[1] : 0,
57
+ Math.abs(axis_lengths[2]) > math.EPS ? xyz[2] / axis_lengths[2] : 0,
58
+ ];
47
59
  // Parse VASP POSCAR file format
48
60
  export function parse_poscar(content) {
49
61
  try {
@@ -98,9 +110,7 @@ export function parse_poscar(content) {
98
110
  for (let lookahead_idx = 1; lookahead_idx < 10; lookahead_idx++) {
99
111
  if (line_index + lookahead_idx >= lines.length)
100
112
  break;
101
- const next_line_first_token = lines[line_index + lookahead_idx]
102
- .trim()
103
- .split(/\s+/)[0];
113
+ const next_line_first_token = lines[line_index + lookahead_idx].trim().split(/\s+/)[0];
104
114
  const next_token_as_number = parseInt(next_line_first_token);
105
115
  if (!isNaN(next_token_as_number)) {
106
116
  symbol_lines = lookahead_idx;
@@ -152,13 +162,18 @@ export function parse_poscar(content) {
152
162
  }
153
163
  // Determine coordinate mode
154
164
  const is_direct = coordinate_mode.startsWith(`D`);
155
- const is_cartesian = coordinate_mode.startsWith(`C`) ||
156
- coordinate_mode.startsWith(`K`);
165
+ const is_cartesian = coordinate_mode.startsWith(`C`) || coordinate_mode.startsWith(`K`);
157
166
  if (!is_direct && !is_cartesian) {
158
167
  console.error(`Unknown coordinate mode in POSCAR: ${coordinate_mode}`);
159
168
  return null;
160
169
  }
161
170
  // Parse atomic positions
171
+ const poscar_axis_lengths = scaled_lattice.map((lattice_vec) => Math.hypot(...lattice_vec));
172
+ const poscar_frac_to_cart = math.create_frac_to_cart(scaled_lattice);
173
+ const poscar_cart_to_frac = try_create_cart_to_frac(scaled_lattice);
174
+ if (!is_direct && !poscar_cart_to_frac) {
175
+ console.warn(`POSCAR: singular lattice, using axis-length fallback for cart→frac`);
176
+ }
162
177
  const sites = [];
163
178
  let atom_index = 0;
164
179
  for (let elem_idx = 0; elem_idx < element_symbols.length; elem_idx++) {
@@ -176,11 +191,7 @@ export function parse_poscar(content) {
176
191
  if (has_selective_dynamics) {
177
192
  const tokens = lines[coord_line_idx].trim().split(/\s+/);
178
193
  if (tokens.length >= 6) {
179
- selective_dynamics = [
180
- tokens[3] === `T`,
181
- tokens[4] === `T`,
182
- tokens[5] === `T`,
183
- ];
194
+ selective_dynamics = [tokens[3] === `T`, tokens[4] === `T`, tokens[5] === `T`];
184
195
  }
185
196
  }
186
197
  let xyz;
@@ -188,45 +199,24 @@ export function parse_poscar(content) {
188
199
  const [x, y, z] = coords;
189
200
  if (is_direct) {
190
201
  // Store fractional coordinates, wrapping to [0, 1) range
191
- abc = [x - Math.floor(x), y - Math.floor(y), z - Math.floor(z)];
192
- // Convert fractional to Cartesian coordinates
193
- const lattice_transposed = math.transpose_3x3_matrix(scaled_lattice);
194
- xyz = math.mat3x3_vec3_multiply(lattice_transposed, abc);
202
+ abc = wrap_to_unit_cell([x, y, z]);
203
+ xyz = poscar_frac_to_cart(abc);
195
204
  }
196
- else { // Already Cartesian, scale if needed
205
+ else {
206
+ // Already Cartesian, scale if needed
197
207
  xyz = math.scale([x, y, z], scale_factor);
198
- // Calculate fractional coordinates using proper matrix inversion
199
- // Note: Our lattice matrix is stored as row vectors, but for coordinate conversion
200
- // we need column vectors, so we transpose before inversion
201
- let raw_abc;
202
- try {
203
- const lattice_transposed = math.transpose_3x3_matrix(scaled_lattice);
204
- const lattice_inv = math.matrix_inverse_3x3(lattice_transposed);
205
- raw_abc = math.mat3x3_vec3_multiply(lattice_inv, xyz);
206
- }
207
- catch {
208
- // Fallback to simplified method if matrix is singular
209
- raw_abc = [
210
- xyz[0] / scaled_lattice[0][0],
211
- xyz[1] / scaled_lattice[1][1],
212
- xyz[2] / scaled_lattice[2][2],
213
- ];
214
- }
208
+ const raw_abc = poscar_cart_to_frac
209
+ ? poscar_cart_to_frac(xyz)
210
+ : approximate_cart_to_frac(xyz, poscar_axis_lengths);
215
211
  // Wrap fractional coordinates to [0, 1) range
216
- abc = [
217
- raw_abc[0] - Math.floor(raw_abc[0]),
218
- raw_abc[1] - Math.floor(raw_abc[1]),
219
- raw_abc[2] - Math.floor(raw_abc[2]),
220
- ];
212
+ abc = wrap_to_unit_cell(raw_abc);
221
213
  }
222
214
  const site = {
223
215
  species: [{ element, occu: 1, oxidation_state: 0 }],
224
216
  abc,
225
217
  xyz,
226
218
  label: `${element}${atom_index + atom_count_idx + 1}`,
227
- properties: selective_dynamics
228
- ? { selective_dynamics: selective_dynamics }
229
- : {},
219
+ properties: selective_dynamics ? { selective_dynamics: selective_dynamics } : {},
230
220
  };
231
221
  sites.push(site);
232
222
  }
@@ -263,9 +253,7 @@ export function parse_xyz(content) {
263
253
  let line_idx = 0;
264
254
  while (line_idx < all_lines.length) {
265
255
  const numAtoms = parseInt(all_lines[line_idx].trim(), 10);
266
- if (!isNaN(numAtoms) &&
267
- numAtoms > 0 &&
268
- line_idx + numAtoms + 1 < all_lines.length) {
256
+ if (!isNaN(numAtoms) && numAtoms > 0 && line_idx + numAtoms + 1 < all_lines.length) {
269
257
  const frameLines = all_lines.slice(line_idx, line_idx + numAtoms + 2);
270
258
  frames.push(frameLines.join(`\n`));
271
259
  line_idx += numAtoms + 2;
@@ -293,7 +281,7 @@ export function parse_xyz(content) {
293
281
  const comment_line = lines[1];
294
282
  let lattice;
295
283
  // Check for extended XYZ lattice information in comment line
296
- const lattice_match = comment_line.match(/Lattice="([^"]+)"/);
284
+ const lattice_match = /Lattice="([^"]+)"/.exec(comment_line);
297
285
  if (lattice_match) {
298
286
  const lattice_values = lattice_match[1].split(/\s+/).map(parse_coordinate);
299
287
  if (lattice_values.length === 9) {
@@ -307,6 +295,13 @@ export function parse_xyz(content) {
307
295
  }
308
296
  }
309
297
  // Parse atomic coordinates (starting from line 3)
298
+ const xyz_axis_lengths = lattice ? [lattice.a, lattice.b, lattice.c] : null;
299
+ let xyz_frac_to_cart = null;
300
+ let xyz_cart_to_frac = null;
301
+ if (lattice) {
302
+ xyz_frac_to_cart = math.create_frac_to_cart(lattice.matrix);
303
+ xyz_cart_to_frac = try_create_cart_to_frac(lattice.matrix);
304
+ }
310
305
  const sites = [];
311
306
  for (let atom_idx = 0; atom_idx < num_atoms; atom_idx++) {
312
307
  const line_idx = atom_idx + 2;
@@ -329,29 +324,14 @@ export function parse_xyz(content) {
329
324
  const xyz = [coords[0], coords[1], coords[2]];
330
325
  // Calculate fractional coordinates if lattice is available
331
326
  let abc = [0, 0, 0];
332
- if (lattice) {
333
- // Calculate fractional coordinates using proper matrix inversion
334
- // Note: Our lattice matrix is stored as row vectors, but for coordinate conversion
335
- // we need column vectors, so we transpose before inversion
336
- try {
337
- const lattice_transposed = math.transpose_3x3_matrix(lattice.matrix);
338
- const lattice_inv = math.matrix_inverse_3x3(lattice_transposed);
339
- abc = math.mat3x3_vec3_multiply(lattice_inv, xyz);
340
- }
341
- catch {
342
- // Fallback to simplified method if matrix is singular
343
- abc = [xyz[0] / lattice.a, xyz[1] / lattice.b, xyz[2] / lattice.c];
344
- }
327
+ if (lattice && xyz_frac_to_cart && xyz_axis_lengths) {
328
+ abc = xyz_cart_to_frac
329
+ ? xyz_cart_to_frac(xyz)
330
+ : approximate_cart_to_frac(xyz, xyz_axis_lengths);
345
331
  // Ensure fractional coordinates are wrapped into [0, 1) for consistency
346
- abc = [
347
- abc[0] - Math.floor(abc[0]),
348
- abc[1] - Math.floor(abc[1]),
349
- abc[2] - Math.floor(abc[2]),
350
- ];
332
+ abc = wrap_to_unit_cell(abc);
351
333
  // Keep rendered atoms inside primary unit cell by recomputing xyz
352
- // from the wrapped fractional coordinates using transpose(lattice)
353
- const lattice_transposed = math.transpose_3x3_matrix(lattice.matrix);
354
- const wrapped_xyz = math.mat3x3_vec3_multiply(lattice_transposed, abc);
334
+ const wrapped_xyz = xyz_frac_to_cart(abc);
355
335
  xyz[0] = wrapped_xyz[0];
356
336
  xyz[1] = wrapped_xyz[1];
357
337
  xyz[2] = wrapped_xyz[2];
@@ -396,7 +376,7 @@ const parse_symmetry_expression = (expr_input) => {
396
376
  tokens.push(current_token);
397
377
  for (const token of tokens) {
398
378
  // Check if this token is a variable term (x, y, or z with optional sign)
399
- const var_match = token.match(/^([+-]?)([xyz])$/);
379
+ const var_match = /^([+-]?)([xyz])$/.exec(token);
400
380
  if (var_match) {
401
381
  const sign = var_match[1] === `-` ? -1 : 1;
402
382
  const var_char = var_match[2];
@@ -445,9 +425,7 @@ const apply_symmetry_ops = (atom, symmetry_ops, wrap_fractional_coords) => {
445
425
  return [atom];
446
426
  const equivalent_atoms = [];
447
427
  const seen = new Set();
448
- const wrap = (coords) => (wrap_fractional_coords
449
- ? coords.map((val) => val - Math.floor(val))
450
- : coords);
428
+ const wrap = (coords) => wrap_fractional_coords ? wrap_to_unit_cell(coords) : coords;
451
429
  // Use 6 decimal places for deduplication to handle floating point imprecision
452
430
  // from compound symmetry operations like x-y, -x+y which can produce small errors
453
431
  const key = (coords) => `${coords[0].toFixed(6)},${coords[1].toFixed(6)},${coords[2].toFixed(6)}`;
@@ -456,7 +434,7 @@ const apply_symmetry_ops = (atom, symmetry_ops, wrap_fractional_coords) => {
456
434
  seen.add(key(base_coords));
457
435
  equivalent_atoms.push({ ...atom, coords: base_coords });
458
436
  for (const operation of symmetry_ops) {
459
- const operation_match = operation.match(/['"]([^'"]+)['"]/);
437
+ const operation_match = /['"]([^'"]+)['"]/.exec(operation);
460
438
  const expr_str = operation_match ? operation_match[1] : operation.trim();
461
439
  const parts = expr_str.split(`,`).map((part) => part.trim());
462
440
  if (parts.length !== 3)
@@ -465,17 +443,18 @@ const apply_symmetry_ops = (atom, symmetry_ops, wrap_fractional_coords) => {
465
443
  for (let dim = 0; dim < 3; dim++) {
466
444
  const { coefficients, translation } = parse_symmetry_expression(parts[dim]);
467
445
  // Apply: new_coord = coeff_x * x + coeff_y * y + coeff_z * z + translation
468
- new_coords[dim] = coefficients[0] * atom.coords[0] +
469
- coefficients[1] * atom.coords[1] +
470
- coefficients[2] * atom.coords[2] +
471
- translation;
446
+ new_coords[dim] =
447
+ coefficients[0] * atom.coords[0] +
448
+ coefficients[1] * atom.coords[1] +
449
+ coefficients[2] * atom.coords[2] +
450
+ translation;
472
451
  }
473
452
  // Wrap and deduplicate transformed coordinates
474
453
  const wrapped = wrap(new_coords);
475
- const k = key(wrapped);
476
- if (seen.has(k))
454
+ const cache_key = key(wrapped);
455
+ if (seen.has(cache_key))
477
456
  continue;
478
- seen.add(k);
457
+ seen.add(cache_key);
479
458
  equivalent_atoms.push({
480
459
  ...atom,
481
460
  coords: wrapped,
@@ -549,7 +528,7 @@ const parse_cif_atom_data = (raw_data, indices, coords_type) => {
549
528
  const occu = occupancy >= 0 && raw_data[occupancy]
550
529
  ? parseFloat(raw_data[occupancy].split(`(`)[0]) || 1.0
551
530
  : 1.0;
552
- const element_symbol = (symbol >= 0 && raw_data[symbol]?.match(/^([A-Z][a-z]*)/)?.[1]) ||
531
+ const element_symbol = (symbol >= 0 && /^([A-Z][a-z]*)/.exec(raw_data[symbol])?.[1]) ||
553
532
  raw_data[label]?.match(/([A-Z][a-z]*)/g)?.[0] ||
554
533
  (() => {
555
534
  throw new Error(`Could not extract element symbol from: ${raw_data.join(` `)}`);
@@ -605,9 +584,11 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
605
584
  continue;
606
585
  // Check if this loop contains coordinate headers
607
586
  const indices_preview = build_cif_atom_site_header_indices(headers);
608
- const has_coords = (indices_preview.x !== undefined && indices_preview.y !== undefined &&
587
+ const has_coords = (indices_preview.x !== undefined &&
588
+ indices_preview.y !== undefined &&
609
589
  indices_preview.z !== undefined) ||
610
- (indices_preview.cart_x !== undefined && indices_preview.cart_y !== undefined &&
590
+ (indices_preview.cart_x !== undefined &&
591
+ indices_preview.cart_y !== undefined &&
611
592
  indices_preview.cart_z !== undefined);
612
593
  if (!has_coords) {
613
594
  ii = jj - 1;
@@ -645,10 +626,12 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
645
626
  // Parse atom data with error handling
646
627
  const header_indices = build_cif_atom_site_header_indices(atom_headers);
647
628
  // Determine available coordinate type
648
- const coords_type = header_indices.x !== undefined && header_indices.y !== undefined &&
629
+ const coords_type = header_indices.x !== undefined &&
630
+ header_indices.y !== undefined &&
649
631
  header_indices.z !== undefined
650
632
  ? `fract`
651
- : header_indices.cart_x !== undefined && header_indices.cart_y !== undefined &&
633
+ : header_indices.cart_x !== undefined &&
634
+ header_indices.cart_y !== undefined &&
652
635
  header_indices.cart_z !== undefined
653
636
  ? `cart`
654
637
  : null;
@@ -699,21 +682,15 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
699
682
  const [alpha, beta, gamma] = angles;
700
683
  const lattice_matrix = math.cell_to_lattice_matrix(a, b, c, alpha, beta, gamma);
701
684
  const lattice_params = math.calc_lattice_params(lattice_matrix);
702
- const lattice_transposed = math.transpose_3x3_matrix(lattice_matrix);
703
- let lattice_inv_transposed = null;
704
- try {
705
- lattice_inv_transposed = math.matrix_inverse_3x3(lattice_transposed);
706
- }
707
- catch {
708
- lattice_inv_transposed = null;
709
- }
685
+ const frac_to_cart = math.create_frac_to_cart(lattice_matrix);
686
+ const cart_to_frac = try_create_cart_to_frac(lattice_matrix);
710
687
  // Create sites with coordinate conversion and symmetry operations
711
- const wrap_vec3 = (v) => wrap_fractional_coords ? v.map((coord) => coord - Math.floor(coord)) : v;
688
+ const wrap_vec3 = (v) => (wrap_fractional_coords ? wrap_to_unit_cell(v) : v);
712
689
  // Apply symmetry operations to generate all equivalent positions
713
690
  const all_sites = [];
714
691
  // Normalize symmetry operations (trim/strip quotes) but preserve duplicates; we deduplicate positions later
715
692
  const normalized_ops = symmetry_ops
716
- .map((op) => op.match(/['\"]([^'\"]+)['\"]/)?.[1] || op.trim())
693
+ .map((op) => /['"]([^'"]+)['"]/.exec(op)?.[1] || op.trim())
717
694
  .map((op) => op.replace(/\s+/g, ``));
718
695
  // Rely on symmetry operations list for all centering/translations to avoid double-counting
719
696
  // TODO: Support conventional cells with centering by discovering centering from space group metadata
@@ -747,7 +724,7 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
747
724
  const toks = (line.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || []).map((tok) => tok.replace(/['"]/g, ``));
748
725
  if (toks.length > Math.max(sym_idx, num_idx)) {
749
726
  // Normalize type symbol to bare element (e.g. 'Sn2+' -> 'Sn')
750
- const match = toks[sym_idx]?.match(/^([A-Z][a-z]*)/);
727
+ const match = /^([A-Z][a-z]*)/.exec(toks[sym_idx]);
751
728
  const sym = match ? match[1] : toks[sym_idx];
752
729
  const num = parseInt(toks[num_idx]);
753
730
  if (sym && !Number.isNaN(num))
@@ -785,13 +762,9 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
785
762
  }
786
763
  else {
787
764
  const xyz_base = [atom.coords[0], atom.coords[1], atom.coords[2]];
788
- let atom_abc;
789
- if (lattice_inv_transposed) {
790
- const raw = math.mat3x3_vec3_multiply(lattice_inv_transposed, xyz_base);
791
- atom_abc = wrap_vec3(raw);
792
- }
793
- else
794
- atom_abc = wrap_vec3([xyz_base[0] / a, xyz_base[1] / b, xyz_base[2] / c]);
765
+ const atom_abc = wrap_vec3(cart_to_frac
766
+ ? cart_to_frac(xyz_base)
767
+ : approximate_cart_to_frac(xyz_base, [a, b, c]));
795
768
  fractional_atom = { ...atom, coords: atom_abc, coords_type: `fract` };
796
769
  }
797
770
  // First apply symmetry operations in fractional space
@@ -808,7 +781,7 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
808
781
  if (seen_site_keys.has(key))
809
782
  continue;
810
783
  seen_site_keys.add(key);
811
- const xyz = math.mat3x3_vec3_multiply(lattice_transposed, abc);
784
+ const xyz = frac_to_cart(abc);
812
785
  all_sites.push({
813
786
  species: [{ element, occu: equiv_atom.occupancy, oxidation_state: 0 }],
814
787
  abc,
@@ -833,15 +806,11 @@ function convert_phonopy_cell(cell) {
833
806
  // Phonopy stores lattice vectors as rows, use them directly
834
807
  const lattice_matrix = cell.lattice;
835
808
  // Process each atomic site
809
+ const phonopy_frac_to_cart = math.create_frac_to_cart(lattice_matrix);
836
810
  for (const point of cell.points) {
837
811
  const element = validate_element_symbol(point.symbol, sites.length);
838
- const abc = [
839
- point.coordinates[0],
840
- point.coordinates[1],
841
- point.coordinates[2],
842
- ];
843
- // Convert fractional to Cartesian coordinates
844
- const xyz = math.mat3x3_vec3_multiply(math.transpose_3x3_matrix(lattice_matrix), abc);
812
+ const abc = [point.coordinates[0], point.coordinates[1], point.coordinates[2]];
813
+ const xyz = phonopy_frac_to_cart(abc);
845
814
  const properties = {
846
815
  mass: point.mass,
847
816
  ...(point.reduced_to !== undefined && { reduced_to: point.reduced_to }),
@@ -869,7 +838,7 @@ export function parse_phonopy_yaml(content, cell_type) {
869
838
  }
870
839
  // Check if we're still in the phonon_displacements section
871
840
  if (skip_displacements) {
872
- if (line.match(/^[a-zA-Z_]/)) {
841
+ if (/^[a-zA-Z_]/.exec(line)) {
873
842
  // New top-level key, stop skipping
874
843
  skip_displacements = false;
875
844
  }
@@ -922,7 +891,7 @@ export function parse_phonopy_yaml(content, cell_type) {
922
891
  // Recursively search for a valid structure object in nested JSON
923
892
  function find_structure_in_json(obj, visited = new WeakSet()) {
924
893
  // Check if current object is null or undefined
925
- if (obj === null || obj === undefined)
894
+ if (obj == null)
926
895
  return null;
927
896
  if (typeof obj !== `object`)
928
897
  return null; // If it's not an object, skip it
@@ -939,11 +908,10 @@ function find_structure_in_json(obj, visited = new WeakSet()) {
939
908
  return null;
940
909
  }
941
910
  // Check if this object looks like a valid structure
942
- const potential_structure = obj;
943
- if (is_parsed_structure(potential_structure))
944
- return potential_structure;
911
+ if (is_parsed_structure(obj))
912
+ return obj;
945
913
  // Otherwise, recursively search through all properties
946
- for (const value of Object.values(potential_structure)) {
914
+ for (const value of Object.values(obj)) {
947
915
  const result = find_structure_in_json(value, visited);
948
916
  if (result)
949
917
  return result;
@@ -954,17 +922,19 @@ function find_structure_in_json(obj, visited = new WeakSet()) {
954
922
  function is_parsed_structure(obj) {
955
923
  if (!obj || typeof obj !== `object`)
956
924
  return false;
957
- const record = obj;
958
- // Must have non-empty sites array
959
- if (!Array.isArray(record.sites) || record.sites.length === 0)
925
+ const parsed_obj = obj;
926
+ const sites = parsed_obj.sites;
927
+ if (!Array.isArray(sites) || sites.length === 0)
960
928
  return false;
961
- // Check if first site looks valid (has species and coordinates)
962
- const first_site = record.sites[0];
929
+ const first_site = sites[0];
963
930
  if (!first_site || typeof first_site !== `object`)
964
931
  return false;
965
- // Must have species (array) and either abc or xyz coordinates
966
- const has_species = Array.isArray(first_site.species) && first_site.species.length > 0;
967
- const has_coords = Array.isArray(first_site.abc) || Array.isArray(first_site.xyz);
932
+ const first_site_obj = first_site;
933
+ const species = first_site_obj.species;
934
+ const abc = first_site_obj.abc;
935
+ const xyz = first_site_obj.xyz;
936
+ const has_species = Array.isArray(species) && species.length > 0;
937
+ const has_coords = Array.isArray(abc) || Array.isArray(xyz);
968
938
  return has_species && has_coords;
969
939
  }
970
940
  // Normalize structure coordinates: wrap fractional coords to [0,1) and recompute Cartesian
@@ -1073,8 +1043,7 @@ export function parse_structure_file(content, filename) {
1073
1043
  const coords = parts.slice(1, 4);
1074
1044
  // Check if first token looks like an element symbol (not a number)
1075
1045
  // and the next 3 tokens look like coordinates (numbers)
1076
- const is_element_symbol = isNaN(parseInt(first_token)) &&
1077
- first_token.length <= 3;
1046
+ const is_element_symbol = isNaN(parseInt(first_token)) && first_token.length <= 3;
1078
1047
  const are_coordinates = coords.every((coord) => !isNaN(parseFloat(coord)));
1079
1048
  if (is_element_symbol && are_coordinates) {
1080
1049
  // First token is likely an element symbol, likely XYZ
@@ -1188,7 +1157,24 @@ export function parse_optimade_from_raw(raw) {
1188
1157
  const species = species_raw;
1189
1158
  // Optimade stores lattice vectors as rows, so use as is
1190
1159
  const lattice_matrix = attrs.lattice_vectors;
1160
+ const optimade_lattice_params = lattice_matrix
1161
+ ? math.calc_lattice_params(lattice_matrix)
1162
+ : null;
1191
1163
  // Parse atomic sites
1164
+ const optimade_exact_cart_to_frac = lattice_matrix
1165
+ ? try_create_cart_to_frac(lattice_matrix)
1166
+ : null;
1167
+ const optimade_cart_to_frac = lattice_matrix && optimade_lattice_params
1168
+ ? (optimade_exact_cart_to_frac ??
1169
+ ((xyz) => approximate_cart_to_frac(xyz, [
1170
+ optimade_lattice_params.a,
1171
+ optimade_lattice_params.b,
1172
+ optimade_lattice_params.c,
1173
+ ])))
1174
+ : null;
1175
+ if (lattice_matrix && !optimade_exact_cart_to_frac) {
1176
+ console.warn(`Failed to create exact coordinate converter for OPTIMADE structure`);
1177
+ }
1192
1178
  const sites = [];
1193
1179
  for (let idx = 0; idx < positions.length; idx++) {
1194
1180
  const pos = positions[idx];
@@ -1200,18 +1186,7 @@ export function parse_optimade_from_raw(raw) {
1200
1186
  const element = validate_element_symbol(element_symbol, idx);
1201
1187
  const xyz = [pos[0], pos[1], pos[2]];
1202
1188
  // Calculate fractional coordinates if lattice is available
1203
- let abc = [0, 0, 0];
1204
- if (lattice_matrix) {
1205
- try {
1206
- const lattice_transposed = math.transpose_3x3_matrix(lattice_matrix);
1207
- const lattice_inv = math.matrix_inverse_3x3(lattice_transposed);
1208
- abc = math.mat3x3_vec3_multiply(lattice_inv, xyz);
1209
- }
1210
- catch {
1211
- // Fallback if matrix inversion fails
1212
- console.warn(`Failed to calculate fractional coordinates for OPTIMADE structure`);
1213
- }
1214
- }
1189
+ const abc = optimade_cart_to_frac ? optimade_cart_to_frac(xyz) : [0, 0, 0];
1215
1190
  const site = {
1216
1191
  species: [{ element, occu: 1, oxidation_state: 0 }],
1217
1192
  abc,
@@ -1227,9 +1202,8 @@ export function parse_optimade_from_raw(raw) {
1227
1202
  }
1228
1203
  // Create structure object
1229
1204
  let lattice;
1230
- if (lattice_matrix) {
1231
- const lattice_params = math.calc_lattice_params(lattice_matrix);
1232
- lattice = { matrix: lattice_matrix, ...lattice_params };
1205
+ if (lattice_matrix && optimade_lattice_params) {
1206
+ lattice = { matrix: lattice_matrix, ...optimade_lattice_params };
1233
1207
  }
1234
1208
  const structure_result = {
1235
1209
  sites,
@@ -1260,7 +1234,7 @@ function extract_optimade_structure_from_raw(raw) {
1260
1234
  const candidate = Array.isArray(payload) ? payload[0] : payload;
1261
1235
  return is_optimade_structure_object(candidate) ? candidate : null;
1262
1236
  }
1263
- const unwrap_data = (value) => (value && typeof value === `object` && `data` in value)
1237
+ const unwrap_data = (value) => value && typeof value === `object` && `data` in value
1264
1238
  ? value.data
1265
1239
  : value;
1266
1240
  // Type guard: verify minimal OPTIMADE structure shape
@@ -1268,8 +1242,13 @@ function is_optimade_structure_object(value) {
1268
1242
  if (!value || typeof value !== `object`)
1269
1243
  return false;
1270
1244
  const obj = value;
1271
- return obj.type === `structures` && typeof obj.id === `string` &&
1272
- typeof obj.attributes === `object` && obj.attributes !== null;
1245
+ const type = obj.type;
1246
+ const id = obj.id;
1247
+ const attributes = obj.attributes;
1248
+ return (type === `structures` &&
1249
+ typeof id === `string` &&
1250
+ typeof attributes === `object` &&
1251
+ attributes !== null);
1273
1252
  }
1274
1253
  // Convert OPTIMADE structure to Crystal format
1275
1254
  export function optimade_to_crystal(optimade_structure) {
@@ -1287,21 +1266,15 @@ export function optimade_to_crystal(optimade_structure) {
1287
1266
  const lattice_params = math.calc_lattice_params(lattice_matrix);
1288
1267
  // Build species lookup for site properties (mass, concentration, etc.)
1289
1268
  const species_map = new Map(species?.map((spec) => [spec.name, spec]));
1269
+ const crystal_cart_to_frac = try_create_cart_to_frac(lattice_matrix) ??
1270
+ ((xyz) => approximate_cart_to_frac(xyz, [lattice_params.a, lattice_params.b, lattice_params.c]));
1290
1271
  const sites = cartesian_site_positions.map((pos, idx) => {
1291
1272
  const element_symbol = species_at_sites[idx];
1292
1273
  if (!element_symbol)
1293
1274
  throw new Error(`Missing species for site ${idx}`);
1294
1275
  const element = validate_element_symbol(element_symbol, idx);
1295
1276
  const xyz = [pos[0], pos[1], pos[2]];
1296
- let abc;
1297
- try {
1298
- const lattice_transposed = math.transpose_3x3_matrix(lattice_matrix);
1299
- const inv_matrix = math.matrix_inverse_3x3(lattice_transposed);
1300
- abc = math.mat3x3_vec3_multiply(inv_matrix, xyz);
1301
- }
1302
- catch {
1303
- abc = [0, 0, 0];
1304
- }
1277
+ const abc = crystal_cart_to_frac ? crystal_cart_to_frac(xyz) : [0, 0, 0];
1305
1278
  // Extract mass/concentration from species data
1306
1279
  const spec = species_map.get(element_symbol);
1307
1280
  const site_props = {};
@@ -1348,8 +1321,10 @@ export function is_structure_file(filename) {
1348
1321
  if (/\.(yaml|yml|xml)$/i.test(name) && STRUCT_KEYWORDS_REGEX.test(name))
1349
1322
  return true;
1350
1323
  // More restrictive keyword detection for JSON files
1351
- if (/\.json$/i.test(name) && STRUCT_KEYWORDS_STRICT_REGEX.test(name) &&
1352
- !TRAJ_KEYWORDS_REGEX.test(name) && !CONFIG_DIRS_REGEX.test(name))
1324
+ if (/\.json$/i.test(name) &&
1325
+ STRUCT_KEYWORDS_STRICT_REGEX.test(name) &&
1326
+ !TRAJ_KEYWORDS_REGEX.test(name) &&
1327
+ !CONFIG_DIRS_REGEX.test(name))
1353
1328
  return true;
1354
1329
  // Compressed files - check base filename recursively
1355
1330
  if (COMPRESSION_EXTENSIONS_REGEX.test(name)) {
@@ -0,0 +1,25 @@
1
+ import type { Site } from './';
2
+ export declare const PARTIAL_OCCUPANCY_SLICE_GAP_RAD = 0.001;
3
+ export type RenderSite = {
4
+ site_idx: number;
5
+ site: Site;
6
+ is_image_atom: boolean;
7
+ source_site_indices: number[];
8
+ };
9
+ export type SliceGeometry = {
10
+ element: string;
11
+ occupancy: number;
12
+ start_phi: number;
13
+ end_phi: number;
14
+ phi_length: number;
15
+ render_start_cap: boolean;
16
+ render_end_cap: boolean;
17
+ };
18
+ export type CapArcConfig = {
19
+ start_cap_arc_start: number;
20
+ end_cap_arc_start: number;
21
+ arc_length: number;
22
+ };
23
+ export declare const PARTIAL_OCCUPANCY_CAP_ARC: CapArcConfig;
24
+ export declare const merge_split_partial_sites: (sites: Site[], hidden_elements?: ReadonlySet<string>) => RenderSite[];
25
+ export declare const compute_slice_geometry: (visible_species: Site[`species`], slice_gap_rad?: number) => SliceGeometry[];