matterviz 0.4.1 → 0.4.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.
- package/dist/EmptyState.svelte +10 -4
- package/dist/FilePicker.svelte +48 -60
- package/dist/Icon.svelte +7 -4
- package/dist/MillerIndexInput.svelte +2 -2
- package/dist/api/optimade.js +10 -7
- package/dist/app.css +68 -0
- package/dist/brillouin/BrillouinZone.svelte +90 -91
- package/dist/brillouin/BrillouinZone.svelte.d.ts +5 -7
- package/dist/brillouin/BrillouinZoneControls.svelte +3 -6
- package/dist/brillouin/BrillouinZoneExportPane.svelte +18 -14
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +6 -10
- package/dist/brillouin/BrillouinZoneScene.svelte +46 -68
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
- package/dist/brillouin/compute.js +10 -16
- package/dist/brillouin/geometry.js +2 -2
- package/dist/chempot-diagram/ChemPotDiagram.svelte +48 -50
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +61 -169
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +1 -1
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +559 -755
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +1 -1
- package/dist/chempot-diagram/async-compute.svelte.js +13 -14
- package/dist/chempot-diagram/color.d.ts +21 -1
- package/dist/chempot-diagram/color.js +70 -1
- package/dist/chempot-diagram/compute.d.ts +7 -0
- package/dist/chempot-diagram/compute.js +85 -26
- package/dist/chempot-diagram/controls-state.svelte.js +1 -1
- package/dist/chempot-diagram/export.js +1 -6
- package/dist/chempot-diagram/temperature.js +9 -10
- package/dist/colors/index.d.ts +0 -4
- package/dist/colors/index.js +19 -20
- package/dist/composition/BarChart.svelte +30 -26
- package/dist/composition/BubbleChart.svelte +17 -17
- package/dist/composition/Composition.svelte +10 -19
- package/dist/composition/Formula.svelte +20 -27
- package/dist/composition/FormulaFilter.svelte +115 -184
- package/dist/composition/PieChart.svelte +55 -41
- package/dist/composition/format.js +7 -9
- package/dist/composition/index.d.ts +2 -0
- package/dist/composition/index.js +14 -0
- package/dist/composition/parse.d.ts +2 -2
- package/dist/composition/parse.js +61 -115
- package/dist/constants.d.ts +0 -1
- package/dist/constants.js +17 -26
- package/dist/convex-hull/ConvexHull.svelte +12 -15
- package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull2D.svelte +93 -94
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +127 -186
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +131 -180
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullChrome.svelte +36 -40
- package/dist/convex-hull/ConvexHullControls.svelte +46 -48
- package/dist/convex-hull/ConvexHullStats.svelte +193 -195
- package/dist/convex-hull/ConvexHullTooltip.svelte +5 -12
- package/dist/convex-hull/GasPressureControls.svelte +5 -12
- package/dist/convex-hull/StructurePopup.svelte +18 -22
- package/dist/convex-hull/StructurePopup.svelte.d.ts +2 -0
- package/dist/convex-hull/TemperatureSlider.svelte +4 -10
- package/dist/convex-hull/canvas-interactions.svelte.js +2 -1
- package/dist/convex-hull/gas-thermodynamics.js +21 -53
- package/dist/convex-hull/helpers.d.ts +8 -10
- package/dist/convex-hull/helpers.js +37 -43
- package/dist/convex-hull/hull-state.svelte.d.ts +1 -1
- package/dist/convex-hull/thermodynamics.js +37 -44
- package/dist/coordination/CoordinationBarPlot.svelte +49 -53
- package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
- package/dist/coordination/calc-coordination.d.ts +1 -1
- package/dist/coordination/calc-coordination.js +28 -25
- package/dist/element/BohrAtom.svelte +5 -9
- package/dist/element/ElementHeading.svelte +4 -1
- package/dist/element/ElementPhoto.svelte +11 -9
- package/dist/element/ElementStats.svelte +12 -10
- package/dist/element/ElementTile.svelte +44 -67
- package/dist/element/Nucleus.svelte +4 -2
- package/dist/element/data.d.ts +3 -2
- package/dist/element/data.js +5 -1
- package/dist/element/helpers.d.ts +4 -0
- package/dist/element/helpers.js +18 -0
- package/dist/element/index.d.ts +2 -5
- package/dist/element/index.js +4 -19
- package/dist/feedback/DragOverlay.svelte +5 -5
- package/dist/feedback/DragOverlay.svelte.d.ts +2 -2
- package/dist/feedback/StatusMessage.svelte +7 -4
- package/dist/fermi-surface/FermiSlice.svelte +21 -18
- package/dist/fermi-surface/FermiSurface.svelte +35 -44
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +7 -13
- package/dist/fermi-surface/FermiSurfaceControls.svelte +6 -20
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +64 -136
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +9 -6
- package/dist/fermi-surface/compute.js +25 -58
- package/dist/fermi-surface/parse.js +54 -88
- package/dist/file-viewer/JsonBrowser.svelte +1043 -0
- package/dist/file-viewer/JsonBrowser.svelte.d.ts +9 -0
- package/dist/file-viewer/PlotPanel.svelte +350 -0
- package/dist/file-viewer/PlotPanel.svelte.d.ts +9 -0
- package/dist/file-viewer/TrajectoryWithDos.svelte +30 -0
- package/dist/file-viewer/TrajectoryWithDos.svelte.d.ts +10 -0
- package/dist/file-viewer/detect.d.ts +11 -0
- package/dist/file-viewer/detect.js +381 -0
- package/dist/file-viewer/eligibility.d.ts +4 -0
- package/dist/file-viewer/eligibility.js +53 -0
- package/dist/file-viewer/host-protocol.d.ts +50 -0
- package/dist/file-viewer/host-transfer.d.ts +25 -0
- package/dist/file-viewer/host-transfer.js +42 -0
- package/dist/file-viewer/index.d.ts +5 -0
- package/dist/file-viewer/index.js +5 -0
- package/dist/file-viewer/main.d.ts +44 -0
- package/dist/file-viewer/main.js +535 -0
- package/dist/file-viewer/parse.d.ts +9 -0
- package/dist/file-viewer/parse.js +170 -0
- package/dist/file-viewer/plot-utils.d.ts +24 -0
- package/dist/file-viewer/plot-utils.js +191 -0
- package/dist/file-viewer/types.d.ts +4 -0
- package/dist/file-viewer/types.js +5 -0
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +153 -219
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +2 -2
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +9 -7
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
- package/dist/heatmap-matrix/index.d.ts +5 -0
- package/dist/icons.d.ts +4 -0
- package/dist/icons.js +4 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/io/ExportPane.svelte +11 -7
- package/dist/io/decompress.d.ts +4 -1
- package/dist/io/decompress.js +24 -36
- package/dist/io/export.d.ts +11 -4
- package/dist/io/export.js +75 -19
- package/dist/io/fetch.d.ts +0 -3
- package/dist/io/fetch.js +0 -11
- package/dist/io/file-drop.js +60 -35
- package/dist/io/index.d.ts +0 -1
- package/dist/io/index.js +2 -9
- package/dist/io/is-binary.d.ts +9 -0
- package/dist/io/is-binary.js +45 -1
- package/dist/io/url-drop.d.ts +2 -0
- package/dist/io/url-drop.js +40 -40
- package/dist/isosurface/Isosurface.svelte +700 -215
- package/dist/isosurface/Isosurface.svelte.d.ts +7 -1
- package/dist/isosurface/IsosurfaceControls.svelte +590 -116
- package/dist/isosurface/IsosurfaceControls.svelte.d.ts +1 -1
- package/dist/isosurface/VolumeSlice.svelte +224 -0
- package/dist/isosurface/VolumeSlice.svelte.d.ts +28 -0
- package/dist/isosurface/coloring.d.ts +20 -0
- package/dist/isosurface/coloring.js +118 -0
- package/dist/isosurface/geometry-worker-types.d.ts +38 -0
- package/dist/isosurface/geometry.worker.d.ts +1 -0
- package/dist/isosurface/geometry.worker.js +59 -0
- package/dist/isosurface/grid.d.ts +10 -0
- package/dist/isosurface/grid.js +31 -0
- package/dist/isosurface/index.d.ts +4 -0
- package/dist/isosurface/index.js +4 -0
- package/dist/isosurface/parse-vaspwave.d.ts +3 -0
- package/dist/isosurface/parse-vaspwave.js +138 -0
- package/dist/isosurface/parse.js +6 -6
- package/dist/isosurface/profile.d.ts +12 -0
- package/dist/isosurface/profile.js +19 -0
- package/dist/isosurface/sampling.d.ts +34 -0
- package/dist/isosurface/sampling.js +408 -0
- package/dist/isosurface/slice-rendering.d.ts +13 -0
- package/dist/isosurface/slice-rendering.js +79 -0
- package/dist/isosurface/slice.d.ts +22 -2
- package/dist/isosurface/slice.js +200 -116
- package/dist/isosurface/types.d.ts +24 -1
- package/dist/isosurface/types.js +120 -8
- package/dist/json-path.d.ts +4 -0
- package/dist/json-path.js +89 -0
- package/dist/labels.d.ts +6 -2
- package/dist/labels.js +44 -14
- package/dist/layout/FullscreenButton.svelte +2 -1
- package/dist/layout/FullscreenToggle.svelte +10 -4
- package/dist/layout/InfoCard.svelte +8 -21
- package/dist/layout/InfoTag.svelte +1 -3
- package/dist/layout/NumberRangeInput.svelte +46 -0
- package/dist/layout/NumberRangeInput.svelte.d.ts +12 -0
- package/dist/layout/PropertyFilter.svelte +2 -6
- package/dist/layout/SettingsSection.svelte +57 -62
- package/dist/layout/SettingsSection.svelte.d.ts +6 -7
- package/dist/layout/SubpageGrid.svelte +3 -1
- package/dist/layout/ViewerChrome.svelte +16 -11
- package/dist/layout/ViewerChrome.svelte.d.ts +2 -1
- package/dist/layout/fullscreen.js +3 -5
- package/dist/layout/fullscreen.svelte.js +1 -2
- package/dist/layout/index.d.ts +1 -0
- package/dist/layout/index.js +1 -0
- package/dist/layout/json-tree/JsonNode.svelte +22 -59
- package/dist/layout/json-tree/JsonTree.svelte +101 -172
- package/dist/layout/json-tree/JsonValue.svelte +9 -32
- package/dist/layout/json-tree/utils.d.ts +0 -3
- package/dist/layout/json-tree/utils.js +50 -188
- package/dist/marching-cubes.d.ts +8 -1
- package/dist/marching-cubes.js +115 -48
- package/dist/math.d.ts +5 -2
- package/dist/math.js +48 -38
- package/dist/overlays/ContextMenu.svelte +19 -34
- package/dist/overlays/CopyButton.svelte +11 -5
- package/dist/overlays/DragControlTab.svelte +6 -1
- package/dist/overlays/DraggablePane.svelte +110 -74
- package/dist/overlays/DraggablePane.svelte.d.ts +2 -1
- package/dist/overlays/GlassChip.svelte +32 -0
- package/dist/overlays/GlassChip.svelte.d.ts +8 -0
- package/dist/overlays/InfoPaneCards.svelte +11 -23
- package/dist/overlays/index.d.ts +7 -0
- package/dist/overlays/index.js +26 -0
- package/dist/overlays/portal.d.ts +2 -0
- package/dist/overlays/portal.js +14 -0
- package/dist/periodic-table/PeriodicTable.svelte +113 -145
- package/dist/periodic-table/PeriodicTable.svelte.d.ts +5 -3
- package/dist/periodic-table/PeriodicTableControls.svelte +37 -115
- package/dist/periodic-table/TableInset.svelte +9 -4
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +68 -105
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -8
- package/dist/phase-diagram/PhaseDiagramControls.svelte +70 -94
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +12 -12
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +54 -58
- package/dist/phase-diagram/TdbInfoPanel.svelte +21 -15
- package/dist/phase-diagram/build-diagram.js +4 -9
- package/dist/phase-diagram/diagram-input.d.ts +1 -2
- package/dist/phase-diagram/parse.js +15 -15
- package/dist/phase-diagram/svg-to-diagram.js +28 -27
- package/dist/phase-diagram/utils.js +2 -3
- package/dist/plot/bar/BarPlot.svelte +461 -426
- package/dist/plot/bar/BarPlot.svelte.d.ts +2 -0
- package/dist/plot/bar/BarPlotControls.svelte +1 -3
- package/dist/plot/bar/BarPlotControls.svelte.d.ts +4 -6
- package/dist/plot/bar/SpacegroupBarPlot.svelte +46 -77
- package/dist/plot/bar/data.d.ts +5 -4
- package/dist/plot/bar/data.js +2 -1
- package/dist/plot/box/BoxPlot.svelte +322 -226
- package/dist/plot/box/BoxPlot.svelte.d.ts +2 -0
- package/dist/plot/box/Violin.svelte.d.ts +1 -0
- package/dist/plot/box/box-plot.js +1 -1
- package/dist/plot/core/auto-place.d.ts +1 -1
- package/dist/plot/core/auto-place.js +2 -2
- package/dist/plot/core/axis-utils.d.ts +12 -7
- package/dist/plot/core/axis-utils.js +48 -48
- package/dist/plot/core/components/AxisLabel.svelte +82 -20
- package/dist/plot/core/components/AxisLabel.svelte.d.ts +1 -0
- package/dist/plot/core/components/ColorBar.svelte +101 -106
- package/dist/plot/core/components/ColorScaleSelect.svelte +1 -1
- package/dist/plot/core/components/FillArea.svelte +10 -17
- package/dist/plot/core/components/HierarchyControls.svelte +249 -0
- package/dist/plot/{sunburst/SunburstControls.svelte.d.ts → core/components/HierarchyControls.svelte.d.ts} +12 -8
- package/dist/plot/core/components/InteractiveAxisLabel.svelte +1 -1
- package/dist/plot/core/components/InteractiveAxisLabel.svelte.d.ts +1 -1
- package/dist/plot/core/components/Line.svelte +18 -9
- package/dist/plot/core/components/Line.svelte.d.ts +2 -0
- package/dist/plot/core/components/PlotAxis.svelte +40 -17
- package/dist/plot/core/components/PlotControls.svelte +65 -41
- package/dist/plot/core/components/PlotLegend.svelte +31 -35
- package/dist/plot/core/components/PlotMarginals.svelte +616 -0
- package/dist/plot/core/components/PlotMarginals.svelte.d.ts +15 -0
- package/dist/plot/core/components/PlotTooltip.svelte +1 -1
- package/dist/plot/core/components/PortalSelect.svelte +4 -8
- package/dist/plot/core/components/ReferenceLine.svelte +27 -24
- package/dist/plot/core/components/ReferenceLine3D.svelte +54 -57
- package/dist/plot/core/components/ReferencePlane.svelte +7 -8
- package/dist/plot/core/components/ZeroLines.svelte +4 -8
- package/dist/plot/core/components/ZoomRect.svelte +1 -2
- package/dist/plot/core/components/index.d.ts +1 -0
- package/dist/plot/core/components/index.js +1 -0
- package/dist/plot/core/data-cleaning-signal.d.ts +46 -0
- package/dist/plot/core/data-cleaning-signal.js +467 -0
- package/dist/plot/core/data-cleaning.d.ts +2 -42
- package/dist/plot/core/data-cleaning.js +27 -474
- package/dist/plot/core/data-transform.d.ts +1 -2
- package/dist/plot/core/data-transform.js +9 -15
- package/dist/plot/core/fill-utils.d.ts +3 -1
- package/dist/plot/core/fill-utils.js +28 -6
- package/dist/plot/core/index.d.ts +1 -0
- package/dist/plot/core/index.js +1 -0
- package/dist/plot/core/interactions.js +0 -2
- package/dist/plot/core/layout.d.ts +15 -6
- package/dist/plot/core/layout.js +57 -38
- package/dist/plot/core/marginals.d.ts +170 -0
- package/dist/plot/core/marginals.js +436 -0
- package/dist/plot/core/pan-zoom.svelte.d.ts +2 -4
- package/dist/plot/core/pan-zoom.svelte.js +34 -17
- package/dist/plot/core/placed-tween.svelte.d.ts +3 -0
- package/dist/plot/core/placed-tween.svelte.js +68 -21
- package/dist/plot/core/reference-line.js +1 -1
- package/dist/plot/core/scales.d.ts +23 -0
- package/dist/plot/core/scales.js +39 -13
- package/dist/plot/core/svg.js +0 -1
- package/dist/plot/core/types/fills.d.ts +101 -0
- package/dist/plot/core/types/fills.js +15 -0
- package/dist/plot/core/types/plot-3d.d.ts +87 -0
- package/dist/plot/core/types/plot-3d.js +4 -0
- package/dist/plot/core/types/reference-lines.d.ts +140 -0
- package/dist/plot/core/types/reference-lines.js +9 -0
- package/dist/plot/core/types.d.ts +29 -324
- package/dist/plot/core/types.js +23 -20
- package/dist/plot/core/utils/hierarchy-chart.d.ts +52 -0
- package/dist/plot/core/utils/hierarchy-chart.js +191 -0
- package/dist/plot/core/utils/hierarchy-labels.d.ts +20 -0
- package/dist/plot/core/utils/hierarchy-labels.js +100 -0
- package/dist/plot/core/utils/label-placement.js +2 -2
- package/dist/plot/histogram/Histogram.svelte +314 -340
- package/dist/plot/histogram/Histogram.svelte.d.ts +7 -7
- package/dist/plot/histogram/HistogramControls.svelte +30 -28
- package/dist/plot/histogram/histogram.d.ts +31 -0
- package/dist/plot/histogram/histogram.js +70 -0
- package/dist/plot/histogram/index.d.ts +1 -0
- package/dist/plot/histogram/index.js +1 -0
- package/dist/plot/index.d.ts +1 -0
- package/dist/plot/index.js +1 -0
- package/dist/plot/sankey/Sankey.svelte +78 -69
- package/dist/plot/sankey/Sankey.svelte.d.ts +2 -2
- package/dist/plot/sankey/SankeyControls.svelte +22 -16
- package/dist/plot/sankey/sankey.js +13 -9
- package/dist/plot/scatter/BinnedScatterPlot.svelte +311 -141
- package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +7 -0
- package/dist/plot/scatter/ElementScatter.svelte +4 -1
- package/dist/plot/scatter/ScatterPlot.svelte +517 -410
- package/dist/plot/scatter/ScatterPlot.svelte.d.ts +2 -0
- package/dist/plot/scatter/ScatterPlotControls.svelte +18 -19
- package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/scatter/ScatterPoint.svelte +16 -9
- package/dist/plot/scatter/adaptive-density.d.ts +1 -1
- package/dist/plot/scatter/adaptive-density.js +36 -20
- package/dist/plot/scatter/binned-scatter-types.d.ts +3 -2
- package/dist/plot/scatter/binned-scatter-types.js +6 -1
- package/dist/plot/scatter/scatter-data.d.ts +1 -1
- package/dist/plot/scatter/scatter-data.js +8 -7
- package/dist/plot/scatter-3d/ScatterPlot3D.svelte +25 -34
- package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +2 -2
- package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +8 -5
- package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +113 -116
- package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +2 -2
- package/dist/plot/scatter-3d/Surface3D.svelte +4 -13
- package/dist/plot/sunburst/Sunburst.svelte +269 -347
- package/dist/plot/sunburst/Sunburst.svelte.d.ts +2 -2
- package/dist/plot/sunburst/index.d.ts +1 -1
- package/dist/plot/sunburst/index.js +2 -1
- package/dist/plot/sunburst/render.d.ts +2 -2
- package/dist/plot/sunburst/render.js +47 -22
- package/dist/plot/sunburst/sunburst.d.ts +5 -1
- package/dist/plot/sunburst/sunburst.js +10 -3
- package/dist/plot/treemap/Treemap.svelte +1022 -0
- package/dist/plot/treemap/Treemap.svelte.d.ts +90 -0
- package/dist/plot/treemap/index.d.ts +4 -0
- package/dist/plot/treemap/index.js +4 -0
- package/dist/plot/treemap/labels.d.ts +38 -0
- package/dist/plot/treemap/labels.js +80 -0
- package/dist/plot/treemap/treemap.d.ts +28 -0
- package/dist/plot/treemap/treemap.js +102 -0
- package/dist/rdf/RdfPlot.svelte +37 -59
- package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
- package/dist/rdf/calc-rdf.js +63 -52
- package/dist/rdf/index.d.ts +0 -1
- package/dist/sanitize.js +85 -27
- package/dist/scene/SceneCamera.svelte +1 -1
- package/dist/scene/SceneCamera.svelte.d.ts +1 -1
- package/dist/scene/bind-renderer.svelte.js +14 -6
- package/dist/scene/index.d.ts +1 -2
- package/dist/scene/index.js +1 -1
- package/dist/scene/{props.js → props.svelte.js} +15 -1
- package/dist/settings.d.ts +19 -9
- package/dist/settings.js +229 -525
- package/dist/spectral/Bands.svelte +207 -221
- package/dist/spectral/BandsAndDos.svelte +11 -9
- package/dist/spectral/BrillouinBandsDos.svelte +19 -42
- package/dist/spectral/Dos.svelte +92 -93
- package/dist/spectral/helpers.d.ts +3 -3
- package/dist/spectral/helpers.js +65 -140
- package/dist/state.svelte.js +5 -18
- package/dist/structure/Arrow.svelte +5 -18
- package/dist/structure/ArrowInstances.svelte +160 -0
- package/dist/structure/ArrowInstances.svelte.d.ts +16 -0
- package/dist/structure/AtomLegend.svelte +120 -85
- package/dist/structure/AtomLegend.svelte.d.ts +2 -3
- package/dist/structure/Bond.svelte +31 -23
- package/dist/structure/CanvasTooltip.svelte +15 -3
- package/dist/structure/CellSelect.svelte +45 -35
- package/dist/structure/Cylinder.svelte +24 -40
- package/dist/structure/InstancedAtoms.svelte +114 -0
- package/dist/structure/InstancedAtoms.svelte.d.ts +15 -0
- package/dist/structure/Lattice.svelte +85 -149
- package/dist/structure/SiteLabels.svelte +122 -0
- package/dist/structure/SiteLabels.svelte.d.ts +18 -0
- package/dist/structure/Structure.svelte +636 -504
- package/dist/structure/Structure.svelte.d.ts +4 -2
- package/dist/structure/StructureCarousel.svelte +637 -0
- package/dist/structure/StructureCarousel.svelte.d.ts +19 -0
- package/dist/structure/StructureControls.svelte +165 -425
- package/dist/structure/StructureControls.svelte.d.ts +1 -1
- package/dist/structure/StructureExportPane.svelte +17 -21
- package/dist/structure/StructureInfoPane.svelte +128 -133
- package/dist/structure/StructureInfoPane.svelte.d.ts +1 -1
- package/dist/structure/StructureScene.svelte +535 -578
- package/dist/structure/StructureScene.svelte.d.ts +6 -2
- package/dist/structure/StructureViewport.svelte +451 -0
- package/dist/structure/StructureViewport.svelte.d.ts +65 -0
- package/dist/structure/atom-label-placement.d.ts +16 -12
- package/dist/structure/atom-label-placement.js +70 -37
- package/dist/structure/atom-properties.d.ts +2 -0
- package/dist/structure/atom-properties.js +105 -38
- package/dist/structure/bond-order-perception.js +3 -8
- package/dist/structure/bonding.d.ts +9 -3
- package/dist/structure/bonding.js +95 -46
- package/dist/structure/edit-history.d.ts +6 -0
- package/dist/structure/edit-history.js +27 -0
- package/dist/structure/export.d.ts +1 -0
- package/dist/structure/export.js +89 -105
- package/dist/structure/format-detect.d.ts +1 -0
- package/dist/structure/format-detect.js +20 -0
- package/dist/structure/geometry.d.ts +9 -0
- package/dist/structure/geometry.js +27 -0
- package/dist/structure/index.d.ts +16 -1
- package/dist/structure/index.js +15 -10
- package/dist/structure/measure.d.ts +1 -1
- package/dist/structure/measure.js +6 -12
- package/dist/structure/parse.d.ts +2 -1
- package/dist/structure/parse.js +272 -313
- package/dist/structure/partial-occupancy.js +10 -5
- package/dist/structure/pbc.js +85 -107
- package/dist/structure/polyhedra.d.ts +0 -1
- package/dist/structure/polyhedra.js +7 -21
- package/dist/structure/prop-groups.d.ts +85 -0
- package/dist/structure/prop-groups.js +18 -0
- package/dist/structure/supercell.d.ts +1 -2
- package/dist/structure/supercell.js +7 -13
- package/dist/symmetry/SymmetryElements.svelte +11 -15
- package/dist/symmetry/SymmetryStats.svelte +55 -71
- package/dist/symmetry/WyckoffTable.svelte +10 -28
- package/dist/symmetry/cell-transform.js +6 -14
- package/dist/symmetry/index.d.ts +1 -0
- package/dist/symmetry/index.js +13 -10
- package/dist/symmetry/spacegroups.js +3 -2
- package/dist/table/HeatmapTable.svelte +1142 -447
- package/dist/table/HeatmapTable.svelte.d.ts +12 -2
- package/dist/table/ToggleMenu.svelte +168 -53
- package/dist/table/ToggleMenu.svelte.d.ts +1 -1
- package/dist/table/index.d.ts +21 -6
- package/dist/table/index.js +34 -21
- package/dist/theme/ThemeControl.svelte +16 -8
- package/dist/theme/embedded.d.ts +18 -0
- package/dist/theme/embedded.js +206 -0
- package/dist/theme/index.d.ts +5 -5
- package/dist/theme/index.js +5 -12
- package/dist/theme/themes.mjs +44 -66
- package/dist/tooltip/KCoords.svelte +4 -1
- package/dist/tooltip/TooltipContent.svelte +3 -6
- package/dist/tooltip/TooltipContent.svelte.d.ts +1 -1
- package/dist/tooltip/index.d.ts +8 -1
- package/dist/trajectory/Trajectory.svelte +613 -386
- package/dist/trajectory/Trajectory.svelte.d.ts +1 -1
- package/dist/trajectory/TrajectoryError.svelte +12 -74
- package/dist/trajectory/TrajectoryExportPane.svelte +14 -32
- package/dist/trajectory/TrajectoryInfoPane.svelte +127 -164
- package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +2 -1
- package/dist/trajectory/extract.js +61 -49
- package/dist/trajectory/format-detect.d.ts +2 -0
- package/dist/trajectory/format-detect.js +17 -13
- package/dist/trajectory/frame-reader.d.ts +2 -0
- package/dist/trajectory/frame-reader.js +35 -32
- package/dist/trajectory/helpers.d.ts +2 -0
- package/dist/trajectory/helpers.js +34 -56
- package/dist/trajectory/index.d.ts +1 -0
- package/dist/trajectory/index.js +5 -8
- package/dist/trajectory/parse/ase.js +1 -1
- package/dist/trajectory/parse/h5-utils.d.ts +13 -0
- package/dist/trajectory/parse/h5-utils.js +109 -0
- package/dist/trajectory/parse/hdf5.d.ts +1 -1
- package/dist/trajectory/parse/hdf5.js +116 -106
- package/dist/trajectory/parse/index.d.ts +6 -6
- package/dist/trajectory/parse/index.js +25 -33
- package/dist/trajectory/parse/lammps.js +8 -12
- package/dist/trajectory/parse/vasp.js +10 -7
- package/dist/trajectory/parse/vaspout-electronic.d.ts +13 -0
- package/dist/trajectory/parse/vaspout-electronic.js +207 -0
- package/dist/trajectory/parse/vaspout-h5.d.ts +4 -0
- package/dist/trajectory/parse/vaspout-h5.js +281 -0
- package/dist/trajectory/parse/xyz.js +59 -9
- package/dist/trajectory/plotting.d.ts +8 -9
- package/dist/trajectory/plotting.js +113 -64
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +7 -0
- package/dist/xrd/XrdPlot.svelte +79 -118
- package/dist/xrd/broadening.js +2 -1
- package/dist/xrd/calc-xrd.js +24 -62
- package/dist/xrd/parse.d.ts +0 -1
- package/dist/xrd/parse.js +69 -95
- package/package.json +94 -34
- package/readme.md +6 -5
- package/dist/api/mp.d.ts +0 -6
- package/dist/api/mp.js +0 -22
- package/dist/plot/sunburst/SunburstControls.svelte +0 -200
- package/dist/scene/types.d.ts +0 -26
- package/dist/tooltip/types.d.ts +0 -8
- package/dist/trajectory/constants.d.ts +0 -6
- package/dist/trajectory/constants.js +0 -11
- /package/dist/{scene/types.js → file-viewer/host-protocol.js} +0 -0
- /package/dist/{tooltip/types.js → isosurface/geometry-worker-types.js} +0 -0
package/dist/structure/parse.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { FALLBACK_ELEMENTS, is_elem_symbol } from '../element';
|
|
3
|
-
import { strip_compression_extensions } from '../io';
|
|
1
|
+
import { XYZ_EXTXYZ_REGEX } from '../constants';
|
|
2
|
+
import { FALLBACK_ELEMENTS, is_elem_symbol } from '../element/helpers';
|
|
3
|
+
import { strip_compression_extensions } from '../io/decompress';
|
|
4
4
|
import * as math from '../math';
|
|
5
5
|
import { wrap_to_unit_cell } from './pbc';
|
|
6
6
|
import { make_site } from './site';
|
|
7
|
-
import { iter_xyz_frames } from '../trajectory/helpers';
|
|
8
|
-
import { normalize_scientific_notation, to_error } from '../utils';
|
|
7
|
+
import { is_xyz_atom_line, iter_xyz_frames } from '../trajectory/helpers';
|
|
8
|
+
import { normalize_scientific_notation, parse_leading_num, parse_num_token, to_error, } from '../utils';
|
|
9
9
|
import { load as yaml_load } from 'js-yaml';
|
|
10
|
+
export { is_structure_file } from './format-detect';
|
|
10
11
|
// === Parse error contract ===
|
|
11
12
|
// Individual format parsers (parse_poscar, parse_cif, parse_xyz, parse_phonopy_yaml,
|
|
12
13
|
// parse_optimade_json, ...) return `T | null` on failure and record failure reasons in a
|
|
@@ -36,27 +37,48 @@ const aggregate_parse_error = (filename) => {
|
|
|
36
37
|
return new Error(`Failed to parse structure${filename ? ` from '${filename}'` : ``}${detail}`);
|
|
37
38
|
};
|
|
38
39
|
const cif_coords_key = (coords) => `${coords[0].toFixed(6)},${coords[1].toFixed(6)},${coords[2].toFixed(6)}`;
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
// Bravais lattice centering translations (excluding the identity) keyed by the
|
|
41
|
+
// leading letter of a space-group Hermann-Mauguin symbol. R is the obverse
|
|
42
|
+
// hexagonal setting.
|
|
43
|
+
const CENTERING_VECTORS = {
|
|
44
|
+
P: [],
|
|
45
|
+
I: [[0.5, 0.5, 0.5]],
|
|
46
|
+
F: [
|
|
47
|
+
[0, 0.5, 0.5],
|
|
48
|
+
[0.5, 0, 0.5],
|
|
49
|
+
[0.5, 0.5, 0],
|
|
50
|
+
],
|
|
51
|
+
A: [[0, 0.5, 0.5]],
|
|
52
|
+
B: [[0.5, 0, 0.5]],
|
|
53
|
+
C: [[0.5, 0.5, 0]],
|
|
54
|
+
R: [
|
|
55
|
+
[2 / 3, 1 / 3, 1 / 3],
|
|
56
|
+
[1 / 3, 2 / 3, 2 / 3],
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
// Detect the centering letter from a CIF's space-group H-M symbol, if present.
|
|
60
|
+
const extract_cif_centering = (text) => {
|
|
61
|
+
for (const line of text.split(`\n`)) {
|
|
62
|
+
const match = /^_(?:symmetry_space_group_name_h-m|space_group_name_h-m(?:_alt)?)\s+(?<symbol>.+)/i.exec(line.trim());
|
|
63
|
+
const letter = match?.groups?.symbol.replaceAll(/['"]/g, ``).trim()[0]?.toUpperCase();
|
|
64
|
+
if (letter && letter in CENTERING_VECTORS)
|
|
65
|
+
return letter;
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
};
|
|
41
69
|
const vec3_from_values = (values, context) => {
|
|
42
70
|
if (values?.length !== 3) {
|
|
43
71
|
throw new Error(`Invalid ${context}: expected 3 coordinates, got ${values?.length ?? 0}`);
|
|
44
72
|
}
|
|
45
73
|
const coords = math.finite_vec3_from_values(values);
|
|
46
|
-
if (coords)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const value = values[idx];
|
|
50
|
-
if (typeof value !== `number` || !Number.isFinite(value)) {
|
|
51
|
-
throw new TypeError(`Invalid ${context}: coordinate ${idx} must be finite, got ${String(value)}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
throw new Error(`Invalid ${context}: expected 3 finite coordinates`);
|
|
74
|
+
if (!coords)
|
|
75
|
+
throw new Error(`Invalid ${context}: expected 3 finite coordinates, got [${values.map(String).join(`, `)}]`);
|
|
76
|
+
return coords;
|
|
55
77
|
};
|
|
56
78
|
// Parse a coordinate value that might be in various scientific notation formats
|
|
57
79
|
function parse_coordinate(str) {
|
|
58
80
|
const normalized = normalize_scientific_notation(str.trim());
|
|
59
|
-
const value =
|
|
81
|
+
const value = Number(normalized);
|
|
60
82
|
if (isNaN(value))
|
|
61
83
|
throw new Error(`Invalid coordinate value: ${str}`);
|
|
62
84
|
return value;
|
|
@@ -70,9 +92,9 @@ function parse_coordinate_line(line) {
|
|
|
70
92
|
const sanitized = line
|
|
71
93
|
.trim()
|
|
72
94
|
// Add space when '-' follows a digit and precedes a digit or dot
|
|
73
|
-
.replaceAll(/(
|
|
95
|
+
.replaceAll(/(?<digit>\d)-(?=[\d.])/g, `$1 -`)
|
|
74
96
|
// Revert accidental spaces after exponent markers
|
|
75
|
-
.replaceAll(/([eE])\s-\s/g, `$1-`);
|
|
97
|
+
.replaceAll(/(?<exp_marker>[eE])\s-\s/g, `$1-`);
|
|
76
98
|
tokens = sanitized.split(/\s+/);
|
|
77
99
|
}
|
|
78
100
|
if (tokens.length < 3)
|
|
@@ -114,14 +136,6 @@ function resolve_optimade_element(species_name, species_list, index) {
|
|
|
114
136
|
return { symbol: stripped, sym_idx: -1 };
|
|
115
137
|
return { symbol: validate_element_symbol(species_name, index), sym_idx: -1 };
|
|
116
138
|
}
|
|
117
|
-
const try_create_cart_to_frac = (lattice_matrix) => {
|
|
118
|
-
try {
|
|
119
|
-
return math.create_cart_to_frac(lattice_matrix);
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
139
|
const approximate_cart_to_frac = (xyz, axis_lengths) => [
|
|
126
140
|
Math.abs(axis_lengths[0]) > math.EPS ? xyz[0] / axis_lengths[0] : 0,
|
|
127
141
|
Math.abs(axis_lengths[1]) > math.EPS ? xyz[1] / axis_lengths[1] : 0,
|
|
@@ -136,9 +150,12 @@ const matrix3x3_from_rows = (rows, context) => [
|
|
|
136
150
|
// cart→frac converter that falls back to per-axis-length division for singular lattices.
|
|
137
151
|
// axis_lengths defaults to the row norms of the lattice matrix.
|
|
138
152
|
const cart_to_frac_with_fallback = (matrix, axis_lengths) => {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
153
|
+
try {
|
|
154
|
+
return { convert: math.create_cart_to_frac(matrix), exact: true };
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// fall through to the per-axis-length approximation below
|
|
158
|
+
}
|
|
142
159
|
const lengths = axis_lengths ?? [
|
|
143
160
|
Math.hypot(...matrix[0]),
|
|
144
161
|
Math.hypot(...matrix[1]),
|
|
@@ -193,38 +210,24 @@ export function parse_poscar(content) {
|
|
|
193
210
|
let atom_counts = [];
|
|
194
211
|
// Detect if this is VASP 5+ format (has element symbols)
|
|
195
212
|
// Try to parse the first token as a number - if it succeeds, it's VASP 4 format
|
|
196
|
-
const
|
|
197
|
-
const first_token_as_number = parseInt(first_token, 10);
|
|
198
|
-
const has_element_symbols = isNaN(first_token_as_number);
|
|
213
|
+
const has_element_symbols = isNaN(parse_leading_num(lines[line_index]));
|
|
199
214
|
if (has_element_symbols) {
|
|
200
|
-
// VASP 5+ format -
|
|
215
|
+
// VASP 5+ format - element symbols (possibly spanning multiple lines),
|
|
216
|
+
// followed by as many atom-count lines. Look ahead to find where numbers start.
|
|
201
217
|
let symbol_lines = 1;
|
|
202
|
-
// Look ahead to find where numbers start (atom counts)
|
|
203
218
|
for (let lookahead_idx = 1; lookahead_idx < 10; lookahead_idx++) {
|
|
204
219
|
if (line_index + lookahead_idx >= lines.length)
|
|
205
220
|
break;
|
|
206
|
-
|
|
207
|
-
const next_token_as_number = parseInt(next_line_first_token, 10);
|
|
208
|
-
if (!isNaN(next_token_as_number)) {
|
|
221
|
+
if (!isNaN(parse_leading_num(lines[line_index + lookahead_idx]))) {
|
|
209
222
|
symbol_lines = lookahead_idx;
|
|
210
223
|
break;
|
|
211
224
|
}
|
|
212
225
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
// Parse atom counts (may span multiple lines)
|
|
220
|
-
for (let count_line_idx = 0; count_line_idx < symbol_lines; count_line_idx++) {
|
|
221
|
-
if (line_index + symbol_lines + count_line_idx < lines.length) {
|
|
222
|
-
const counts = lines[line_index + symbol_lines + count_line_idx]
|
|
223
|
-
.trim()
|
|
224
|
-
.split(/\s+/)
|
|
225
|
-
.map(Number);
|
|
226
|
-
atom_counts.push(...counts);
|
|
227
|
-
}
|
|
226
|
+
for (let offset = 0; offset < symbol_lines; offset++) {
|
|
227
|
+
const symbol_tokens = lines[line_index + offset]?.trim().split(/\s+/) ?? [];
|
|
228
|
+
element_symbols.push(...symbol_tokens);
|
|
229
|
+
const count_tokens = lines[line_index + symbol_lines + offset]?.trim().split(/\s+/);
|
|
230
|
+
atom_counts.push(...(count_tokens?.map(Number) ?? []));
|
|
228
231
|
}
|
|
229
232
|
line_index += 2 * symbol_lines;
|
|
230
233
|
}
|
|
@@ -329,8 +332,9 @@ export function parse_xyz(content) {
|
|
|
329
332
|
diag_error(`XYZ frame too short`);
|
|
330
333
|
return null;
|
|
331
334
|
}
|
|
332
|
-
// Parse number of atoms (line 1)
|
|
333
|
-
|
|
335
|
+
// Parse number of atoms (line 1). Only the first token counts: Tinker-style
|
|
336
|
+
// XYZ files put a title after the count (e.g. `6 methane`)
|
|
337
|
+
const num_atoms = Math.trunc(parse_leading_num(lines[0]));
|
|
334
338
|
if (isNaN(num_atoms) || num_atoms <= 0) {
|
|
335
339
|
diag_error(`Invalid number of atoms in XYZ file`);
|
|
336
340
|
return null;
|
|
@@ -339,7 +343,7 @@ export function parse_xyz(content) {
|
|
|
339
343
|
const comment_line = lines[1];
|
|
340
344
|
let lattice;
|
|
341
345
|
// Check for extended XYZ lattice information in comment line
|
|
342
|
-
const lattice_match = /Lattice="([^"]+)"/.exec(comment_line);
|
|
346
|
+
const lattice_match = /Lattice="(?<lattice>[^"]+)"/.exec(comment_line);
|
|
343
347
|
if (lattice_match) {
|
|
344
348
|
const lattice_values = lattice_match[1].split(/\s+/).map(parse_coordinate);
|
|
345
349
|
if (lattice_values.length === 9) {
|
|
@@ -372,17 +376,14 @@ export function parse_xyz(content) {
|
|
|
372
376
|
return null;
|
|
373
377
|
}
|
|
374
378
|
const element = validate_element_symbol(parts[0], atom_idx);
|
|
375
|
-
|
|
379
|
+
let xyz = vec3_from_values(parts.slice(1, 4).map(parse_coordinate), `XYZ atom position ${atom_idx + 1}`);
|
|
376
380
|
// Calculate fractional coordinates if lattice is available
|
|
377
381
|
let abc = [0, 0, 0];
|
|
378
382
|
if (lattice && xyz_frac_to_cart && xyz_cart_to_frac) {
|
|
379
|
-
//
|
|
383
|
+
// Wrap fractional coordinates into [0, 1) and recompute xyz from them so
|
|
384
|
+
// rendered atoms stay inside the primary unit cell
|
|
380
385
|
abc = wrap_to_unit_cell(xyz_cart_to_frac(xyz));
|
|
381
|
-
|
|
382
|
-
const wrapped_xyz = xyz_frac_to_cart(abc);
|
|
383
|
-
xyz[0] = wrapped_xyz[0];
|
|
384
|
-
xyz[1] = wrapped_xyz[1];
|
|
385
|
-
xyz[2] = wrapped_xyz[2];
|
|
386
|
+
xyz = xyz_frac_to_cart(abc);
|
|
386
387
|
}
|
|
387
388
|
sites.push(make_site(element, abc, xyz, `${element}${atom_idx + 1}`));
|
|
388
389
|
}
|
|
@@ -399,114 +400,78 @@ export function parse_xyz(content) {
|
|
|
399
400
|
const parse_symmetry_expression = (expr_input) => {
|
|
400
401
|
const coefficients = [0, 0, 0];
|
|
401
402
|
let translation = 0;
|
|
402
|
-
//
|
|
403
|
+
// Strip whitespace, then split into signed terms: "x-y+1/3" → ["x", "-y", "+1/3"].
|
|
404
|
+
// Dangling operators (e.g. "x+") produce no token and are silently ignored.
|
|
403
405
|
const expr = expr_input.replaceAll(/\s+/g, ``);
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
for (let idx = 0; idx < expr.length; idx++) {
|
|
411
|
-
const char = expr[idx];
|
|
412
|
-
if ((char === `+` || char === `-`) && current_token.length > 0) {
|
|
413
|
-
tokens.push(current_token);
|
|
414
|
-
current_token = char;
|
|
415
|
-
}
|
|
416
|
-
else {
|
|
417
|
-
current_token += char;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
if (current_token)
|
|
421
|
-
tokens.push(current_token);
|
|
422
|
-
for (const token of tokens) {
|
|
423
|
-
// Check if this token is a variable term (x, y, or z with optional sign)
|
|
424
|
-
const var_match = /^([+-]?)([xyz])$/.exec(token);
|
|
425
|
-
if (var_match) {
|
|
426
|
-
const sign = var_match[1] === `-` ? -1 : 1;
|
|
427
|
-
const var_char = var_match[2];
|
|
428
|
-
const var_idx = var_char === `x` ? 0 : var_char === `y` ? 1 : 2;
|
|
406
|
+
for (const token of expr.match(/[+-]?[^+-]+/g) ?? []) {
|
|
407
|
+
const sign = token.startsWith(`-`) ? -1 : 1;
|
|
408
|
+
const term = token.replace(/^[+-]/, ``);
|
|
409
|
+
// Variable term (x, y, or z)
|
|
410
|
+
const var_idx = [`x`, `y`, `z`].indexOf(term);
|
|
411
|
+
if (var_idx !== -1) {
|
|
429
412
|
coefficients[var_idx] += sign;
|
|
430
413
|
continue;
|
|
431
414
|
}
|
|
432
|
-
//
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
num_str = num_str.slice(1);
|
|
438
|
-
}
|
|
439
|
-
else if (num_str.startsWith(`-`)) {
|
|
440
|
-
sign = -1;
|
|
441
|
-
num_str = num_str.slice(1);
|
|
442
|
-
}
|
|
443
|
-
// Skip empty tokens (from dangling operators like "x+")
|
|
444
|
-
if (!num_str || num_str === `+` || num_str === `-`)
|
|
415
|
+
// Numeric term: integer, decimal, or fraction like "1/3"
|
|
416
|
+
const parts = term.split(`/`);
|
|
417
|
+
// skip malformed terms like "1/2/3"
|
|
418
|
+
if (parts.length > 2) {
|
|
419
|
+
diag_warn(`Skipping malformed symmetry term '${term}'`);
|
|
445
420
|
continue;
|
|
446
|
-
if (num_str.includes(`/`)) {
|
|
447
|
-
// Fraction
|
|
448
|
-
const parts = num_str.split(`/`);
|
|
449
|
-
if (parts.length === 2) {
|
|
450
|
-
const numerator = parseFloat(parts[0]);
|
|
451
|
-
const denominator = parseFloat(parts[1]);
|
|
452
|
-
if (!isNaN(numerator) && !isNaN(denominator) && denominator !== 0) {
|
|
453
|
-
translation += sign * (numerator / denominator);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
else {
|
|
458
|
-
// Integer or decimal
|
|
459
|
-
const val = parseFloat(num_str);
|
|
460
|
-
if (!isNaN(val)) {
|
|
461
|
-
translation += sign * val;
|
|
462
|
-
}
|
|
463
421
|
}
|
|
422
|
+
const [numerator, denominator = `1`] = parts;
|
|
423
|
+
const value = Number(numerator) / Number(denominator);
|
|
424
|
+
if (Number.isFinite(value))
|
|
425
|
+
translation += sign * value;
|
|
426
|
+
else
|
|
427
|
+
diag_warn(`Skipping non-finite symmetry term '${term}'`);
|
|
464
428
|
}
|
|
465
429
|
return { coefficients, translation };
|
|
466
430
|
};
|
|
467
|
-
// Apply symmetry operations
|
|
468
|
-
|
|
469
|
-
|
|
431
|
+
// Apply symmetry operations (and optional lattice-centering translations) to
|
|
432
|
+
// generate all equivalent positions. Deduplication uses 6 decimal places to
|
|
433
|
+
// absorb floating point error from compound ops like x-y, -x+y.
|
|
434
|
+
const apply_symmetry_ops = (atom, symmetry_ops, wrap_fractional_coords, centering = []) => {
|
|
435
|
+
if (symmetry_ops.length === 0 && centering.length === 0)
|
|
470
436
|
return [atom];
|
|
471
437
|
const equivalent_atoms = [];
|
|
472
438
|
const seen = new Set();
|
|
473
439
|
const wrap = (coords) => wrap_fractional_coords ? wrap_to_unit_cell(coords) : coords;
|
|
474
|
-
//
|
|
475
|
-
|
|
476
|
-
//
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
440
|
+
// Every generated position is also offset by each centering translation
|
|
441
|
+
const shifts = [[0, 0, 0], ...centering];
|
|
442
|
+
// Record a position plus its centering images, deduplicating on wrapped coords
|
|
443
|
+
const add_position = (coords) => {
|
|
444
|
+
for (const [dx, dy, dz] of shifts) {
|
|
445
|
+
const wrapped = wrap([coords[0] + dx, coords[1] + dy, coords[2] + dz]);
|
|
446
|
+
const key = cif_coords_key(wrapped);
|
|
447
|
+
if (seen.has(key))
|
|
448
|
+
continue;
|
|
449
|
+
seen.add(key);
|
|
450
|
+
const id = equivalent_atoms.length === 0 ? atom.id : `${atom.id}_${equivalent_atoms.length}`;
|
|
451
|
+
equivalent_atoms.push({ ...atom, coords: wrapped, id });
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
add_position(atom.coords); // base atom (+ centering images)
|
|
455
|
+
// ops arrive pre-normalized (quotes + whitespace already stripped, see normalized_ops)
|
|
480
456
|
for (const operation of symmetry_ops) {
|
|
481
|
-
const
|
|
482
|
-
const expr_str = operation_match ? operation_match[1] : operation.trim();
|
|
483
|
-
const parts = expr_str.split(`,`).map((part) => part.trim());
|
|
457
|
+
const parts = operation.split(`,`);
|
|
484
458
|
if (parts.length !== 3)
|
|
485
459
|
continue;
|
|
486
460
|
const new_coords = [0, 0, 0];
|
|
487
461
|
for (let dim = 0; dim < 3; dim++) {
|
|
488
462
|
const { coefficients, translation } = parse_symmetry_expression(parts[dim]);
|
|
489
|
-
//
|
|
490
|
-
new_coords[dim] =
|
|
491
|
-
coefficients[0] * atom.coords[0] +
|
|
492
|
-
coefficients[1] * atom.coords[1] +
|
|
493
|
-
coefficients[2] * atom.coords[2] +
|
|
494
|
-
translation;
|
|
463
|
+
// new_coord = coeff_x * x + coeff_y * y + coeff_z * z + translation
|
|
464
|
+
new_coords[dim] = math.dot(coefficients, atom.coords) + translation;
|
|
495
465
|
}
|
|
496
|
-
|
|
497
|
-
const wrapped = wrap(new_coords);
|
|
498
|
-
const cache_key = cif_coords_key(wrapped);
|
|
499
|
-
if (seen.has(cache_key))
|
|
500
|
-
continue;
|
|
501
|
-
seen.add(cache_key);
|
|
502
|
-
equivalent_atoms.push({
|
|
503
|
-
...atom,
|
|
504
|
-
coords: wrapped,
|
|
505
|
-
id: `${atom.id}_${equivalent_atoms.length}`,
|
|
506
|
-
});
|
|
466
|
+
add_position(new_coords);
|
|
507
467
|
}
|
|
508
468
|
return equivalent_atoms;
|
|
509
469
|
};
|
|
470
|
+
// Parse a CIF numeric token, stripping a trailing uncertainty like "1.234(5)"
|
|
471
|
+
const parse_cif_uncertain_number = (token) => {
|
|
472
|
+
const value = parse_num_token(token.split(`(`)[0]);
|
|
473
|
+
return isNaN(value) ? null : value;
|
|
474
|
+
};
|
|
510
475
|
const extract_cif_cell_parameters = (text, type, strict = true) => text
|
|
511
476
|
.split(`\n`)
|
|
512
477
|
.filter((line) => line.startsWith(`_${type}`))
|
|
@@ -519,11 +484,9 @@ const extract_cif_cell_parameters = (text, type, strict = true) => text
|
|
|
519
484
|
throw new Error(`Invalid CIF cell parameter line format: ${line}`);
|
|
520
485
|
return null;
|
|
521
486
|
}
|
|
522
|
-
const value =
|
|
523
|
-
if (
|
|
524
|
-
|
|
525
|
-
throw new Error(`Invalid CIF cell parameter in line: ${line}`);
|
|
526
|
-
return null; // Return null for invalid values in non-strict mode
|
|
487
|
+
const value = parse_cif_uncertain_number(tokens[1]);
|
|
488
|
+
if (value === null && strict) {
|
|
489
|
+
throw new Error(`Invalid CIF cell parameter in line: ${line}`);
|
|
527
490
|
}
|
|
528
491
|
return value;
|
|
529
492
|
})
|
|
@@ -551,6 +514,22 @@ const build_cif_atom_site_header_indices = (headers) => {
|
|
|
551
514
|
});
|
|
552
515
|
return indices;
|
|
553
516
|
};
|
|
517
|
+
// Which coordinate triple a CIF atom-site loop provides (fractional preferred), or null
|
|
518
|
+
const cif_coords_type = (indices) => {
|
|
519
|
+
if (indices.x !== undefined && indices.y !== undefined && indices.z !== undefined) {
|
|
520
|
+
return `fract`;
|
|
521
|
+
}
|
|
522
|
+
if (indices.cart_x !== undefined &&
|
|
523
|
+
indices.cart_y !== undefined &&
|
|
524
|
+
indices.cart_z !== undefined) {
|
|
525
|
+
return `cart`;
|
|
526
|
+
}
|
|
527
|
+
return null;
|
|
528
|
+
};
|
|
529
|
+
// The 3 column indices for the requested coordinate type
|
|
530
|
+
const cif_coord_indices = (indices, coords_type) => coords_type === `fract`
|
|
531
|
+
? [indices.x, indices.y, indices.z]
|
|
532
|
+
: [indices.cart_x, indices.cart_y, indices.cart_z];
|
|
554
533
|
// Walk CIF loop_ blocks: yields each loop's header tags plus the index of its first data line
|
|
555
534
|
function* iter_cif_loops(lines) {
|
|
556
535
|
for (let idx = 0; idx < lines.length; idx++) {
|
|
@@ -571,28 +550,24 @@ const split_cif_tokens = (line) => (line.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)
|
|
|
571
550
|
// Parse atom data from CIF with robust error handling
|
|
572
551
|
const parse_cif_atom_data = (raw_data, indices, coords_type) => {
|
|
573
552
|
const { label = 0, symbol = -1, occupancy = -1 } = indices;
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
: [indices.cart_x, indices.cart_y, indices.cart_z];
|
|
577
|
-
if (coord_indices.some((idx) => idx === undefined)) {
|
|
578
|
-
throw new Error(`Missing coordinate indices`);
|
|
579
|
-
}
|
|
553
|
+
// cif_coords_type already guaranteed all three indices for coords_type exist
|
|
554
|
+
const coord_indices = cif_coord_indices(indices, coords_type);
|
|
580
555
|
const coords_triplet = vec3_from_values(coord_indices.map((idx) => {
|
|
581
|
-
if (idx === undefined)
|
|
582
|
-
throw new Error(`Invalid coordinate index`);
|
|
583
556
|
const coord_str = raw_data[idx];
|
|
584
557
|
if (!coord_str)
|
|
585
558
|
throw new Error(`Missing coordinate at index ${idx}`);
|
|
586
|
-
const coord =
|
|
587
|
-
if (
|
|
559
|
+
const coord = parse_cif_uncertain_number(coord_str);
|
|
560
|
+
if (coord === null)
|
|
588
561
|
throw new Error(`Invalid coordinate: ${coord_str}`);
|
|
589
562
|
return coord;
|
|
590
563
|
}), `CIF atom coordinates`);
|
|
591
|
-
const
|
|
592
|
-
?
|
|
593
|
-
:
|
|
594
|
-
|
|
595
|
-
const
|
|
564
|
+
const raw_occu = occupancy >= 0 && raw_data[occupancy]
|
|
565
|
+
? parse_cif_uncertain_number(raw_data[occupancy])
|
|
566
|
+
: null;
|
|
567
|
+
// Explicit 0 is kept; missing / `.` / `?` (null) default to fully occupied.
|
|
568
|
+
const occu = raw_occu ?? 1.0;
|
|
569
|
+
const from_symbol = symbol >= 0 ? /^(?<element>[A-Z][a-z]*)/.exec(raw_data[symbol])?.[1] : undefined;
|
|
570
|
+
const element_symbol = from_symbol ?? raw_data[label]?.match(/(?:[A-Z][a-z]*)/g)?.[0];
|
|
596
571
|
if (!element_symbol) {
|
|
597
572
|
throw new Error(`Could not extract element symbol from: ${raw_data.join(` `)}`);
|
|
598
573
|
}
|
|
@@ -639,13 +614,7 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
639
614
|
continue;
|
|
640
615
|
// Check if this loop contains coordinate headers
|
|
641
616
|
const indices_preview = build_cif_atom_site_header_indices(headers);
|
|
642
|
-
|
|
643
|
-
indices_preview.y !== undefined &&
|
|
644
|
-
indices_preview.z !== undefined) ||
|
|
645
|
-
(indices_preview.cart_x !== undefined &&
|
|
646
|
-
indices_preview.cart_y !== undefined &&
|
|
647
|
-
indices_preview.cart_z !== undefined);
|
|
648
|
-
if (!has_coords)
|
|
617
|
+
if (cif_coords_type(indices_preview) === null)
|
|
649
618
|
continue;
|
|
650
619
|
// This is the desired atom-site loop with coordinates: collect data lines
|
|
651
620
|
atom_headers = headers;
|
|
@@ -679,23 +648,13 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
679
648
|
// Parse atom data with error handling
|
|
680
649
|
const header_indices = build_cif_atom_site_header_indices(atom_headers);
|
|
681
650
|
// Determine available coordinate type
|
|
682
|
-
const coords_type = header_indices
|
|
683
|
-
header_indices.y !== undefined &&
|
|
684
|
-
header_indices.z !== undefined
|
|
685
|
-
? `fract`
|
|
686
|
-
: header_indices.cart_x !== undefined &&
|
|
687
|
-
header_indices.cart_y !== undefined &&
|
|
688
|
-
header_indices.cart_z !== undefined
|
|
689
|
-
? `cart`
|
|
690
|
-
: null;
|
|
651
|
+
const coords_type = cif_coords_type(header_indices);
|
|
691
652
|
if (!coords_type) {
|
|
692
653
|
diag_error(`CIF atom site loop missing coordinates (fract or Cartn)`);
|
|
693
654
|
return null;
|
|
694
655
|
}
|
|
695
656
|
// Collect required coordinate indices
|
|
696
|
-
const required_indices = coords_type
|
|
697
|
-
? [header_indices.x, header_indices.y, header_indices.z]
|
|
698
|
-
: [header_indices.cart_x, header_indices.cart_y, header_indices.cart_z];
|
|
657
|
+
const required_indices = cif_coord_indices(header_indices, coords_type);
|
|
699
658
|
const atoms = atom_data_lines
|
|
700
659
|
.map(split_cif_tokens)
|
|
701
660
|
.filter((tokens) => {
|
|
@@ -734,15 +693,9 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
734
693
|
const cart_to_frac = cart_to_frac_with_fallback(lattice_matrix, [a, b, c]).convert;
|
|
735
694
|
// Create sites with coordinate conversion and symmetry operations
|
|
736
695
|
const wrap_vec3 = (vec) => wrap_fractional_coords ? wrap_to_unit_cell(vec) : vec;
|
|
737
|
-
//
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
const normalized_ops = symmetry_ops
|
|
741
|
-
.map((op) => /['"]([^'"]+)['"]/.exec(op)?.[1] ?? op.trim())
|
|
742
|
-
.map((op) => op.replaceAll(/\s+/g, ``));
|
|
743
|
-
// Rely on symmetry operations list for all centering/translations to avoid double-counting
|
|
744
|
-
// TODO: Support conventional cells with centering by discovering centering from space group metadata
|
|
745
|
-
// when present (e.g. P, I, F, C, R centering types)
|
|
696
|
+
// Strip surrounding quotes and all whitespace (preserving duplicates; positions
|
|
697
|
+
// are deduplicated later). Leaves ops as bare `x,y,z`-style expressions.
|
|
698
|
+
const normalized_ops = symmetry_ops.map((op) => (/['"](?<expr>[^'"]+)['"]/.exec(op)?.groups?.expr ?? op).replaceAll(/\s+/g, ``));
|
|
746
699
|
// Inspect optional _atom_type_number_in_cell loop to see if atom sites are already expanded
|
|
747
700
|
const atom_type_counts = {};
|
|
748
701
|
for (const { headers, data_start } of iter_cif_loops(lines)) {
|
|
@@ -760,11 +713,15 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
760
713
|
const toks = split_cif_tokens(line);
|
|
761
714
|
if (toks.length > Math.max(sym_idx, num_idx)) {
|
|
762
715
|
// Normalize type symbol to bare element (e.g. 'Sn2+' -> 'Sn')
|
|
763
|
-
const match = /^([A-Z][a-z]*)/.exec(toks[sym_idx]);
|
|
716
|
+
const match = /^(?<element>[A-Z][a-z]*)/.exec(toks[sym_idx]);
|
|
764
717
|
const sym = match ? match[1] : toks[sym_idx];
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
718
|
+
// Strip standard-uncertainty parentheses (`8(0)` -> `8`) like other CIF
|
|
719
|
+
// readers; empty prefixes like `(8)` parse as NaN and get skipped
|
|
720
|
+
const num = Math.trunc(parse_num_token(toks[num_idx].split(`(`)[0]));
|
|
721
|
+
// sum rows that normalize to the same element (e.g. Fe2+ and Fe3+ → Fe)
|
|
722
|
+
if (sym && !Number.isNaN(num)) {
|
|
723
|
+
atom_type_counts[sym] = (atom_type_counts[sym] ?? 0) + num;
|
|
724
|
+
}
|
|
768
725
|
}
|
|
769
726
|
}
|
|
770
727
|
break;
|
|
@@ -777,38 +734,61 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
777
734
|
const already_enumerated = has_expected_counts &&
|
|
778
735
|
Object.entries(atom_type_counts).every(([el, exp]) => (observed_counts[el] || 0) >= exp);
|
|
779
736
|
const ops_to_use = already_enumerated ? [] : normalized_ops;
|
|
780
|
-
//
|
|
781
|
-
//
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
const
|
|
796
|
-
const
|
|
797
|
-
|
|
737
|
+
// Candidate lattice-centering translations from the space-group symbol (R
|
|
738
|
+
// only valid in the hexagonal setting, α≈β≈90°, γ≈120°). Whether to actually
|
|
739
|
+
// apply them is decided below by reconciling against _atom_type_number_in_cell.
|
|
740
|
+
const centering_letter = extract_cif_centering(text);
|
|
741
|
+
const is_hexagonal_setting = Math.abs(alpha - 90) <= 1 && Math.abs(beta - 90) <= 1 && Math.abs(gamma - 120) <= 1;
|
|
742
|
+
const centering = centering_letter && (centering_letter !== `R` || is_hexagonal_setting)
|
|
743
|
+
? CENTERING_VECTORS[centering_letter]
|
|
744
|
+
: [];
|
|
745
|
+
// Build all sites by expanding each atom via the symmetry ops (+ optional
|
|
746
|
+
// centering). Deduplicate globally on element + coordinates + label (6 dp to
|
|
747
|
+
// absorb floating point error from compound ops).
|
|
748
|
+
const build_sites = (extra_centering) => {
|
|
749
|
+
const sites = [];
|
|
750
|
+
const seen_site_keys = new Set();
|
|
751
|
+
for (const atom of atoms) {
|
|
752
|
+
const element = validate_element_symbol(atom.element, sites.length);
|
|
753
|
+
const coords = atom.coords_type === `fract`
|
|
754
|
+
? wrap_vec3(atom.coords)
|
|
755
|
+
: wrap_vec3(cart_to_frac([atom.coords[0], atom.coords[1], atom.coords[2]]));
|
|
756
|
+
const fractional_atom = { ...atom, coords, coords_type: `fract` };
|
|
757
|
+
const equiv_atoms = apply_symmetry_ops(fractional_atom, ops_to_use, wrap_fractional_coords, extra_centering);
|
|
758
|
+
for (const equiv_atom of equiv_atoms) {
|
|
759
|
+
const abc = wrap_vec3(equiv_atom.coords);
|
|
760
|
+
const key = `${element}|${equiv_atom.id}|${cif_coords_key(abc)}`;
|
|
761
|
+
if (seen_site_keys.has(key))
|
|
762
|
+
continue;
|
|
763
|
+
seen_site_keys.add(key);
|
|
764
|
+
sites.push(make_site(element, abc, frac_to_cart(abc), equiv_atom.id, {}, equiv_atom.occupancy));
|
|
765
|
+
}
|
|
798
766
|
}
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
767
|
+
return sites;
|
|
768
|
+
};
|
|
769
|
+
// Expand with point-group ops first. If the space group is centered and the
|
|
770
|
+
// result falls short of _atom_type_number_in_cell, retry with centering and
|
|
771
|
+
// adopt it only when it reconciles the expected total exactly — this fixes
|
|
772
|
+
// CIFs listing point-only ops for the asymmetric unit while avoiding
|
|
773
|
+
// double-counting CIFs whose atom list already embeds centering (e.g. C2/c
|
|
774
|
+
// COD 7008984, where listed ops + atoms already total the cell contents).
|
|
775
|
+
let sites = build_sites([]);
|
|
776
|
+
const expected_total = Object.values(atom_type_counts).reduce((sum, num) => sum + num, 0);
|
|
777
|
+
if (centering.length > 0 && expected_total > sites.length) {
|
|
778
|
+
const centered_sites = build_sites(centering);
|
|
779
|
+
// Adopt centering only when per-element counts reconcile exactly. Checking
|
|
780
|
+
// the total alone is insufficient: it can coincide while individual element
|
|
781
|
+
// counts are wrong (e.g. expected Fe 1 / O 3 but centering yields Fe 2 / O 2).
|
|
782
|
+
const counts = {};
|
|
783
|
+
for (const site of centered_sites) {
|
|
784
|
+
const element = site.species[0].element;
|
|
785
|
+
counts[element] = (counts[element] ?? 0) + 1;
|
|
809
786
|
}
|
|
787
|
+
const reconciles = centered_sites.length === expected_total &&
|
|
788
|
+
Object.entries(atom_type_counts).every(([element, exp]) => counts[element] === exp);
|
|
789
|
+
if (reconciles)
|
|
790
|
+
sites = centered_sites;
|
|
810
791
|
}
|
|
811
|
-
const sites = all_sites;
|
|
812
792
|
return { sites, lattice: { matrix: lattice_matrix, ...lattice_params } };
|
|
813
793
|
}
|
|
814
794
|
catch (error) {
|
|
@@ -865,7 +845,7 @@ export function parse_phonopy_yaml(content, cell_type) {
|
|
|
865
845
|
}
|
|
866
846
|
// Check if we're still in the phonon_displacements section
|
|
867
847
|
if (skip_displacements) {
|
|
868
|
-
if (/^[a-zA-Z_]/.
|
|
848
|
+
if (/^[a-zA-Z_]/.test(line)) {
|
|
869
849
|
// New top-level key, stop skipping
|
|
870
850
|
skip_displacements = false;
|
|
871
851
|
}
|
|
@@ -888,17 +868,16 @@ export function parse_phonopy_yaml(content, cell_type) {
|
|
|
888
868
|
diag_error(`Requested cell type '${cell_type}' not found in phonopy YAML`);
|
|
889
869
|
return null;
|
|
890
870
|
}
|
|
891
|
-
// Auto mode: return
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
get_phonopy_cell(data,
|
|
900
|
-
|
|
901
|
-
get_phonopy_cell(data, `primitive_cell`);
|
|
871
|
+
// Auto mode: return first available cell, most detailed first
|
|
872
|
+
const auto_cell = [
|
|
873
|
+
`supercell`,
|
|
874
|
+
`phonon_supercell`,
|
|
875
|
+
`unit_cell`,
|
|
876
|
+
`phonon_primitive_cell`,
|
|
877
|
+
`primitive_cell`,
|
|
878
|
+
]
|
|
879
|
+
.map((kind) => get_phonopy_cell(data, kind))
|
|
880
|
+
.find(Boolean);
|
|
902
881
|
if (auto_cell)
|
|
903
882
|
return convert_phonopy_cell(auto_cell);
|
|
904
883
|
diag_error(`No valid cells found in phonopy YAML`);
|
|
@@ -956,6 +935,31 @@ export function is_parsed_structure(obj) {
|
|
|
956
935
|
const has_coords = Array.isArray(abc) || Array.isArray(xyz);
|
|
957
936
|
return has_species && has_coords;
|
|
958
937
|
}
|
|
938
|
+
// Structure JSON serialized by pymatgen (default verbosity) stores only the lattice
|
|
939
|
+
// matrix + pbc; derive the missing scalar params (a/b/c/angles/volume) from the matrix
|
|
940
|
+
// so downstream consumers (camera auto-fit, density, export) never see NaN.
|
|
941
|
+
export function ensure_lattice_params(structure) {
|
|
942
|
+
const lattice = structure.lattice;
|
|
943
|
+
if (!lattice?.matrix)
|
|
944
|
+
return structure;
|
|
945
|
+
const params = [
|
|
946
|
+
lattice.a,
|
|
947
|
+
lattice.b,
|
|
948
|
+
lattice.c,
|
|
949
|
+
lattice.alpha,
|
|
950
|
+
lattice.beta,
|
|
951
|
+
lattice.gamma,
|
|
952
|
+
lattice.volume,
|
|
953
|
+
];
|
|
954
|
+
if (params.every(Number.isFinite))
|
|
955
|
+
return structure;
|
|
956
|
+
// The matrix is authoritative: recompute all params from it rather than
|
|
957
|
+
// trusting a partially-populated (or non-numeric) set of values.
|
|
958
|
+
return {
|
|
959
|
+
...structure,
|
|
960
|
+
lattice: { ...lattice, ...math.calc_lattice_params(lattice.matrix) },
|
|
961
|
+
};
|
|
962
|
+
}
|
|
959
963
|
// Normalize structure coordinates: wrap fractional coords to [0,1) and recompute Cartesian
|
|
960
964
|
// Only normalizes when lattice matrix is available to ensure abc/xyz stay consistent
|
|
961
965
|
export function normalize_fractional_coords(structure) {
|
|
@@ -989,7 +993,7 @@ const detect_json_structure = (content) => {
|
|
|
989
993
|
}
|
|
990
994
|
// Otherwise try parsing as pymatgen/nested structure JSON
|
|
991
995
|
const structure = find_structure_in_json(parsed);
|
|
992
|
-
return structure ? normalize_fractional_coords(structure) : null;
|
|
996
|
+
return structure ? ensure_lattice_params(normalize_fractional_coords(structure)) : null;
|
|
993
997
|
};
|
|
994
998
|
// Internal: auto-detect file format, returns null on failure after recording reasons (see parse error contract at top)
|
|
995
999
|
function parse_structure_file_impl(content, filename) {
|
|
@@ -1048,36 +1052,20 @@ function parse_structure_file_impl(content, filename) {
|
|
|
1048
1052
|
diag_error(`File too short to determine format`);
|
|
1049
1053
|
return null;
|
|
1050
1054
|
}
|
|
1051
|
-
// XYZ format detection: first line
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
const parts = lines[coord_line_idx].trim().split(/\s+/);
|
|
1060
|
-
// XYZ format: element symbol followed by 3 coordinates
|
|
1061
|
-
if (parts.length >= 4) {
|
|
1062
|
-
const first_token = parts[0];
|
|
1063
|
-
const coords = parts.slice(1, 4);
|
|
1064
|
-
// Check if first token looks like an element symbol (not a number)
|
|
1065
|
-
// and the next 3 tokens look like coordinates (numbers)
|
|
1066
|
-
const is_element_symbol = isNaN(parseInt(first_token, 10)) && first_token.length <= 3;
|
|
1067
|
-
const are_coordinates = coords.every((coord) => !isNaN(parseFloat(coord)));
|
|
1068
|
-
if (is_element_symbol && are_coordinates) {
|
|
1069
|
-
// First token is likely an element symbol, likely XYZ
|
|
1070
|
-
return parse_xyz(content);
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1055
|
+
// XYZ format detection: first line is a positive atom count, second line is a
|
|
1056
|
+
// comment, and the first coordinate line looks like "<element> <x> <y> <z>"
|
|
1057
|
+
const first_line_number = Math.trunc(parse_leading_num(lines[0]));
|
|
1058
|
+
if (!isNaN(first_line_number) &&
|
|
1059
|
+
first_line_number > 0 &&
|
|
1060
|
+
lines.length >= first_line_number + 2 &&
|
|
1061
|
+
is_xyz_atom_line(lines[2]?.trim().split(/\s+/)))
|
|
1062
|
+
return parse_xyz(content);
|
|
1076
1063
|
// POSCAR format detection: look for typical structure
|
|
1077
1064
|
if (lines.length >= 8) {
|
|
1078
|
-
|
|
1079
|
-
//
|
|
1080
|
-
|
|
1065
|
+
// Second line starts with a number (scale factor), likely POSCAR. First
|
|
1066
|
+
// token only: POSCAR allows three per-axis scale factors (or trailing
|
|
1067
|
+
// comments) on line 2, and blank lines must not pass
|
|
1068
|
+
if (!isNaN(parse_leading_num(lines[1])))
|
|
1081
1069
|
return parse_poscar(content);
|
|
1082
1070
|
}
|
|
1083
1071
|
// CIF format detection: look for CIF-specific keywords
|
|
@@ -1109,16 +1097,19 @@ export function parse_structure_file(content, filename) {
|
|
|
1109
1097
|
// Universal parser for JSON and structure files; throws an Error aggregating per-format failure reasons when nothing parses
|
|
1110
1098
|
export function parse_any_structure(content, filename) {
|
|
1111
1099
|
reset_parse_diagnostics();
|
|
1112
|
-
const finalize_structure = (
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1100
|
+
const finalize_structure = (parsed_structure) => {
|
|
1101
|
+
const structure = ensure_lattice_params(parsed_structure);
|
|
1102
|
+
return {
|
|
1103
|
+
sites: structure.sites,
|
|
1104
|
+
charge: 0,
|
|
1105
|
+
...(structure.properties && {
|
|
1106
|
+
properties: structuredClone(structure.properties),
|
|
1107
|
+
}),
|
|
1108
|
+
...(structure.lattice && {
|
|
1109
|
+
lattice: { ...structure.lattice, pbc: [true, true, true] },
|
|
1110
|
+
}),
|
|
1111
|
+
};
|
|
1112
|
+
};
|
|
1122
1113
|
// Fast path: content is already a serialized structure object
|
|
1123
1114
|
try {
|
|
1124
1115
|
const parsed = JSON.parse(content);
|
|
@@ -1271,10 +1262,7 @@ const unwrap_data = (value) => value && typeof value === `object` && `data` in v
|
|
|
1271
1262
|
function is_optimade_structure_object(value) {
|
|
1272
1263
|
if (!value || typeof value !== `object`)
|
|
1273
1264
|
return false;
|
|
1274
|
-
const
|
|
1275
|
-
const type = obj.type;
|
|
1276
|
-
const id = obj.id;
|
|
1277
|
-
const attributes = obj.attributes;
|
|
1265
|
+
const { type, id, attributes } = value;
|
|
1278
1266
|
return (type === `structures` &&
|
|
1279
1267
|
typeof id === `string` &&
|
|
1280
1268
|
typeof attributes === `object` &&
|
|
@@ -1306,35 +1294,6 @@ export function optimade_to_crystal(optimade_structure) {
|
|
|
1306
1294
|
return null;
|
|
1307
1295
|
}
|
|
1308
1296
|
}
|
|
1309
|
-
// Check if filename indicates a structure file
|
|
1310
|
-
export function is_structure_file(filename) {
|
|
1311
|
-
const name = filename.toLowerCase();
|
|
1312
|
-
// Trajectory-only formats (can't be structures)
|
|
1313
|
-
if (/\.(traj|xtc|h5|hdf5)$/i.test(name) || /xdatcar/i.test(name))
|
|
1314
|
-
return false;
|
|
1315
|
-
// Always structure formats
|
|
1316
|
-
if (STRUCTURE_EXTENSIONS_REGEX.test(name))
|
|
1317
|
-
return true;
|
|
1318
|
-
if (VASP_FILES_REGEX.test(name))
|
|
1319
|
-
return true;
|
|
1320
|
-
// .xyz/.extxyz files: structure unless they have trajectory keywords
|
|
1321
|
-
if (/\.(xyz|extxyz)$/i.test(name))
|
|
1322
|
-
return !TRAJ_KEYWORDS_REGEX.test(name);
|
|
1323
|
-
// Keyword-based detection for YAML/XML
|
|
1324
|
-
if (/\.(yaml|yml|xml)$/i.test(name) && STRUCT_KEYWORDS_REGEX.test(name))
|
|
1325
|
-
return true;
|
|
1326
|
-
// More restrictive keyword detection for JSON files
|
|
1327
|
-
if (/\.json$/i.test(name) &&
|
|
1328
|
-
STRUCT_KEYWORDS_STRICT_REGEX.test(name) &&
|
|
1329
|
-
!TRAJ_KEYWORDS_REGEX.test(name) &&
|
|
1330
|
-
!CONFIG_DIRS_REGEX.test(name))
|
|
1331
|
-
return true;
|
|
1332
|
-
// Compressed files - check base filename recursively
|
|
1333
|
-
if (COMPRESSION_EXTENSIONS_REGEX.test(name)) {
|
|
1334
|
-
return is_structure_file(name.replace(COMPRESSION_EXTENSIONS_REGEX, ``));
|
|
1335
|
-
}
|
|
1336
|
-
return false;
|
|
1337
|
-
}
|
|
1338
1297
|
export const detect_structure_type = (filename, content) => {
|
|
1339
1298
|
// Normalize compressed suffixes (gz, gzip, zip, xz, bz2) for detection parity
|
|
1340
1299
|
const name_to_check = strip_compression_extensions(filename);
|