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
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
// Parser for VASP 6.x HDF5 output (vaspout.h5, written with LH5=.TRUE.).
|
|
2
|
+
// Reads the ionic-relaxation trajectory from intermediate/ion_dynamics (with
|
|
3
|
+
// per-frame energy/forces so convergence plots come for free) and falls back
|
|
4
|
+
// to the final structure in results/positions for static (NSW=0) runs, which
|
|
5
|
+
// render as single-frame trajectories (structure only, plots hidden).
|
|
6
|
+
//
|
|
7
|
+
// Tolerates files from incomplete/running calculations: datasets flushed
|
|
8
|
+
// mid-write may disagree on step counts (e.g. 5 energies but 4 position
|
|
9
|
+
// frames), so frame count is the minimum over required datasets and per-step
|
|
10
|
+
// reads never index past any dataset's end.
|
|
11
|
+
//
|
|
12
|
+
// Electronic results (results/electron_dos, results/electron_eigenvalues*)
|
|
13
|
+
// are read by ./vaspout-electronic.ts and attached as metadata.electronic;
|
|
14
|
+
// vaspwave.h5 charge density is handled by isosurface/parse-vaspwave.ts.
|
|
15
|
+
//
|
|
16
|
+
// Schema reference: ferrox src/io/vasp/hdf5/mod.rs (which follows py4vasp's
|
|
17
|
+
// VASP 6.x schema definitions).
|
|
18
|
+
import { calc_lattice_params, create_frac_to_cart } from '../../math';
|
|
19
|
+
import { count_elements, create_trajectory_frame, validate_3x3_matrix, } from '../helpers';
|
|
20
|
+
import { expand_ion_types, is_hdf5_group, read_dataset, scale_matrix, to_number_array, to_scalar_number, to_string_array, with_h5_file, } from './h5-utils';
|
|
21
|
+
import { read_vaspout_dos, read_vaspout_electronic } from './vaspout-electronic';
|
|
22
|
+
const FINAL_ION_TYPES = `results/positions/ion_types`;
|
|
23
|
+
const FINAL_ION_COUNTS = `results/positions/number_ion_types`;
|
|
24
|
+
const FINAL_LATTICE = `results/positions/lattice_vectors`;
|
|
25
|
+
const FINAL_POSITIONS = `results/positions/position_ions`;
|
|
26
|
+
const FINAL_SCALE = `results/positions/scale`;
|
|
27
|
+
const TRAJ_SCALE = `intermediate/ion_dynamics/scale`;
|
|
28
|
+
const TRAJ_LATTICE = `intermediate/ion_dynamics/lattice_vectors`;
|
|
29
|
+
const TRAJ_POSITIONS = `intermediate/ion_dynamics/position_ions`;
|
|
30
|
+
const TRAJ_FORCES = `intermediate/ion_dynamics/forces`;
|
|
31
|
+
const ENERGY_VALUES = `intermediate/ion_dynamics/energies`;
|
|
32
|
+
const ENERGY_LABELS = `intermediate/ion_dynamics/energies_tags`;
|
|
33
|
+
const OSZICAR_ROWS = `intermediate/ion_dynamics/oszicar`;
|
|
34
|
+
const OSZICAR_LABELS = `intermediate/ion_dynamics/oszicar_label`;
|
|
35
|
+
const ELECTRONIC_STEP_ENERGIES = `intermediate/electronic_steps/energies`;
|
|
36
|
+
// vaspout.h5 root groups per the VASP 6.x schema. torch-sim files use flat
|
|
37
|
+
// dataset names (positions/atomic_numbers/...) and never these groups.
|
|
38
|
+
export const is_vaspout_h5_file = (h5_file) => [`results`, `intermediate`].some((name) => is_hdf5_group(h5_file.get(name))) &&
|
|
39
|
+
[`input`, `version`, `results`, `intermediate`].filter((name) => is_hdf5_group(h5_file.get(name))).length >= 2;
|
|
40
|
+
// Final-structure datasets may carry a leading singleton step axis
|
|
41
|
+
// ([1, 3, 3] lattices, [1, n_atoms, 3] positions) depending on VASP version.
|
|
42
|
+
const squeeze_leading_axis = (data) => Array.isArray(data) &&
|
|
43
|
+
data.length === 1 &&
|
|
44
|
+
Array.isArray(data[0]) &&
|
|
45
|
+
Array.isArray(data[0][0])
|
|
46
|
+
? data[0]
|
|
47
|
+
: data;
|
|
48
|
+
// VASP writes several energy kinds per step; energies_tags names the columns.
|
|
49
|
+
// Prefer the free energy (TOTEN), then energy(sigma->0), then column 0.
|
|
50
|
+
const pick_energy_column = (tags) => {
|
|
51
|
+
if (!tags)
|
|
52
|
+
return { idx: 0, tag: null };
|
|
53
|
+
for (const needle of [`TOTEN`, `sigma->0`]) {
|
|
54
|
+
const idx = tags.findIndex((tag) => tag.includes(needle));
|
|
55
|
+
if (idx !== -1)
|
|
56
|
+
return { idx, tag: tags[idx] };
|
|
57
|
+
}
|
|
58
|
+
return { idx: 0, tag: tags[0] ?? null };
|
|
59
|
+
};
|
|
60
|
+
const finite_or_null = (value) => typeof value === `number` && Number.isFinite(value) ? value : null;
|
|
61
|
+
// OSZICAR rows are [total_scf_rows, n_cols] with oszicar_label naming columns
|
|
62
|
+
// (N, E, dE, d eps, ncg, rms, rms(c)). The N counter resets to 1 at each new
|
|
63
|
+
// ionic step, which is how rows group into ionic steps (same as ferrox).
|
|
64
|
+
const read_oszicar_history = (h5_file) => {
|
|
65
|
+
const rows = read_dataset(h5_file, OSZICAR_ROWS);
|
|
66
|
+
const labels = to_string_array(read_dataset(h5_file, OSZICAR_LABELS));
|
|
67
|
+
if (!rows || !labels)
|
|
68
|
+
return null;
|
|
69
|
+
const col = (name) => labels.findIndex((label) => label.trim() === name);
|
|
70
|
+
const [n_col, energy_col] = [col(`N`), col(`E`)];
|
|
71
|
+
if (n_col < 0 || energy_col < 0)
|
|
72
|
+
return null;
|
|
73
|
+
const [delta_col, rms_col, charge_rms_col] = [col(`dE`), col(`rms`), col(`rms(c)`)];
|
|
74
|
+
const ionic_steps = [];
|
|
75
|
+
let current = null;
|
|
76
|
+
let previous_scf_counter = Infinity;
|
|
77
|
+
for (const row of rows) {
|
|
78
|
+
const scf_counter = row[n_col];
|
|
79
|
+
const energy = finite_or_null(row[energy_col]);
|
|
80
|
+
if (energy === null || !Number.isFinite(scf_counter))
|
|
81
|
+
return null;
|
|
82
|
+
if (scf_counter <= previous_scf_counter || !current) {
|
|
83
|
+
current = { energies: [], energy_deltas: [], rms: [], charge_rms: [] };
|
|
84
|
+
ionic_steps.push(current);
|
|
85
|
+
}
|
|
86
|
+
current.energies.push(energy);
|
|
87
|
+
current.energy_deltas.push(delta_col >= 0 ? finite_or_null(row[delta_col]) : null);
|
|
88
|
+
current.rms.push(rms_col >= 0 ? finite_or_null(row[rms_col]) : null);
|
|
89
|
+
current.charge_rms.push(charge_rms_col >= 0 ? finite_or_null(row[charge_rms_col]) : null);
|
|
90
|
+
previous_scf_counter = scf_counter;
|
|
91
|
+
}
|
|
92
|
+
return ionic_steps.length > 0 ? ionic_steps : null;
|
|
93
|
+
};
|
|
94
|
+
// Fallback when the OSZICAR mirror is absent: the explicit per-ionic-step SCF
|
|
95
|
+
// energy dataset (shape [n_ionic, n_scf]); deltas derived from consecutive
|
|
96
|
+
// energies, no density residuals available.
|
|
97
|
+
const read_electronic_step_history = (h5_file) => {
|
|
98
|
+
const rows = read_dataset(h5_file, ELECTRONIC_STEP_ENERGIES);
|
|
99
|
+
if (!rows)
|
|
100
|
+
return null;
|
|
101
|
+
const ionic_steps = rows
|
|
102
|
+
.map((row) => {
|
|
103
|
+
const energies = (Array.isArray(row) ? row : [row]).filter((val) => typeof val === `number` && Number.isFinite(val));
|
|
104
|
+
return {
|
|
105
|
+
energies,
|
|
106
|
+
energy_deltas: energies.map((energy, idx) => idx > 0 ? energy - energies[idx - 1] : null),
|
|
107
|
+
rms: energies.map(() => null),
|
|
108
|
+
charge_rms: energies.map(() => null),
|
|
109
|
+
};
|
|
110
|
+
})
|
|
111
|
+
.filter((step) => step.energies.length > 0);
|
|
112
|
+
return ionic_steps.length > 0 ? ionic_steps : null;
|
|
113
|
+
};
|
|
114
|
+
const read_scf_history = (h5_file) => read_oszicar_history(h5_file) ?? read_electronic_step_history(h5_file);
|
|
115
|
+
// Convergence fields (|dE|, density residuals) for one SCF step; negative idx
|
|
116
|
+
// counts from the end (-1 = final step). Shared by the per-frame summary below
|
|
117
|
+
// and the SCF pseudo-frame expansion for single-point runs.
|
|
118
|
+
const scf_step_metadata = (scf, idx) => {
|
|
119
|
+
const metadata = {};
|
|
120
|
+
const delta = scf.energy_deltas.at(idx);
|
|
121
|
+
if (delta != null)
|
|
122
|
+
metadata.scf_energy_delta = Math.abs(delta);
|
|
123
|
+
const rms = scf.rms.at(idx);
|
|
124
|
+
if (rms != null)
|
|
125
|
+
metadata.scf_rms = rms;
|
|
126
|
+
const charge_rms = scf.charge_rms.at(idx);
|
|
127
|
+
if (charge_rms != null)
|
|
128
|
+
metadata.scf_charge_rms = charge_rms;
|
|
129
|
+
return metadata;
|
|
130
|
+
};
|
|
131
|
+
// Final-SCF-step summary attached to each ionic frame so relax trajectories
|
|
132
|
+
// plot SCF effort/residuals alongside energy and force_max.
|
|
133
|
+
const scf_frame_metadata = (scf) => {
|
|
134
|
+
if (!scf || scf.energies.length === 0)
|
|
135
|
+
return {};
|
|
136
|
+
const metadata = {
|
|
137
|
+
n_scf_steps: scf.energies.length,
|
|
138
|
+
...scf_step_metadata(scf, -1),
|
|
139
|
+
};
|
|
140
|
+
// interrupted writers can drop the final dE entry: derive it from the last two energies
|
|
141
|
+
if (!(`scf_energy_delta` in metadata) && scf.energies.length >= 2) {
|
|
142
|
+
metadata.scf_energy_delta = Math.abs(scf.energies.at(-1) - scf.energies.at(-2));
|
|
143
|
+
}
|
|
144
|
+
return metadata;
|
|
145
|
+
};
|
|
146
|
+
// Bands/DOS-only outputs (e.g. phelel band-path files) carry no (complete)
|
|
147
|
+
// structure datasets but still have renderable electronic results: surface
|
|
148
|
+
// them as a zero-frame trajectory the webview routes to the spectral
|
|
149
|
+
// components. Files with neither structure nor electronic data throw.
|
|
150
|
+
const electronic_only_trajectory = (h5_file, missing) => {
|
|
151
|
+
const electronic = read_vaspout_electronic(h5_file);
|
|
152
|
+
if (!electronic) {
|
|
153
|
+
throw new Error(`vaspout.h5 file has no structure data (missing ${missing}) — ` +
|
|
154
|
+
`and no electron_dos/electron_eigenvalues results either`);
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
frames: [],
|
|
158
|
+
metadata: {
|
|
159
|
+
source_format: `vaspout_h5`,
|
|
160
|
+
frame_count: 0,
|
|
161
|
+
electronic,
|
|
162
|
+
vaspout_electronic_only: true,
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
export function parse_vaspout_h5_file(h5_file) {
|
|
167
|
+
const ion_types = to_string_array(read_dataset(h5_file, FINAL_ION_TYPES));
|
|
168
|
+
const ion_counts = to_number_array(read_dataset(h5_file, FINAL_ION_COUNTS));
|
|
169
|
+
if (!ion_types || !ion_counts) {
|
|
170
|
+
return electronic_only_trajectory(h5_file, `${FINAL_ION_TYPES} / ${FINAL_ION_COUNTS}`);
|
|
171
|
+
}
|
|
172
|
+
// expand_ion_types throws on length mismatch and unknown symbols (fail loudly)
|
|
173
|
+
const elements = expand_ion_types(ion_types, ion_counts);
|
|
174
|
+
// POSCAR-style universal scaling factor; scalar in real files, but some
|
|
175
|
+
// writers store it as a 1-element array. Intermediate ion-dynamics frames
|
|
176
|
+
// carry their own scale (py4vasp's trajectory Cell reads
|
|
177
|
+
// intermediate/ion_dynamics/scale), distinct from the final-structure scale.
|
|
178
|
+
const scale = to_scalar_number(read_dataset(h5_file, FINAL_SCALE)) ?? 1;
|
|
179
|
+
const traj_scale = to_scalar_number(read_dataset(h5_file, TRAJ_SCALE)) ?? scale;
|
|
180
|
+
const traj_positions = read_dataset(h5_file, TRAJ_POSITIONS);
|
|
181
|
+
const traj_lattices = read_dataset(h5_file, TRAJ_LATTICE);
|
|
182
|
+
const traj_forces = read_dataset(h5_file, TRAJ_FORCES);
|
|
183
|
+
const energies = read_dataset(h5_file, ENERGY_VALUES);
|
|
184
|
+
const energy_tags = to_string_array(read_dataset(h5_file, ENERGY_LABELS));
|
|
185
|
+
const { idx: energy_col, tag: energy_tag } = pick_energy_column(energy_tags);
|
|
186
|
+
const scf_history = read_scf_history(h5_file);
|
|
187
|
+
const frames = [];
|
|
188
|
+
let dropped_steps = 0;
|
|
189
|
+
// Shared frame assembly for the per-step trajectory path and the
|
|
190
|
+
// final-structure fallback: validate + scale the lattice, convert fractional
|
|
191
|
+
// positions to Cartesian, attach volume/SCF/energy (and optional forces).
|
|
192
|
+
const build_frame = (lattice_data, frac_positions, step, scf, raw_energy, forces, frame_scale) => {
|
|
193
|
+
const lattice = scale_matrix(validate_3x3_matrix(lattice_data), frame_scale);
|
|
194
|
+
const frac_to_cart = create_frac_to_cart(lattice);
|
|
195
|
+
const positions = frac_positions.map((frac) => frac_to_cart(frac));
|
|
196
|
+
const metadata = {
|
|
197
|
+
volume: calc_lattice_params(lattice).volume,
|
|
198
|
+
...scf_frame_metadata(scf),
|
|
199
|
+
};
|
|
200
|
+
const energy = finite_or_null(raw_energy);
|
|
201
|
+
if (energy !== null)
|
|
202
|
+
metadata.energy = energy;
|
|
203
|
+
if (Array.isArray(forces))
|
|
204
|
+
metadata.forces = forces;
|
|
205
|
+
return create_trajectory_frame(positions, elements, lattice, [true, true, true], step, metadata);
|
|
206
|
+
};
|
|
207
|
+
if (traj_positions && traj_lattices) {
|
|
208
|
+
// A torn file's datasets may disagree on step count; never index past the
|
|
209
|
+
// shortest required one (forces/energies are optional per step).
|
|
210
|
+
const n_steps = Math.min(traj_positions.length, traj_lattices.length);
|
|
211
|
+
for (let step = 0; step < n_steps; step++) {
|
|
212
|
+
try {
|
|
213
|
+
frames.push(build_frame(traj_lattices[step], traj_positions[step], step, scf_history?.[step], energies?.[step]?.[energy_col], traj_forces?.[step], traj_scale));
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
// Torn final step: keep the frames parsed so far.
|
|
217
|
+
dropped_steps = n_steps - step;
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Static (NSW=0) runs have no intermediate/ion_dynamics group at all; fall
|
|
223
|
+
// back to the final structure so the file still opens as a 1-frame view.
|
|
224
|
+
if (frames.length === 0) {
|
|
225
|
+
const final_lattice_data = squeeze_leading_axis(read_dataset(h5_file, FINAL_LATTICE));
|
|
226
|
+
const final_positions_data = squeeze_leading_axis(read_dataset(h5_file, FINAL_POSITIONS));
|
|
227
|
+
if (!final_lattice_data || !final_positions_data) {
|
|
228
|
+
// Ion species exist but geometry is missing/torn — a bands/DOS-only
|
|
229
|
+
// file can still render its electronic results.
|
|
230
|
+
return electronic_only_trajectory(h5_file, `${TRAJ_POSITIONS} / ${FINAL_POSITIONS}`);
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
frames.push(build_frame(final_lattice_data, final_positions_data, 0, scf_history?.at(-1), energies?.at(-1)?.[energy_col], undefined, scale));
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
// Geometry datasets present but malformed (file torn mid-write): same
|
|
237
|
+
// electronic-only fallback as when they're missing entirely.
|
|
238
|
+
return electronic_only_trajectory(h5_file, `${FINAL_LATTICE} / ${FINAL_POSITIONS}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// Static/single-point runs (one ionic step) get their SCF electronic steps
|
|
242
|
+
// expanded into pseudo-frames: the structure stays fixed while energy, |dE|,
|
|
243
|
+
// and the density residuals (incl. OSZICAR's rms(c) charge-density residual)
|
|
244
|
+
// plot per SCF step — the convergence view that matters when watching a
|
|
245
|
+
// running static job. Multi-point plots need >=2 frames, so this also makes
|
|
246
|
+
// single-point files plottable at all.
|
|
247
|
+
let frames_are_scf_steps = false;
|
|
248
|
+
const only_scf = scf_history?.length === 1 ? scf_history[0] : null;
|
|
249
|
+
if (frames.length === 1 && only_scf && only_scf.energies.length >= 2) {
|
|
250
|
+
const base_frame = frames[0];
|
|
251
|
+
frames.length = 0;
|
|
252
|
+
for (const [scf_idx, scf_energy] of only_scf.energies.entries()) {
|
|
253
|
+
const metadata = {
|
|
254
|
+
energy: scf_energy,
|
|
255
|
+
volume: base_frame.metadata?.volume,
|
|
256
|
+
...scf_step_metadata(only_scf, scf_idx),
|
|
257
|
+
};
|
|
258
|
+
frames.push({ structure: base_frame.structure, step: scf_idx, metadata });
|
|
259
|
+
}
|
|
260
|
+
frames_are_scf_steps = true;
|
|
261
|
+
}
|
|
262
|
+
// Trajectory views only ever render the DOS panel, so skip reading the
|
|
263
|
+
// (potentially large) eigenvalue arrays here — bands are read only on the
|
|
264
|
+
// electronic-only paths above, which are the sole consumers.
|
|
265
|
+
const dos = read_vaspout_dos(h5_file);
|
|
266
|
+
return {
|
|
267
|
+
frames,
|
|
268
|
+
metadata: {
|
|
269
|
+
source_format: `vaspout_h5`,
|
|
270
|
+
frame_count: frames.length,
|
|
271
|
+
num_atoms: elements.length,
|
|
272
|
+
element_counts: count_elements(elements),
|
|
273
|
+
periodic_boundary_conditions: [true, true, true],
|
|
274
|
+
has_cell_info: true,
|
|
275
|
+
...(energy_tag ? { energy_tag } : {}),
|
|
276
|
+
...(dropped_steps > 0 ? { dropped_steps } : {}),
|
|
277
|
+
...(frames_are_scf_steps ? { frames_are_scf_steps: true } : {}),
|
|
278
|
+
...(dos ? { electronic: { dos, bands: null } } : {}),
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
export async function parse_vaspout_h5(buffer, filename) {
|
|
283
|
+
return with_h5_file(buffer, filename, parse_vaspout_h5_file);
|
|
284
|
+
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import * as math from '../../math';
|
|
2
|
-
import { coerce_elem_symbol } from '../../element';
|
|
2
|
+
import { coerce_elem_symbol } from '../../element/helpers';
|
|
3
3
|
import { calc_force_stats, create_trajectory_frame, iter_xyz_frames, } from '../helpers';
|
|
4
4
|
import { traj_warn } from './diagnostics';
|
|
5
5
|
// Resolve species/pos/forces column offsets from an extxyz Properties string of
|
|
6
6
|
// name:type:ncols triples (e.g. "species:S:1:pos:R:3:forces:R:3"), falling back
|
|
7
7
|
// to the conventional "symbol x y z" layout when absent or malformed
|
|
8
8
|
function parse_extxyz_columns(comment) {
|
|
9
|
-
const fields = /Properties\s*=\s*"?([^"\s]+)"?/i.exec(comment)?.[1].split(`:`) ?? [];
|
|
9
|
+
const fields = /Properties\s*=\s*"?(?<properties>[^"\s]+)"?/i.exec(comment)?.[1].split(`:`) ?? [];
|
|
10
10
|
// Well-formed Properties is name:type:ncols triples; a non-multiple of 3 is malformed,
|
|
11
11
|
// so bail to the conventional default rather than trusting a partial layout
|
|
12
12
|
let layout = fields.length % 3 === 0 ? {} : null;
|
|
13
13
|
for (let idx = 0, offset = 0; layout && idx + 3 <= fields.length; idx += 3) {
|
|
14
|
-
const ncols =
|
|
14
|
+
const ncols = Math.trunc(Number(fields[idx + 2]));
|
|
15
15
|
if (Number.isInteger(ncols) && ncols > 0) {
|
|
16
16
|
layout[fields[idx].toLowerCase()] = { offset, ncols };
|
|
17
17
|
offset += ncols;
|
|
@@ -26,7 +26,11 @@ function parse_extxyz_columns(comment) {
|
|
|
26
26
|
}
|
|
27
27
|
// Parse Lattice="ax ay az bx by bz cx cy cz" from an extxyz comment line
|
|
28
28
|
function parse_extxyz_lattice(comment) {
|
|
29
|
-
const vals = /Lattice\s*=\s*"([^"]+)"/i
|
|
29
|
+
const vals = /Lattice\s*=\s*"(?<lattice>[^"]+)"/i
|
|
30
|
+
.exec(comment)?.[1]
|
|
31
|
+
.trim()
|
|
32
|
+
.split(/\s+/)
|
|
33
|
+
.map(Number);
|
|
30
34
|
if (vals?.length !== 9 || !vals.every(Number.isFinite))
|
|
31
35
|
return undefined;
|
|
32
36
|
return [vals.slice(0, 3), vals.slice(3, 6), vals.slice(6, 9)];
|
|
@@ -47,10 +51,10 @@ export function parse_xyz_comment_metadata(comment) {
|
|
|
47
51
|
for (const [key, pattern] of Object.entries(METADATA_PATTERNS)) {
|
|
48
52
|
const match = pattern.exec(comment);
|
|
49
53
|
if (match)
|
|
50
|
-
properties[key] =
|
|
54
|
+
properties[key] = Number(match[1]);
|
|
51
55
|
}
|
|
52
|
-
const step = /(?:^|\s)(?:step|frame|ionic_step)\s*[=:]?\s*(
|
|
53
|
-
return { step: step ?
|
|
56
|
+
const step = /(?:^|\s)(?:step|frame|ionic_step)\s*[=:]?\s*(?<step>\d+)/i.exec(comment)?.[1];
|
|
57
|
+
return { step: step ? Math.trunc(Number(step)) : undefined, properties };
|
|
54
58
|
}
|
|
55
59
|
// Parse num_atoms atom lines starting at lines[start], reading species/pos/forces from
|
|
56
60
|
// their Properties-declared column offsets; invalid atoms are skipped with a warning.
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type TrajPropertyConfig } from '../labels';
|
|
2
|
+
import type { DataSeries, ScaleType } from '../plot/core/types';
|
|
2
3
|
import type { TrajectoryDataExtractor, TrajectoryMetadata, TrajectoryType } from './index';
|
|
3
4
|
export interface PlotSeriesOptions {
|
|
4
|
-
property_config?: Record<string,
|
|
5
|
-
label: string;
|
|
6
|
-
unit: string;
|
|
7
|
-
}>;
|
|
5
|
+
property_config?: Record<string, TrajPropertyConfig>;
|
|
8
6
|
colors?: readonly string[];
|
|
9
7
|
default_visible_properties?: Set<string>;
|
|
10
8
|
}
|
|
@@ -14,11 +12,12 @@ export declare function generate_axis_labels(plot_series: DataSeries[]): {
|
|
|
14
12
|
y1: string;
|
|
15
13
|
y2: string;
|
|
16
14
|
};
|
|
15
|
+
export declare function generate_axis_scale_types(plot_series: DataSeries[]): {
|
|
16
|
+
y1: ScaleType;
|
|
17
|
+
y2: ScaleType;
|
|
18
|
+
};
|
|
17
19
|
interface StreamingPlotOptions {
|
|
18
|
-
property_config?: Record<string,
|
|
19
|
-
label: string;
|
|
20
|
-
unit: string;
|
|
21
|
-
}>;
|
|
20
|
+
property_config?: Record<string, TrajPropertyConfig>;
|
|
22
21
|
colors?: readonly string[];
|
|
23
22
|
default_visible_properties?: Set<string>;
|
|
24
23
|
max_points?: number;
|
|
@@ -1,19 +1,51 @@
|
|
|
1
1
|
// Plotting utilities for trajectory visualization
|
|
2
2
|
import { PLOT_COLORS } from '../colors';
|
|
3
|
-
import { trajectory_property_config } from '../labels';
|
|
3
|
+
import { SCF_AXIS_GROUP, trajectory_property_config, } from '../labels';
|
|
4
4
|
import { get_coefficient_of_variation } from '../math';
|
|
5
5
|
// Configuration constants
|
|
6
6
|
const ENERGY_UNITS = [`eV`, `eV/atom`, `hartree`, `kcal/mol`, `kJ/mol`];
|
|
7
7
|
const ENERGY_PROPERTIES = [`energy`, `total_energy`, `potential_energy`];
|
|
8
8
|
const FORCE_PROPERTIES = [`force`, `fmax`, `f`];
|
|
9
|
-
|
|
9
|
+
// scf_energy_delta lives in its own axis group (see trajectory_property_config), so
|
|
10
|
+
// listing it here only surfaces it when higher-priority groups (energy/force/stress)
|
|
11
|
+
// don't fill both axes — i.e. single-point SCF convergence views.
|
|
12
|
+
const DEFAULT_VISIBLE = new Set([
|
|
13
|
+
`energy`,
|
|
14
|
+
`force_max`,
|
|
15
|
+
`stress_frobenius`,
|
|
16
|
+
`scf_energy_delta`,
|
|
17
|
+
]);
|
|
18
|
+
// Shared per-series line/point styling derived from a single color
|
|
19
|
+
const series_color_styles = (color) => ({
|
|
20
|
+
line_style: { stroke: color, stroke_width: 2 },
|
|
21
|
+
point_style: { fill: color, stroke: color, stroke_width: 1 },
|
|
22
|
+
});
|
|
23
|
+
// Cache the per-frame walk per trajectory so re-renders that only change visible_properties or
|
|
24
|
+
// labels/config reuse it (legend toggles mutate plot_series directly and skip regeneration, so they
|
|
25
|
+
// don't hit this). Keyed by trajectory identity (WeakMap auto-evicts) and invalidated on extractor
|
|
26
|
+
// or frame identity changes.
|
|
27
|
+
const same_frames = (cached_frames, current_frames) => cached_frames.length === current_frames.length &&
|
|
28
|
+
cached_frames.every((frame, frame_idx) => frame === current_frames[frame_idx]);
|
|
29
|
+
const stats_cache = new WeakMap();
|
|
10
30
|
// Unified property extraction and series generation
|
|
11
31
|
export function generate_plot_series(trajectory, data_extractor, options = {}) {
|
|
12
32
|
if (!trajectory?.frames?.length)
|
|
13
33
|
return [];
|
|
14
34
|
const { property_config = trajectory_property_config, colors = PLOT_COLORS, default_visible_properties = DEFAULT_VISIBLE, } = options;
|
|
15
|
-
// Single-pass
|
|
16
|
-
const
|
|
35
|
+
// Single-pass extraction with variance detection, cached per trajectory (see stats_cache above)
|
|
36
|
+
const cached = stats_cache.get(trajectory);
|
|
37
|
+
let property_stats;
|
|
38
|
+
if (cached?.extractor === data_extractor && same_frames(cached.frames, trajectory.frames)) {
|
|
39
|
+
property_stats = cached.stats;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
property_stats = extract_property_statistics(trajectory, data_extractor);
|
|
43
|
+
stats_cache.set(trajectory, {
|
|
44
|
+
extractor: data_extractor,
|
|
45
|
+
frames: [...trajectory.frames],
|
|
46
|
+
stats: property_stats,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
17
49
|
// Create all series
|
|
18
50
|
const all_series = create_series_from_stats(property_stats, property_config, colors);
|
|
19
51
|
// Group by units and assign axes/visibility
|
|
@@ -66,22 +98,24 @@ function create_series_from_stats(property_stats, property_config, colors) {
|
|
|
66
98
|
if (!stat)
|
|
67
99
|
continue;
|
|
68
100
|
const n_values = stat.values.length;
|
|
69
|
-
const { clean_label, unit } = extract_label_and_unit(key, property_config);
|
|
101
|
+
const { clean_label, unit, axis_group } = extract_label_and_unit(key, property_config);
|
|
70
102
|
const color = colors[color_idx % colors.length];
|
|
103
|
+
// shared per-series metadata (consumers only read metadata[0]); one object, not n copies
|
|
104
|
+
const series_metadata = {
|
|
105
|
+
series_label: unit ? `${clean_label} (${unit})` : clean_label,
|
|
106
|
+
property_key: key, // Store original property key for robust lookups
|
|
107
|
+
};
|
|
71
108
|
all_series.push({
|
|
72
109
|
x: Array.from({ length: n_values }, (_, idx) => idx),
|
|
73
110
|
y: stat.values,
|
|
74
111
|
label: clean_label,
|
|
75
112
|
unit,
|
|
113
|
+
...(axis_group ? { axis_group } : {}),
|
|
76
114
|
y_axis: `y1`, // Will be reassigned
|
|
77
115
|
visible: false, // Will be assigned
|
|
78
116
|
markers: n_values < 30 ? `line+points` : `line`,
|
|
79
|
-
metadata: Array(n_values)
|
|
80
|
-
|
|
81
|
-
property_key: key, // Store original property key for robust lookups
|
|
82
|
-
}),
|
|
83
|
-
line_style: { stroke: color, stroke_width: 2 },
|
|
84
|
-
point_style: { fill: color, radius: 4, stroke: color, stroke_width: 1 },
|
|
117
|
+
metadata: Array.from({ length: n_values }, () => series_metadata),
|
|
118
|
+
...series_color_styles(color),
|
|
85
119
|
});
|
|
86
120
|
color_idx++;
|
|
87
121
|
}
|
|
@@ -89,10 +123,10 @@ function create_series_from_stats(property_stats, property_config, colors) {
|
|
|
89
123
|
}
|
|
90
124
|
// Group series and assign visibility/axes
|
|
91
125
|
function group_and_assign_series(series, default_visible_properties) {
|
|
92
|
-
// Group by unit
|
|
126
|
+
// Group by axis_group when set (forces a dedicated axis), else by unit
|
|
93
127
|
const unit_map = new Map();
|
|
94
128
|
for (const srs of series) {
|
|
95
|
-
const unit = srs.unit ?? `dimensionless`;
|
|
129
|
+
const unit = srs.axis_group ?? srs.unit ?? `dimensionless`;
|
|
96
130
|
const group = unit_map.get(unit) ?? [];
|
|
97
131
|
group.push(srs);
|
|
98
132
|
unit_map.set(unit, group);
|
|
@@ -101,12 +135,12 @@ function group_and_assign_series(series, default_visible_properties) {
|
|
|
101
135
|
const groups = Array.from(unit_map.entries())
|
|
102
136
|
.map(([unit, group_series]) => {
|
|
103
137
|
const priority = calculate_priority(unit, group_series);
|
|
104
|
-
const
|
|
138
|
+
const is_visible = group_series.some((srs) => {
|
|
105
139
|
const metadata = Array.isArray(srs.metadata) ? srs.metadata[0] : srs.metadata;
|
|
106
140
|
const property_key = (metadata?.property_key || srs.label) ?? ``;
|
|
107
141
|
return is_default_visible(property_key, default_visible_properties);
|
|
108
142
|
});
|
|
109
|
-
return { unit, series: group_series, priority, is_visible
|
|
143
|
+
return { unit, series: group_series, priority, is_visible };
|
|
110
144
|
})
|
|
111
145
|
.sort((a, b) => a.priority - b.priority);
|
|
112
146
|
// Apply 2-group visibility limit
|
|
@@ -146,8 +180,9 @@ function apply_group_assignments(series, unit_groups) {
|
|
|
146
180
|
// Helper functions
|
|
147
181
|
function extract_label_and_unit(key, property_config) {
|
|
148
182
|
const config = property_config[key] || property_config[key.toLowerCase()];
|
|
149
|
-
if (config)
|
|
150
|
-
return { clean_label: config.label, unit: config.unit };
|
|
183
|
+
if (config) {
|
|
184
|
+
return { clean_label: config.label, unit: config.unit, axis_group: config.axis_group };
|
|
185
|
+
}
|
|
151
186
|
return {
|
|
152
187
|
clean_label: key.charAt(0).toUpperCase() + key.slice(1).replaceAll('_', ` `),
|
|
153
188
|
unit: ``,
|
|
@@ -243,14 +278,47 @@ export function generate_axis_labels(plot_series) {
|
|
|
243
278
|
y2: get_axis_label(plot_series.filter((srs) => srs.y_axis === `y2`)),
|
|
244
279
|
};
|
|
245
280
|
}
|
|
281
|
+
// Log-scale heuristic: a y-axis defaults to log scale when every visible series on
|
|
282
|
+
// it is strictly positive AND their combined values span at least this many decades.
|
|
283
|
+
// SCF convergence residuals (|ΔE|, density rms) span 6+ decades and degenerate into
|
|
284
|
+
// hockey sticks on linear axes; plain energies (large negative) stay linear.
|
|
285
|
+
const LOG_SCALE_MIN_DECADE_SPAN = 3;
|
|
286
|
+
const LOG_SCALE_AXIS_GROUPS = new Set([SCF_AXIS_GROUP]);
|
|
287
|
+
export function generate_axis_scale_types(plot_series) {
|
|
288
|
+
const scale_for_axis = (axis) => {
|
|
289
|
+
let min_val = Infinity;
|
|
290
|
+
let max_val = -Infinity;
|
|
291
|
+
for (const srs of plot_series) {
|
|
292
|
+
if (!srs.visible || (srs.y_axis ?? `y1`) !== axis)
|
|
293
|
+
continue;
|
|
294
|
+
if (!srs.axis_group || !LOG_SCALE_AXIS_GROUPS.has(srs.axis_group))
|
|
295
|
+
return `linear`;
|
|
296
|
+
for (const val of srs.y) {
|
|
297
|
+
if (!Number.isFinite(val))
|
|
298
|
+
continue;
|
|
299
|
+
if (val < min_val)
|
|
300
|
+
min_val = val;
|
|
301
|
+
if (val > max_val)
|
|
302
|
+
max_val = val;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (!Number.isFinite(min_val) || min_val <= 0)
|
|
306
|
+
return `linear`;
|
|
307
|
+
return max_val / min_val >= 10 ** LOG_SCALE_MIN_DECADE_SPAN ? `log` : `linear`;
|
|
308
|
+
};
|
|
309
|
+
return { y1: scale_for_axis(`y1`), y2: scale_for_axis(`y2`) };
|
|
310
|
+
}
|
|
246
311
|
export function generate_streaming_plot_series(metadata_list, options = {}) {
|
|
247
312
|
if (metadata_list.length === 0)
|
|
248
313
|
return [];
|
|
249
314
|
const { property_config = trajectory_property_config, colors = PLOT_COLORS, default_visible_properties = DEFAULT_VISIBLE, max_points = 10_000, // 10,000 plot points provides good visual fidelity while maintaining browser performance
|
|
250
315
|
} = options;
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
316
|
+
const ordered_metadata = [...metadata_list]
|
|
317
|
+
.sort((metadata_a, metadata_b) => metadata_a.frame_number - metadata_b.frame_number)
|
|
318
|
+
.filter((metadata, idx, sorted_metadata) => idx === 0 || metadata.frame_number !== sorted_metadata[idx - 1].frame_number);
|
|
319
|
+
const sampled_metadata = ordered_metadata.length > max_points
|
|
320
|
+
? downsample_metadata(ordered_metadata, max_points)
|
|
321
|
+
: ordered_metadata;
|
|
254
322
|
const all_properties = new Set();
|
|
255
323
|
sampled_metadata.forEach((metadata) => {
|
|
256
324
|
Object.keys(metadata.properties).forEach((prop) => all_properties.add(prop));
|
|
@@ -261,22 +329,26 @@ export function generate_streaming_plot_series(metadata_list, options = {}) {
|
|
|
261
329
|
for (const property_key of all_properties) {
|
|
262
330
|
const data_points = sampled_metadata
|
|
263
331
|
.filter((metadata) => property_key in metadata.properties)
|
|
264
|
-
.map((metadata) => ({
|
|
332
|
+
.map((metadata) => ({
|
|
333
|
+
x: metadata.frame_number,
|
|
334
|
+
y: metadata.properties[property_key],
|
|
335
|
+
}));
|
|
265
336
|
if (data_points.length < 2)
|
|
266
337
|
continue;
|
|
267
338
|
const is_energy = property_key.toLowerCase() === `energy`;
|
|
268
339
|
if (!is_energy && !has_significant_variation(data_points.map((point) => point.y)))
|
|
269
340
|
continue;
|
|
270
|
-
const { clean_label, unit } = extract_label_and_unit(property_key, property_config);
|
|
341
|
+
const { clean_label, unit, axis_group } = extract_label_and_unit(property_key, property_config);
|
|
271
342
|
const is_visible = is_default_visible(property_key, default_visible_properties) || color_idx < 2;
|
|
272
343
|
const color = colors[color_idx % colors.length];
|
|
273
344
|
if (is_visible)
|
|
274
|
-
visible_props.push({ property: property_key, unit });
|
|
345
|
+
visible_props.push({ property: property_key, unit, axis_group });
|
|
275
346
|
all_series.push({
|
|
276
347
|
x: data_points.map((point) => point.x),
|
|
277
348
|
y: data_points.map((point) => point.y),
|
|
278
349
|
label: clean_label,
|
|
279
350
|
unit,
|
|
351
|
+
...(axis_group ? { axis_group } : {}),
|
|
280
352
|
y_axis: determine_axis_from_groups(property_key, unit, visible_props),
|
|
281
353
|
visible: is_visible,
|
|
282
354
|
markers: data_points.length < 1000 ? `line+points` : `line`,
|
|
@@ -284,8 +356,7 @@ export function generate_streaming_plot_series(metadata_list, options = {}) {
|
|
|
284
356
|
series_label: unit ? `${clean_label} (${unit})` : clean_label,
|
|
285
357
|
property_key, // Store original property key for robust lookups
|
|
286
358
|
})),
|
|
287
|
-
|
|
288
|
-
point_style: { fill: color, radius: 4, stroke: color, stroke_width: 1 },
|
|
359
|
+
...series_color_styles(color),
|
|
289
360
|
});
|
|
290
361
|
color_idx++;
|
|
291
362
|
}
|
|
@@ -314,9 +385,10 @@ function has_significant_variation(values, tolerance = 1e-6) {
|
|
|
314
385
|
return coefficient_of_variation >= tolerance;
|
|
315
386
|
}
|
|
316
387
|
function determine_axis_from_groups(property, unit, visible_properties) {
|
|
317
|
-
const mock_series = visible_properties.map(({ property: prop, unit:
|
|
388
|
+
const mock_series = visible_properties.map(({ property: prop, unit: prop_unit, axis_group }) => ({
|
|
318
389
|
label: prop,
|
|
319
|
-
unit:
|
|
390
|
+
unit: prop_unit,
|
|
391
|
+
...(axis_group ? { axis_group } : {}),
|
|
320
392
|
}));
|
|
321
393
|
const groups = group_and_assign_series(mock_series, new Set([property]));
|
|
322
394
|
const target_group = groups.find((group) => group.series.some((srs) => srs.label === property && srs.unit === unit));
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
export declare const is_plain_object: (val: unknown) => val is Record<string, unknown>;
|
|
2
|
+
export declare const clamp01: (value: number) => number;
|
|
2
3
|
export declare function merge_nested<T extends Record<string, unknown>>(obj1: T, obj2?: Partial<T>): T;
|
|
3
4
|
export declare const escape_html: (unsafe_string: string) => string;
|
|
4
5
|
export declare const normalize_unicode_minus: (value: string) => string;
|
|
5
6
|
export declare const normalize_scientific_notation: (value: string) => string;
|
|
7
|
+
export declare const parse_num_token: (token: string) => number;
|
|
8
|
+
export declare const parse_leading_num: (line: string) => number;
|
|
6
9
|
export declare const to_error: (value: unknown) => Error;
|
|
7
10
|
export declare function make_change_detector(): (value: unknown) => boolean;
|
|
8
11
|
export declare function decode_url_safe_base64(encoded: string): string | undefined;
|
package/dist/utils.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// Check if value is a plain object (not array, null, or other types)
|
|
2
2
|
export const is_plain_object = (val) => typeof val === `object` && val !== null && !Array.isArray(val);
|
|
3
|
+
// Clamp a number to the [0, 1] range.
|
|
4
|
+
export const clamp01 = (value) => Math.max(0, Math.min(1, value));
|
|
3
5
|
// Merge nested objects (1 level deep).
|
|
4
6
|
export function merge_nested(obj1, obj2) {
|
|
5
7
|
const result = { ...obj1, ...obj2 };
|
|
@@ -21,6 +23,11 @@ export const escape_html = (unsafe_string) => unsafe_string
|
|
|
21
23
|
export const normalize_unicode_minus = (value) => value.replaceAll('−', `-`);
|
|
22
24
|
// Normalize scientific notation variants (d/D exponent, Mathematica *^).
|
|
23
25
|
export const normalize_scientific_notation = (value) => normalize_unicode_minus(value).toLowerCase().replaceAll('d', `e`).replaceAll('*^', `e`);
|
|
26
|
+
// Number(token) that treats blank strings as NaN, not 0 like Number(``) does
|
|
27
|
+
export const parse_num_token = (token) => token.trim() === `` ? NaN : Number(token);
|
|
28
|
+
// Parse a line's first whitespace-separated token: tolerates trailing
|
|
29
|
+
// tokens/comments (`1.0 ! scale` -> 1.0) like parseFloat, blank lines are NaN
|
|
30
|
+
export const parse_leading_num = (line) => parse_num_token(line.trim().split(/\s+/)[0]);
|
|
24
31
|
// Coerce an unknown thrown value into an Error (for typed Promise rejections / error callbacks).
|
|
25
32
|
export const to_error = (value) => value instanceof Error ? value : new Error(String(value));
|
|
26
33
|
export function make_change_detector() {
|