matterviz 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/EmptyState.svelte +10 -4
- package/dist/FilePicker.svelte +12 -14
- package/dist/Icon.svelte +7 -4
- package/dist/MillerIndexInput.svelte +2 -2
- package/dist/app.css +53 -0
- package/dist/brillouin/BrillouinZone.svelte +75 -85
- 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 +26 -39
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
- package/dist/chempot-diagram/ChemPotDiagram.svelte +33 -38
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +62 -74
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +1 -1
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +511 -547
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +1 -1
- package/dist/chempot-diagram/async-compute.svelte.js +1 -1
- package/dist/chempot-diagram/controls-state.svelte.js +1 -1
- package/dist/colors/index.d.ts +0 -4
- package/dist/colors/index.js +19 -20
- package/dist/composition/BarChart.svelte +37 -31
- package/dist/composition/BubbleChart.svelte +17 -17
- package/dist/composition/Composition.svelte +7 -11
- package/dist/composition/Formula.svelte +18 -25
- package/dist/composition/FormulaFilter.svelte +92 -95
- package/dist/composition/PieChart.svelte +55 -41
- package/dist/composition/format.js +1 -1
- 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 +11 -10
- package/dist/constants.js +11 -9
- 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 -92
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +109 -140
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +75 -94
- 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 +17 -21
- 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/helpers.d.ts +8 -10
- package/dist/convex-hull/helpers.js +36 -36
- package/dist/convex-hull/hull-state.svelte.d.ts +1 -1
- package/dist/convex-hull/thermodynamics.js +18 -17
- package/dist/coordination/CoordinationBarPlot.svelte +38 -33
- 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 -8
- 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 +24 -29
- package/dist/element/Nucleus.svelte +4 -2
- package/dist/element/data.d.ts +1 -2
- package/dist/element/helpers.d.ts +4 -0
- package/dist/element/helpers.js +18 -0
- package/dist/element/index.d.ts +1 -4
- package/dist/element/index.js +3 -18
- 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 +14 -22
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +5 -7
- package/dist/fermi-surface/FermiSurfaceControls.svelte +6 -20
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +37 -64
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +9 -6
- package/dist/fermi-surface/compute.js +3 -3
- package/dist/fermi-surface/parse.js +22 -24
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +109 -148
- 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/io/ExportPane.svelte +10 -6
- package/dist/io/decompress.d.ts +2 -1
- package/dist/io/decompress.js +22 -35
- package/dist/io/export.d.ts +4 -0
- package/dist/io/export.js +5 -1
- package/dist/io/fetch.d.ts +0 -3
- package/dist/io/fetch.js +0 -11
- 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.js +13 -25
- package/dist/isosurface/Isosurface.svelte +17 -23
- package/dist/isosurface/IsosurfaceControls.svelte +37 -42
- 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/labels.d.ts +6 -2
- package/dist/labels.js +43 -13
- package/dist/layout/FullscreenButton.svelte +2 -1
- package/dist/layout/FullscreenToggle.svelte +10 -4
- package/dist/layout/InfoCard.svelte +7 -18
- 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 +21 -17
- package/dist/layout/SubpageGrid.svelte +3 -1
- package/dist/layout/ViewerChrome.svelte +9 -10
- 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 +8 -11
- package/dist/layout/json-tree/JsonTree.svelte +53 -77
- package/dist/layout/json-tree/JsonValue.svelte +7 -15
- package/dist/layout/json-tree/utils.js +14 -23
- package/dist/math.d.ts +2 -0
- package/dist/math.js +19 -0
- package/dist/overlays/ContextMenu.svelte +4 -2
- package/dist/overlays/CopyButton.svelte +11 -5
- package/dist/overlays/DragControlTab.svelte +6 -1
- package/dist/overlays/DraggablePane.svelte +81 -26
- package/dist/overlays/DraggablePane.svelte.d.ts +1 -0
- package/dist/overlays/GlassChip.svelte +30 -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 +11 -63
- package/dist/periodic-table/TableInset.svelte +9 -4
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +50 -71
- 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 +3 -3
- 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 +21 -15
- package/dist/phase-diagram/utils.js +2 -3
- package/dist/plot/bar/BarPlot.svelte +430 -391
- 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 +29 -36
- package/dist/plot/bar/data.d.ts +5 -4
- package/dist/plot/bar/data.js +2 -1
- package/dist/plot/box/BoxPlot.svelte +301 -209
- 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.js +1 -1
- 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.js +9 -5
- 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 +3 -1
- package/dist/plot/core/layout.js +22 -13
- package/dist/plot/core/marginals.d.ts +170 -0
- package/dist/plot/core/marginals.js +436 -0
- 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/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 +51 -0
- package/dist/plot/core/utils/hierarchy-chart.js +179 -0
- package/dist/plot/core/utils/hierarchy-labels.d.ts +20 -0
- package/dist/plot/core/utils/hierarchy-labels.js +99 -0
- package/dist/plot/core/utils/label-placement.js +2 -2
- package/dist/plot/histogram/Histogram.svelte +304 -325
- 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/scatter/BinnedScatterPlot.svelte +263 -109
- package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +7 -0
- package/dist/plot/scatter/ElementScatter.svelte +4 -1
- package/dist/plot/scatter/ScatterPlot.svelte +477 -385
- 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/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 +251 -329
- 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 +907 -0
- package/dist/plot/treemap/Treemap.svelte.d.ts +84 -0
- package/dist/plot/treemap/index.d.ts +3 -0
- package/dist/plot/treemap/index.js +4 -0
- package/dist/plot/treemap/treemap.d.ts +26 -0
- package/dist/plot/treemap/treemap.js +99 -0
- package/dist/rdf/RdfPlot.svelte +22 -26
- package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
- package/dist/rdf/calc-rdf.js +4 -8
- package/dist/sanitize.js +1 -1
- package/dist/scene/bind-renderer.svelte.js +14 -6
- package/dist/settings.d.ts +19 -9
- package/dist/settings.js +77 -50
- package/dist/spectral/Bands.svelte +223 -230
- package/dist/spectral/BandsAndDos.svelte +11 -9
- package/dist/spectral/BrillouinBandsDos.svelte +19 -27
- package/dist/spectral/Dos.svelte +70 -73
- package/dist/spectral/helpers.d.ts +3 -3
- package/dist/spectral/helpers.js +25 -35
- 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 +115 -75
- package/dist/structure/AtomLegend.svelte.d.ts +1 -1
- package/dist/structure/Bond.svelte +31 -23
- package/dist/structure/CanvasTooltip.svelte +15 -3
- package/dist/structure/CellSelect.svelte +11 -26
- 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 +473 -419
- package/dist/structure/Structure.svelte.d.ts +4 -2
- package/dist/structure/StructureCarousel.svelte +630 -0
- package/dist/structure/StructureCarousel.svelte.d.ts +19 -0
- package/dist/structure/StructureControls.svelte +157 -399
- package/dist/structure/StructureControls.svelte.d.ts +1 -1
- package/dist/structure/StructureExportPane.svelte +17 -21
- package/dist/structure/StructureInfoPane.svelte +72 -66
- package/dist/structure/StructureInfoPane.svelte.d.ts +1 -1
- package/dist/structure/StructureScene.svelte +467 -476
- package/dist/structure/StructureScene.svelte.d.ts +3 -1
- package/dist/structure/StructureViewport.svelte +386 -0
- package/dist/structure/StructureViewport.svelte.d.ts +63 -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 +104 -38
- package/dist/structure/bonding.d.ts +10 -3
- package/dist/structure/bonding.js +84 -28
- 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 +39 -31
- package/dist/structure/format-detect.d.ts +1 -0
- package/dist/structure/format-detect.js +22 -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 +13 -2
- 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 +243 -222
- package/dist/structure/partial-occupancy.js +4 -2
- package/dist/structure/pbc.js +78 -98
- package/dist/structure/polyhedra.d.ts +0 -1
- package/dist/structure/polyhedra.js +3 -18
- 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 +56 -68
- package/dist/symmetry/WyckoffTable.svelte +5 -24
- package/dist/symmetry/cell-transform.js +1 -0
- package/dist/symmetry/index.d.ts +1 -0
- package/dist/symmetry/index.js +8 -2
- package/dist/symmetry/spacegroups.js +3 -2
- package/dist/table/HeatmapTable.svelte +1102 -365
- package/dist/table/HeatmapTable.svelte.d.ts +12 -2
- package/dist/table/ToggleMenu.svelte +5 -12
- 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/index.d.ts +1 -5
- package/dist/theme/index.js +4 -10
- package/dist/theme/themes.mjs +14 -7
- package/dist/tooltip/KCoords.svelte +4 -1
- package/dist/tooltip/TooltipContent.svelte +2 -5
- package/dist/trajectory/Trajectory.svelte +515 -308
- package/dist/trajectory/Trajectory.svelte.d.ts +1 -1
- package/dist/trajectory/TrajectoryError.svelte +11 -8
- package/dist/trajectory/TrajectoryExportPane.svelte +14 -31
- package/dist/trajectory/TrajectoryInfoPane.svelte +85 -97
- package/dist/trajectory/extract.js +56 -34
- package/dist/trajectory/format-detect.js +11 -9
- package/dist/trajectory/frame-reader.d.ts +2 -0
- package/dist/trajectory/frame-reader.js +33 -29
- package/dist/trajectory/helpers.d.ts +2 -0
- package/dist/trajectory/helpers.js +18 -7
- package/dist/trajectory/index.d.ts +1 -0
- package/dist/trajectory/index.js +3 -0
- 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 +2 -3
- package/dist/trajectory/parse/index.js +17 -20
- 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 +5 -0
- package/dist/trajectory/parse/vaspout-h5.js +284 -0
- package/dist/trajectory/parse/xyz.js +11 -7
- package/dist/trajectory/plotting.d.ts +8 -9
- package/dist/trajectory/plotting.js +99 -27
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +7 -0
- package/dist/xrd/XrdPlot.svelte +47 -61
- package/dist/xrd/broadening.js +2 -1
- package/dist/xrd/calc-xrd.js +2 -4
- package/dist/xrd/parse.d.ts +0 -1
- package/dist/xrd/parse.js +56 -73
- package/package.json +61 -33
- 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/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
|
|
@@ -37,7 +38,35 @@ const aggregate_parse_error = (filename) => {
|
|
|
37
38
|
};
|
|
38
39
|
const cif_coords_key = (coords) => `${coords[0].toFixed(6)},${coords[1].toFixed(6)},${coords[2].toFixed(6)}`;
|
|
39
40
|
const cif_site_key = (element, abc, label) => `${element}|${label}|${cif_coords_key(abc)}`;
|
|
40
|
-
|
|
41
|
+
// Bravais lattice centering translations (excluding the identity) keyed by the
|
|
42
|
+
// leading letter of a space-group Hermann-Mauguin symbol. R is the obverse
|
|
43
|
+
// hexagonal setting.
|
|
44
|
+
const CENTERING_VECTORS = {
|
|
45
|
+
P: [],
|
|
46
|
+
I: [[0.5, 0.5, 0.5]],
|
|
47
|
+
F: [
|
|
48
|
+
[0, 0.5, 0.5],
|
|
49
|
+
[0.5, 0, 0.5],
|
|
50
|
+
[0.5, 0.5, 0],
|
|
51
|
+
],
|
|
52
|
+
A: [[0, 0.5, 0.5]],
|
|
53
|
+
B: [[0.5, 0, 0.5]],
|
|
54
|
+
C: [[0.5, 0.5, 0]],
|
|
55
|
+
R: [
|
|
56
|
+
[2 / 3, 1 / 3, 1 / 3],
|
|
57
|
+
[1 / 3, 2 / 3, 2 / 3],
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
// Detect the centering letter from a CIF's space-group H-M symbol, if present.
|
|
61
|
+
const extract_cif_centering = (text) => {
|
|
62
|
+
for (const line of text.split(`\n`)) {
|
|
63
|
+
const match = /^_(?:symmetry_space_group_name_h-m|space_group_name_h-m(?:_alt)?)\s+(?<symbol>.+)/i.exec(line.trim());
|
|
64
|
+
const letter = match?.groups?.symbol.replaceAll(/['"]/g, ``).trim()[0]?.toUpperCase();
|
|
65
|
+
if (letter && letter in CENTERING_VECTORS)
|
|
66
|
+
return letter;
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
};
|
|
41
70
|
const vec3_from_values = (values, context) => {
|
|
42
71
|
if (values?.length !== 3) {
|
|
43
72
|
throw new Error(`Invalid ${context}: expected 3 coordinates, got ${values?.length ?? 0}`);
|
|
@@ -56,7 +85,7 @@ const vec3_from_values = (values, context) => {
|
|
|
56
85
|
// Parse a coordinate value that might be in various scientific notation formats
|
|
57
86
|
function parse_coordinate(str) {
|
|
58
87
|
const normalized = normalize_scientific_notation(str.trim());
|
|
59
|
-
const value =
|
|
88
|
+
const value = Number(normalized);
|
|
60
89
|
if (isNaN(value))
|
|
61
90
|
throw new Error(`Invalid coordinate value: ${str}`);
|
|
62
91
|
return value;
|
|
@@ -70,9 +99,9 @@ function parse_coordinate_line(line) {
|
|
|
70
99
|
const sanitized = line
|
|
71
100
|
.trim()
|
|
72
101
|
// Add space when '-' follows a digit and precedes a digit or dot
|
|
73
|
-
.replaceAll(/(
|
|
102
|
+
.replaceAll(/(?<digit>\d)-(?=[\d.])/g, `$1 -`)
|
|
74
103
|
// Revert accidental spaces after exponent markers
|
|
75
|
-
.replaceAll(/([eE])\s-\s/g, `$1-`);
|
|
104
|
+
.replaceAll(/(?<exp_marker>[eE])\s-\s/g, `$1-`);
|
|
76
105
|
tokens = sanitized.split(/\s+/);
|
|
77
106
|
}
|
|
78
107
|
if (tokens.length < 3)
|
|
@@ -114,14 +143,6 @@ function resolve_optimade_element(species_name, species_list, index) {
|
|
|
114
143
|
return { symbol: stripped, sym_idx: -1 };
|
|
115
144
|
return { symbol: validate_element_symbol(species_name, index), sym_idx: -1 };
|
|
116
145
|
}
|
|
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
146
|
const approximate_cart_to_frac = (xyz, axis_lengths) => [
|
|
126
147
|
Math.abs(axis_lengths[0]) > math.EPS ? xyz[0] / axis_lengths[0] : 0,
|
|
127
148
|
Math.abs(axis_lengths[1]) > math.EPS ? xyz[1] / axis_lengths[1] : 0,
|
|
@@ -136,9 +157,12 @@ const matrix3x3_from_rows = (rows, context) => [
|
|
|
136
157
|
// cart→frac converter that falls back to per-axis-length division for singular lattices.
|
|
137
158
|
// axis_lengths defaults to the row norms of the lattice matrix.
|
|
138
159
|
const cart_to_frac_with_fallback = (matrix, axis_lengths) => {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
160
|
+
try {
|
|
161
|
+
return { convert: math.create_cart_to_frac(matrix), exact: true };
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
// fall through to the per-axis-length approximation below
|
|
165
|
+
}
|
|
142
166
|
const lengths = axis_lengths ?? [
|
|
143
167
|
Math.hypot(...matrix[0]),
|
|
144
168
|
Math.hypot(...matrix[1]),
|
|
@@ -193,9 +217,7 @@ export function parse_poscar(content) {
|
|
|
193
217
|
let atom_counts = [];
|
|
194
218
|
// Detect if this is VASP 5+ format (has element symbols)
|
|
195
219
|
// 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);
|
|
220
|
+
const has_element_symbols = isNaN(parse_leading_num(lines[line_index]));
|
|
199
221
|
if (has_element_symbols) {
|
|
200
222
|
// VASP 5+ format - parse element symbols (may span multiple lines)
|
|
201
223
|
let symbol_lines = 1;
|
|
@@ -203,9 +225,7 @@ export function parse_poscar(content) {
|
|
|
203
225
|
for (let lookahead_idx = 1; lookahead_idx < 10; lookahead_idx++) {
|
|
204
226
|
if (line_index + lookahead_idx >= lines.length)
|
|
205
227
|
break;
|
|
206
|
-
|
|
207
|
-
const next_token_as_number = parseInt(next_line_first_token, 10);
|
|
208
|
-
if (!isNaN(next_token_as_number)) {
|
|
228
|
+
if (!isNaN(parse_leading_num(lines[line_index + lookahead_idx]))) {
|
|
209
229
|
symbol_lines = lookahead_idx;
|
|
210
230
|
break;
|
|
211
231
|
}
|
|
@@ -329,8 +349,9 @@ export function parse_xyz(content) {
|
|
|
329
349
|
diag_error(`XYZ frame too short`);
|
|
330
350
|
return null;
|
|
331
351
|
}
|
|
332
|
-
// Parse number of atoms (line 1)
|
|
333
|
-
|
|
352
|
+
// Parse number of atoms (line 1). Only the first token counts: Tinker-style
|
|
353
|
+
// XYZ files put a title after the count (e.g. `6 methane`)
|
|
354
|
+
const num_atoms = Math.trunc(parse_leading_num(lines[0]));
|
|
334
355
|
if (isNaN(num_atoms) || num_atoms <= 0) {
|
|
335
356
|
diag_error(`Invalid number of atoms in XYZ file`);
|
|
336
357
|
return null;
|
|
@@ -339,7 +360,7 @@ export function parse_xyz(content) {
|
|
|
339
360
|
const comment_line = lines[1];
|
|
340
361
|
let lattice;
|
|
341
362
|
// Check for extended XYZ lattice information in comment line
|
|
342
|
-
const lattice_match = /Lattice="([^"]+)"/.exec(comment_line);
|
|
363
|
+
const lattice_match = /Lattice="(?<lattice>[^"]+)"/.exec(comment_line);
|
|
343
364
|
if (lattice_match) {
|
|
344
365
|
const lattice_values = lattice_match[1].split(/\s+/).map(parse_coordinate);
|
|
345
366
|
if (lattice_values.length === 9) {
|
|
@@ -407,8 +428,7 @@ const parse_symmetry_expression = (expr_input) => {
|
|
|
407
428
|
// E.g., "x-y+1/3" → ["x", "-y", "+1/3"] or "-x+y" → ["-x", "+y"]
|
|
408
429
|
const tokens = [];
|
|
409
430
|
let current_token = ``;
|
|
410
|
-
for (
|
|
411
|
-
const char = expr[idx];
|
|
431
|
+
for (const char of expr) {
|
|
412
432
|
if ((char === `+` || char === `-`) && current_token.length > 0) {
|
|
413
433
|
tokens.push(current_token);
|
|
414
434
|
current_token = char;
|
|
@@ -421,7 +441,7 @@ const parse_symmetry_expression = (expr_input) => {
|
|
|
421
441
|
tokens.push(current_token);
|
|
422
442
|
for (const token of tokens) {
|
|
423
443
|
// Check if this token is a variable term (x, y, or z with optional sign)
|
|
424
|
-
const var_match = /^([+-]?)([xyz])$/.exec(token);
|
|
444
|
+
const var_match = /^(?<sign>[+-]?)(?<axis>[xyz])$/.exec(token);
|
|
425
445
|
if (var_match) {
|
|
426
446
|
const sign = var_match[1] === `-` ? -1 : 1;
|
|
427
447
|
const var_char = var_match[2];
|
|
@@ -447,8 +467,8 @@ const parse_symmetry_expression = (expr_input) => {
|
|
|
447
467
|
// Fraction
|
|
448
468
|
const parts = num_str.split(`/`);
|
|
449
469
|
if (parts.length === 2) {
|
|
450
|
-
const numerator =
|
|
451
|
-
const denominator =
|
|
470
|
+
const numerator = Number(parts[0]);
|
|
471
|
+
const denominator = Number(parts[1]);
|
|
452
472
|
if (!isNaN(numerator) && !isNaN(denominator) && denominator !== 0) {
|
|
453
473
|
translation += sign * (numerator / denominator);
|
|
454
474
|
}
|
|
@@ -456,7 +476,7 @@ const parse_symmetry_expression = (expr_input) => {
|
|
|
456
476
|
}
|
|
457
477
|
else {
|
|
458
478
|
// Integer or decimal
|
|
459
|
-
const val =
|
|
479
|
+
const val = Number(num_str);
|
|
460
480
|
if (!isNaN(val)) {
|
|
461
481
|
translation += sign * val;
|
|
462
482
|
}
|
|
@@ -464,49 +484,50 @@ const parse_symmetry_expression = (expr_input) => {
|
|
|
464
484
|
}
|
|
465
485
|
return { coefficients, translation };
|
|
466
486
|
};
|
|
467
|
-
// Apply symmetry operations
|
|
468
|
-
|
|
469
|
-
|
|
487
|
+
// Apply symmetry operations (and optional lattice-centering translations) to
|
|
488
|
+
// generate all equivalent positions. Deduplication uses 6 decimal places to
|
|
489
|
+
// absorb floating point error from compound ops like x-y, -x+y.
|
|
490
|
+
const apply_symmetry_ops = (atom, symmetry_ops, wrap_fractional_coords, centering = []) => {
|
|
491
|
+
if (symmetry_ops.length === 0 && centering.length === 0)
|
|
470
492
|
return [atom];
|
|
471
493
|
const equivalent_atoms = [];
|
|
472
494
|
const seen = new Set();
|
|
473
495
|
const wrap = (coords) => wrap_fractional_coords ? wrap_to_unit_cell(coords) : coords;
|
|
474
|
-
//
|
|
475
|
-
|
|
476
|
-
//
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
496
|
+
// Every generated position is also offset by each centering translation
|
|
497
|
+
const shifts = [[0, 0, 0], ...centering];
|
|
498
|
+
// Record a position plus its centering images, deduplicating on wrapped coords
|
|
499
|
+
const add_position = (coords) => {
|
|
500
|
+
for (const [dx, dy, dz] of shifts) {
|
|
501
|
+
const wrapped = wrap([coords[0] + dx, coords[1] + dy, coords[2] + dz]);
|
|
502
|
+
const key = cif_coords_key(wrapped);
|
|
503
|
+
if (seen.has(key))
|
|
504
|
+
continue;
|
|
505
|
+
seen.add(key);
|
|
506
|
+
const id = equivalent_atoms.length === 0 ? atom.id : `${atom.id}_${equivalent_atoms.length}`;
|
|
507
|
+
equivalent_atoms.push({ ...atom, coords: wrapped, id });
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
add_position(atom.coords); // base atom (+ centering images)
|
|
511
|
+
// ops arrive pre-normalized (quotes + whitespace already stripped, see normalized_ops)
|
|
480
512
|
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());
|
|
513
|
+
const parts = operation.split(`,`);
|
|
484
514
|
if (parts.length !== 3)
|
|
485
515
|
continue;
|
|
486
516
|
const new_coords = [0, 0, 0];
|
|
487
517
|
for (let dim = 0; dim < 3; dim++) {
|
|
488
518
|
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;
|
|
519
|
+
// new_coord = coeff_x * x + coeff_y * y + coeff_z * z + translation
|
|
520
|
+
new_coords[dim] = math.dot(coefficients, atom.coords) + translation;
|
|
495
521
|
}
|
|
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
|
-
});
|
|
522
|
+
add_position(new_coords);
|
|
507
523
|
}
|
|
508
524
|
return equivalent_atoms;
|
|
509
525
|
};
|
|
526
|
+
// Parse a CIF numeric token, stripping a trailing uncertainty like "1.234(5)"
|
|
527
|
+
const parse_cif_uncertain_number = (token) => {
|
|
528
|
+
const value = parse_num_token(token.split(`(`)[0]);
|
|
529
|
+
return isNaN(value) ? null : value;
|
|
530
|
+
};
|
|
510
531
|
const extract_cif_cell_parameters = (text, type, strict = true) => text
|
|
511
532
|
.split(`\n`)
|
|
512
533
|
.filter((line) => line.startsWith(`_${type}`))
|
|
@@ -519,11 +540,9 @@ const extract_cif_cell_parameters = (text, type, strict = true) => text
|
|
|
519
540
|
throw new Error(`Invalid CIF cell parameter line format: ${line}`);
|
|
520
541
|
return null;
|
|
521
542
|
}
|
|
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
|
|
543
|
+
const value = parse_cif_uncertain_number(tokens[1]);
|
|
544
|
+
if (value === null && strict) {
|
|
545
|
+
throw new Error(`Invalid CIF cell parameter in line: ${line}`);
|
|
527
546
|
}
|
|
528
547
|
return value;
|
|
529
548
|
})
|
|
@@ -551,6 +570,22 @@ const build_cif_atom_site_header_indices = (headers) => {
|
|
|
551
570
|
});
|
|
552
571
|
return indices;
|
|
553
572
|
};
|
|
573
|
+
// Which coordinate triple a CIF atom-site loop provides (fractional preferred), or null
|
|
574
|
+
const cif_coords_type = (indices) => {
|
|
575
|
+
if (indices.x !== undefined && indices.y !== undefined && indices.z !== undefined) {
|
|
576
|
+
return `fract`;
|
|
577
|
+
}
|
|
578
|
+
if (indices.cart_x !== undefined &&
|
|
579
|
+
indices.cart_y !== undefined &&
|
|
580
|
+
indices.cart_z !== undefined) {
|
|
581
|
+
return `cart`;
|
|
582
|
+
}
|
|
583
|
+
return null;
|
|
584
|
+
};
|
|
585
|
+
// The 3 column indices for the requested coordinate type
|
|
586
|
+
const cif_coord_indices = (indices, coords_type) => coords_type === `fract`
|
|
587
|
+
? [indices.x, indices.y, indices.z]
|
|
588
|
+
: [indices.cart_x, indices.cart_y, indices.cart_z];
|
|
554
589
|
// Walk CIF loop_ blocks: yields each loop's header tags plus the index of its first data line
|
|
555
590
|
function* iter_cif_loops(lines) {
|
|
556
591
|
for (let idx = 0; idx < lines.length; idx++) {
|
|
@@ -571,28 +606,27 @@ const split_cif_tokens = (line) => (line.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)
|
|
|
571
606
|
// Parse atom data from CIF with robust error handling
|
|
572
607
|
const parse_cif_atom_data = (raw_data, indices, coords_type) => {
|
|
573
608
|
const { label = 0, symbol = -1, occupancy = -1 } = indices;
|
|
574
|
-
const coord_indices = coords_type
|
|
575
|
-
? [indices.x, indices.y, indices.z]
|
|
576
|
-
: [indices.cart_x, indices.cart_y, indices.cart_z];
|
|
609
|
+
const coord_indices = cif_coord_indices(indices, coords_type);
|
|
577
610
|
if (coord_indices.some((idx) => idx === undefined)) {
|
|
578
611
|
throw new Error(`Missing coordinate indices`);
|
|
579
612
|
}
|
|
580
613
|
const coords_triplet = vec3_from_values(coord_indices.map((idx) => {
|
|
581
|
-
|
|
582
|
-
throw new Error(`Invalid coordinate index`);
|
|
614
|
+
// idx cannot be undefined: the `.some` guard above already threw
|
|
583
615
|
const coord_str = raw_data[idx];
|
|
584
616
|
if (!coord_str)
|
|
585
617
|
throw new Error(`Missing coordinate at index ${idx}`);
|
|
586
|
-
const coord =
|
|
587
|
-
if (
|
|
618
|
+
const coord = parse_cif_uncertain_number(coord_str);
|
|
619
|
+
if (coord === null)
|
|
588
620
|
throw new Error(`Invalid coordinate: ${coord_str}`);
|
|
589
621
|
return coord;
|
|
590
622
|
}), `CIF atom coordinates`);
|
|
591
|
-
const
|
|
592
|
-
?
|
|
593
|
-
:
|
|
594
|
-
|
|
595
|
-
const
|
|
623
|
+
const raw_occu = occupancy >= 0 && raw_data[occupancy]
|
|
624
|
+
? parse_cif_uncertain_number(raw_data[occupancy])
|
|
625
|
+
: null;
|
|
626
|
+
// invalid or zero occupancy defaults to fully occupied
|
|
627
|
+
const occu = raw_occu == null || raw_occu === 0 ? 1.0 : raw_occu;
|
|
628
|
+
const from_symbol = symbol >= 0 ? /^(?<element>[A-Z][a-z]*)/.exec(raw_data[symbol])?.[1] : undefined;
|
|
629
|
+
const element_symbol = from_symbol ?? raw_data[label]?.match(/(?:[A-Z][a-z]*)/g)?.[0];
|
|
596
630
|
if (!element_symbol) {
|
|
597
631
|
throw new Error(`Could not extract element symbol from: ${raw_data.join(` `)}`);
|
|
598
632
|
}
|
|
@@ -639,13 +673,7 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
639
673
|
continue;
|
|
640
674
|
// Check if this loop contains coordinate headers
|
|
641
675
|
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)
|
|
676
|
+
if (cif_coords_type(indices_preview) === null)
|
|
649
677
|
continue;
|
|
650
678
|
// This is the desired atom-site loop with coordinates: collect data lines
|
|
651
679
|
atom_headers = headers;
|
|
@@ -679,23 +707,13 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
679
707
|
// Parse atom data with error handling
|
|
680
708
|
const header_indices = build_cif_atom_site_header_indices(atom_headers);
|
|
681
709
|
// 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;
|
|
710
|
+
const coords_type = cif_coords_type(header_indices);
|
|
691
711
|
if (!coords_type) {
|
|
692
712
|
diag_error(`CIF atom site loop missing coordinates (fract or Cartn)`);
|
|
693
713
|
return null;
|
|
694
714
|
}
|
|
695
715
|
// 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];
|
|
716
|
+
const required_indices = cif_coord_indices(header_indices, coords_type);
|
|
699
717
|
const atoms = atom_data_lines
|
|
700
718
|
.map(split_cif_tokens)
|
|
701
719
|
.filter((tokens) => {
|
|
@@ -734,15 +752,9 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
734
752
|
const cart_to_frac = cart_to_frac_with_fallback(lattice_matrix, [a, b, c]).convert;
|
|
735
753
|
// Create sites with coordinate conversion and symmetry operations
|
|
736
754
|
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)
|
|
755
|
+
// Strip surrounding quotes and all whitespace (preserving duplicates; positions
|
|
756
|
+
// are deduplicated later). Leaves ops as bare `x,y,z`-style expressions.
|
|
757
|
+
const normalized_ops = symmetry_ops.map((op) => (/['"](?<expr>[^'"]+)['"]/.exec(op)?.groups?.expr ?? op).replaceAll(/\s+/g, ``));
|
|
746
758
|
// Inspect optional _atom_type_number_in_cell loop to see if atom sites are already expanded
|
|
747
759
|
const atom_type_counts = {};
|
|
748
760
|
for (const { headers, data_start } of iter_cif_loops(lines)) {
|
|
@@ -760,11 +772,15 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
760
772
|
const toks = split_cif_tokens(line);
|
|
761
773
|
if (toks.length > Math.max(sym_idx, num_idx)) {
|
|
762
774
|
// Normalize type symbol to bare element (e.g. 'Sn2+' -> 'Sn')
|
|
763
|
-
const match = /^([A-Z][a-z]*)/.exec(toks[sym_idx]);
|
|
775
|
+
const match = /^(?<element>[A-Z][a-z]*)/.exec(toks[sym_idx]);
|
|
764
776
|
const sym = match ? match[1] : toks[sym_idx];
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
777
|
+
// Strip standard-uncertainty parentheses (`8(0)` -> `8`) like other CIF
|
|
778
|
+
// readers; empty prefixes like `(8)` parse as NaN and get skipped
|
|
779
|
+
const num = Math.trunc(parse_num_token(toks[num_idx].split(`(`)[0]));
|
|
780
|
+
// sum rows that normalize to the same element (e.g. Fe2+ and Fe3+ → Fe)
|
|
781
|
+
if (sym && !Number.isNaN(num)) {
|
|
782
|
+
atom_type_counts[sym] = (atom_type_counts[sym] ?? 0) + num;
|
|
783
|
+
}
|
|
768
784
|
}
|
|
769
785
|
}
|
|
770
786
|
break;
|
|
@@ -777,38 +793,61 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
777
793
|
const already_enumerated = has_expected_counts &&
|
|
778
794
|
Object.entries(atom_type_counts).every(([el, exp]) => (observed_counts[el] || 0) >= exp);
|
|
779
795
|
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
|
-
|
|
796
|
+
// Candidate lattice-centering translations from the space-group symbol (R
|
|
797
|
+
// only valid in the hexagonal setting, α≈β≈90°, γ≈120°). Whether to actually
|
|
798
|
+
// apply them is decided below by reconciling against _atom_type_number_in_cell.
|
|
799
|
+
const centering_letter = extract_cif_centering(text);
|
|
800
|
+
const is_hexagonal_setting = Math.abs(alpha - 90) <= 1 && Math.abs(beta - 90) <= 1 && Math.abs(gamma - 120) <= 1;
|
|
801
|
+
const centering = centering_letter && (centering_letter !== `R` || is_hexagonal_setting)
|
|
802
|
+
? CENTERING_VECTORS[centering_letter]
|
|
803
|
+
: [];
|
|
804
|
+
// Build all sites by expanding each atom via the symmetry ops (+ optional
|
|
805
|
+
// centering). Deduplicate globally on element + coordinates + label (6 dp to
|
|
806
|
+
// absorb floating point error from compound ops).
|
|
807
|
+
const build_sites = (extra_centering) => {
|
|
808
|
+
const sites = [];
|
|
809
|
+
const seen_site_keys = new Set();
|
|
810
|
+
for (const atom of atoms) {
|
|
811
|
+
const element = validate_element_symbol(atom.element, sites.length);
|
|
812
|
+
const coords = atom.coords_type === `fract`
|
|
813
|
+
? wrap_vec3(atom.coords)
|
|
814
|
+
: wrap_vec3(cart_to_frac([atom.coords[0], atom.coords[1], atom.coords[2]]));
|
|
815
|
+
const fractional_atom = { ...atom, coords, coords_type: `fract` };
|
|
816
|
+
const equiv_atoms = apply_symmetry_ops(fractional_atom, ops_to_use, wrap_fractional_coords, extra_centering);
|
|
817
|
+
for (const equiv_atom of equiv_atoms) {
|
|
818
|
+
const abc = wrap_vec3(equiv_atom.coords);
|
|
819
|
+
const key = cif_site_key(element, abc, equiv_atom.id);
|
|
820
|
+
if (seen_site_keys.has(key))
|
|
821
|
+
continue;
|
|
822
|
+
seen_site_keys.add(key);
|
|
823
|
+
sites.push(make_site(element, abc, frac_to_cart(abc), equiv_atom.id, {}, equiv_atom.occupancy));
|
|
824
|
+
}
|
|
798
825
|
}
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
826
|
+
return sites;
|
|
827
|
+
};
|
|
828
|
+
// Expand with point-group ops first. If the space group is centered and the
|
|
829
|
+
// result falls short of _atom_type_number_in_cell, retry with centering and
|
|
830
|
+
// adopt it only when it reconciles the expected total exactly — this fixes
|
|
831
|
+
// CIFs listing point-only ops for the asymmetric unit while avoiding
|
|
832
|
+
// double-counting CIFs whose atom list already embeds centering (e.g. C2/c
|
|
833
|
+
// COD 7008984, where listed ops + atoms already total the cell contents).
|
|
834
|
+
let sites = build_sites([]);
|
|
835
|
+
const expected_total = Object.values(atom_type_counts).reduce((sum, num) => sum + num, 0);
|
|
836
|
+
if (centering.length > 0 && expected_total > sites.length) {
|
|
837
|
+
const centered_sites = build_sites(centering);
|
|
838
|
+
// Adopt centering only when per-element counts reconcile exactly. Checking
|
|
839
|
+
// the total alone is insufficient: it can coincide while individual element
|
|
840
|
+
// counts are wrong (e.g. expected Fe 1 / O 3 but centering yields Fe 2 / O 2).
|
|
841
|
+
const counts = {};
|
|
842
|
+
for (const site of centered_sites) {
|
|
843
|
+
const element = site.species[0].element;
|
|
844
|
+
counts[element] = (counts[element] ?? 0) + 1;
|
|
809
845
|
}
|
|
846
|
+
const reconciles = centered_sites.length === expected_total &&
|
|
847
|
+
Object.entries(atom_type_counts).every(([element, exp]) => counts[element] === exp);
|
|
848
|
+
if (reconciles)
|
|
849
|
+
sites = centered_sites;
|
|
810
850
|
}
|
|
811
|
-
const sites = all_sites;
|
|
812
851
|
return { sites, lattice: { matrix: lattice_matrix, ...lattice_params } };
|
|
813
852
|
}
|
|
814
853
|
catch (error) {
|
|
@@ -865,7 +904,7 @@ export function parse_phonopy_yaml(content, cell_type) {
|
|
|
865
904
|
}
|
|
866
905
|
// Check if we're still in the phonon_displacements section
|
|
867
906
|
if (skip_displacements) {
|
|
868
|
-
if (/^[a-zA-Z_]/.
|
|
907
|
+
if (/^[a-zA-Z_]/.test(line)) {
|
|
869
908
|
// New top-level key, stop skipping
|
|
870
909
|
skip_displacements = false;
|
|
871
910
|
}
|
|
@@ -888,17 +927,16 @@ export function parse_phonopy_yaml(content, cell_type) {
|
|
|
888
927
|
diag_error(`Requested cell type '${cell_type}' not found in phonopy YAML`);
|
|
889
928
|
return null;
|
|
890
929
|
}
|
|
891
|
-
// Auto mode: return
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
get_phonopy_cell(data,
|
|
900
|
-
|
|
901
|
-
get_phonopy_cell(data, `primitive_cell`);
|
|
930
|
+
// Auto mode: return first available cell, most detailed first
|
|
931
|
+
const auto_cell = [
|
|
932
|
+
`supercell`,
|
|
933
|
+
`phonon_supercell`,
|
|
934
|
+
`unit_cell`,
|
|
935
|
+
`phonon_primitive_cell`,
|
|
936
|
+
`primitive_cell`,
|
|
937
|
+
]
|
|
938
|
+
.map((kind) => get_phonopy_cell(data, kind))
|
|
939
|
+
.find(Boolean);
|
|
902
940
|
if (auto_cell)
|
|
903
941
|
return convert_phonopy_cell(auto_cell);
|
|
904
942
|
diag_error(`No valid cells found in phonopy YAML`);
|
|
@@ -956,6 +994,31 @@ export function is_parsed_structure(obj) {
|
|
|
956
994
|
const has_coords = Array.isArray(abc) || Array.isArray(xyz);
|
|
957
995
|
return has_species && has_coords;
|
|
958
996
|
}
|
|
997
|
+
// Structure JSON serialized by pymatgen (default verbosity) stores only the lattice
|
|
998
|
+
// matrix + pbc; derive the missing scalar params (a/b/c/angles/volume) from the matrix
|
|
999
|
+
// so downstream consumers (camera auto-fit, density, export) never see NaN.
|
|
1000
|
+
export function ensure_lattice_params(structure) {
|
|
1001
|
+
const lattice = structure.lattice;
|
|
1002
|
+
if (!lattice?.matrix)
|
|
1003
|
+
return structure;
|
|
1004
|
+
const params = [
|
|
1005
|
+
lattice.a,
|
|
1006
|
+
lattice.b,
|
|
1007
|
+
lattice.c,
|
|
1008
|
+
lattice.alpha,
|
|
1009
|
+
lattice.beta,
|
|
1010
|
+
lattice.gamma,
|
|
1011
|
+
lattice.volume,
|
|
1012
|
+
];
|
|
1013
|
+
if (params.every(Number.isFinite))
|
|
1014
|
+
return structure;
|
|
1015
|
+
// The matrix is authoritative: recompute all params from it rather than
|
|
1016
|
+
// trusting a partially-populated (or non-numeric) set of values.
|
|
1017
|
+
return {
|
|
1018
|
+
...structure,
|
|
1019
|
+
lattice: { ...lattice, ...math.calc_lattice_params(lattice.matrix) },
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
959
1022
|
// Normalize structure coordinates: wrap fractional coords to [0,1) and recompute Cartesian
|
|
960
1023
|
// Only normalizes when lattice matrix is available to ensure abc/xyz stay consistent
|
|
961
1024
|
export function normalize_fractional_coords(structure) {
|
|
@@ -989,7 +1052,7 @@ const detect_json_structure = (content) => {
|
|
|
989
1052
|
}
|
|
990
1053
|
// Otherwise try parsing as pymatgen/nested structure JSON
|
|
991
1054
|
const structure = find_structure_in_json(parsed);
|
|
992
|
-
return structure ? normalize_fractional_coords(structure) : null;
|
|
1055
|
+
return structure ? ensure_lattice_params(normalize_fractional_coords(structure)) : null;
|
|
993
1056
|
};
|
|
994
1057
|
// Internal: auto-detect file format, returns null on failure after recording reasons (see parse error contract at top)
|
|
995
1058
|
function parse_structure_file_impl(content, filename) {
|
|
@@ -1048,36 +1111,20 @@ function parse_structure_file_impl(content, filename) {
|
|
|
1048
1111
|
diag_error(`File too short to determine format`);
|
|
1049
1112
|
return null;
|
|
1050
1113
|
}
|
|
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
|
-
}
|
|
1114
|
+
// XYZ format detection: first line is a positive atom count, second line is a
|
|
1115
|
+
// comment, and the first coordinate line looks like "<element> <x> <y> <z>"
|
|
1116
|
+
const first_line_number = Math.trunc(parse_leading_num(lines[0]));
|
|
1117
|
+
if (!isNaN(first_line_number) &&
|
|
1118
|
+
first_line_number > 0 &&
|
|
1119
|
+
lines.length >= first_line_number + 2 &&
|
|
1120
|
+
is_xyz_atom_line(lines[2]?.trim().split(/\s+/)))
|
|
1121
|
+
return parse_xyz(content);
|
|
1076
1122
|
// POSCAR format detection: look for typical structure
|
|
1077
1123
|
if (lines.length >= 8) {
|
|
1078
|
-
|
|
1079
|
-
//
|
|
1080
|
-
|
|
1124
|
+
// Second line starts with a number (scale factor), likely POSCAR. First
|
|
1125
|
+
// token only: POSCAR allows three per-axis scale factors (or trailing
|
|
1126
|
+
// comments) on line 2, and blank lines must not pass
|
|
1127
|
+
if (!isNaN(parse_leading_num(lines[1])))
|
|
1081
1128
|
return parse_poscar(content);
|
|
1082
1129
|
}
|
|
1083
1130
|
// CIF format detection: look for CIF-specific keywords
|
|
@@ -1109,16 +1156,19 @@ export function parse_structure_file(content, filename) {
|
|
|
1109
1156
|
// Universal parser for JSON and structure files; throws an Error aggregating per-format failure reasons when nothing parses
|
|
1110
1157
|
export function parse_any_structure(content, filename) {
|
|
1111
1158
|
reset_parse_diagnostics();
|
|
1112
|
-
const finalize_structure = (
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1159
|
+
const finalize_structure = (parsed_structure) => {
|
|
1160
|
+
const structure = ensure_lattice_params(parsed_structure);
|
|
1161
|
+
return {
|
|
1162
|
+
sites: structure.sites,
|
|
1163
|
+
charge: 0,
|
|
1164
|
+
...(structure.properties && {
|
|
1165
|
+
properties: structuredClone(structure.properties),
|
|
1166
|
+
}),
|
|
1167
|
+
...(structure.lattice && {
|
|
1168
|
+
lattice: { ...structure.lattice, pbc: [true, true, true] },
|
|
1169
|
+
}),
|
|
1170
|
+
};
|
|
1171
|
+
};
|
|
1122
1172
|
// Fast path: content is already a serialized structure object
|
|
1123
1173
|
try {
|
|
1124
1174
|
const parsed = JSON.parse(content);
|
|
@@ -1306,35 +1356,6 @@ export function optimade_to_crystal(optimade_structure) {
|
|
|
1306
1356
|
return null;
|
|
1307
1357
|
}
|
|
1308
1358
|
}
|
|
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
1359
|
export const detect_structure_type = (filename, content) => {
|
|
1339
1360
|
// Normalize compressed suffixes (gz, gzip, zip, xz, bz2) for detection parity
|
|
1340
1361
|
const name_to_check = strip_compression_extensions(filename);
|