matterviz 0.3.4 → 0.3.6
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/.vscode/launch.json +13 -0
- package/.vscodeignore +7 -0
- package/dist/assets/STLExporter-BpTH3YHE.js +8 -0
- package/dist/assets/browser-DdDecX_W.js +1 -0
- package/dist/assets/export-qgn-H9y6.js +2 -0
- package/dist/assets/main-DiKYzti2.css +1 -0
- package/dist/assets/moyo_wasm_bg-0ocwg7xY.wasm +0 -0
- package/dist/extension.js +31293 -0
- package/dist/src/lib/FilePicker.svelte +360 -0
- package/dist/src/lib/MillerIndexInput.svelte +66 -0
- package/dist/src/lib/api/mp.ts +26 -0
- package/dist/src/lib/api/optimade.ts +204 -0
- package/dist/src/lib/app.css +247 -0
- package/dist/src/lib/brillouin/BrillouinZone.svelte +549 -0
- package/dist/src/lib/brillouin/BrillouinZoneControls.svelte +144 -0
- package/dist/src/lib/brillouin/BrillouinZoneExportPane.svelte +146 -0
- package/dist/src/lib/brillouin/BrillouinZoneInfoPane.svelte +146 -0
- package/dist/src/lib/brillouin/BrillouinZoneScene.svelte +476 -0
- package/dist/src/lib/brillouin/BrillouinZoneTooltip.svelte +92 -0
- package/dist/src/lib/brillouin/compute.ts +529 -0
- package/dist/src/lib/brillouin/index.ts +8 -0
- package/dist/src/lib/brillouin/types.ts +51 -0
- package/dist/src/lib/chempot-diagram/ChemPotDiagram.svelte +327 -0
- package/dist/src/lib/chempot-diagram/ChemPotDiagram2D.svelte +846 -0
- package/dist/src/lib/chempot-diagram/ChemPotDiagram3D.svelte +3193 -0
- package/dist/src/lib/chempot-diagram/async-compute.svelte.ts +94 -0
- package/dist/src/lib/chempot-diagram/chempot-worker.ts +11 -0
- package/dist/src/lib/chempot-diagram/color.ts +42 -0
- package/dist/src/lib/chempot-diagram/compute.ts +1014 -0
- package/dist/src/lib/chempot-diagram/index.ts +6 -0
- package/dist/src/lib/chempot-diagram/pointer.ts +56 -0
- package/dist/src/lib/chempot-diagram/temperature.ts +77 -0
- package/dist/src/lib/chempot-diagram/types.ts +130 -0
- package/dist/src/lib/colors/index.ts +249 -0
- package/dist/src/lib/composition/BarChart.svelte +297 -0
- package/dist/src/lib/composition/BubbleChart.svelte +218 -0
- package/dist/src/lib/composition/Composition.svelte +165 -0
- package/dist/src/lib/composition/Formula.svelte +268 -0
- package/dist/src/lib/composition/FormulaFilter.svelte +1257 -0
- package/dist/src/lib/composition/PieChart.svelte +323 -0
- package/dist/src/lib/composition/format.ts +155 -0
- package/dist/src/lib/composition/index.ts +37 -0
- package/dist/src/lib/composition/parse.ts +605 -0
- package/dist/src/lib/constants.ts +134 -0
- package/dist/src/lib/controls.ts +42 -0
- package/dist/src/lib/convex-hull/ConvexHull.svelte +157 -0
- package/dist/src/lib/convex-hull/ConvexHull2D.svelte +825 -0
- package/dist/src/lib/convex-hull/ConvexHull3D.svelte +1801 -0
- package/dist/src/lib/convex-hull/ConvexHull4D.svelte +1398 -0
- package/dist/src/lib/convex-hull/ConvexHullControls.svelte +535 -0
- package/dist/src/lib/convex-hull/ConvexHullInfoPane.svelte +125 -0
- package/dist/src/lib/convex-hull/ConvexHullStats.svelte +929 -0
- package/dist/src/lib/convex-hull/ConvexHullTooltip.svelte +131 -0
- package/dist/src/lib/convex-hull/GasPressureControls.svelte +247 -0
- package/dist/src/lib/convex-hull/StructurePopup.svelte +151 -0
- package/dist/src/lib/convex-hull/TemperatureSlider.svelte +140 -0
- package/dist/src/lib/convex-hull/barycentric-coords.ts +246 -0
- package/dist/src/lib/convex-hull/demo-temperature.ts +63 -0
- package/dist/src/lib/convex-hull/gas-thermodynamics.ts +405 -0
- package/dist/src/lib/convex-hull/helpers.ts +932 -0
- package/dist/src/lib/convex-hull/index.ts +202 -0
- package/dist/src/lib/convex-hull/thermodynamics.ts +2192 -0
- package/dist/src/lib/convex-hull/types.ts +267 -0
- package/dist/src/lib/coordination/CoordinationBarPlot.svelte +311 -0
- package/dist/src/lib/coordination/calc-coordination.ts +93 -0
- package/dist/src/lib/coordination/index.ts +9 -0
- package/dist/src/lib/effects.svelte.ts +48 -0
- package/dist/src/lib/element/BohrAtom.svelte +147 -0
- package/dist/src/lib/element/ElementHeading.svelte +26 -0
- package/dist/src/lib/element/ElementPhoto.svelte +57 -0
- package/dist/src/lib/element/ElementStats.svelte +80 -0
- package/dist/src/lib/element/ElementTile.svelte +484 -0
- package/dist/src/lib/element/data.json.gz.d.ts +4 -0
- package/dist/src/lib/element/data.ts +14 -0
- package/dist/src/lib/element/index.ts +8 -0
- package/dist/src/lib/element/types.ts +62 -0
- package/dist/src/lib/feedback/ClickFeedback.svelte +58 -0
- package/dist/src/lib/feedback/DragOverlay.svelte +42 -0
- package/dist/src/lib/feedback/index.ts +4 -0
- package/dist/src/lib/fermi-surface/FermiSlice.svelte +189 -0
- package/dist/src/lib/fermi-surface/FermiSurface.svelte +600 -0
- package/dist/src/lib/fermi-surface/FermiSurfaceControls.svelte +448 -0
- package/dist/src/lib/fermi-surface/FermiSurfaceScene.svelte +794 -0
- package/dist/src/lib/fermi-surface/FermiSurfaceTooltip.svelte +111 -0
- package/dist/src/lib/fermi-surface/compute.ts +728 -0
- package/dist/src/lib/fermi-surface/constants.ts +32 -0
- package/dist/src/lib/fermi-surface/export.ts +64 -0
- package/dist/src/lib/fermi-surface/index.ts +14 -0
- package/dist/src/lib/fermi-surface/marching-cubes.ts +3 -0
- package/dist/src/lib/fermi-surface/parse.ts +574 -0
- package/dist/src/lib/fermi-surface/symmetry.ts +56 -0
- package/dist/src/lib/fermi-surface/types.ts +159 -0
- package/dist/src/lib/heatmap-matrix/HeatmapMatrix.svelte +1545 -0
- package/dist/src/lib/heatmap-matrix/HeatmapMatrixControls.svelte +225 -0
- package/dist/src/lib/heatmap-matrix/index.ts +167 -0
- package/dist/src/lib/heatmap-matrix/shared.ts +7 -0
- package/dist/src/lib/icons.ts +650 -0
- package/dist/src/lib/index.ts +61 -0
- package/dist/src/lib/io/decompress.ts +92 -0
- package/dist/src/lib/io/export.ts +385 -0
- package/dist/src/lib/io/fetch.ts +46 -0
- package/dist/src/lib/io/file-drop.ts +51 -0
- package/dist/src/lib/io/index.ts +7 -0
- package/dist/src/lib/io/is-binary.ts +24 -0
- package/dist/src/lib/io/types.ts +8 -0
- package/dist/src/lib/io/url-drop.ts +141 -0
- package/dist/src/lib/isosurface/Isosurface.svelte +285 -0
- package/dist/src/lib/isosurface/IsosurfaceControls.svelte +277 -0
- package/dist/src/lib/isosurface/index.ts +7 -0
- package/dist/src/lib/isosurface/parse.ts +656 -0
- package/dist/src/lib/isosurface/slice.ts +175 -0
- package/dist/src/lib/isosurface/types.ts +309 -0
- package/dist/src/lib/labels.ts +320 -0
- package/dist/src/lib/layout/FullscreenToggle.svelte +50 -0
- package/dist/src/lib/layout/InfoCard.svelte +120 -0
- package/dist/src/lib/layout/InfoTag.svelte +185 -0
- package/dist/src/lib/layout/PropertyFilter.svelte +246 -0
- package/dist/src/lib/layout/SettingsSection.svelte +148 -0
- package/dist/src/lib/layout/SubpageGrid.svelte +82 -0
- package/dist/src/lib/layout/fullscreen.ts +65 -0
- package/dist/src/lib/layout/index.ts +11 -0
- package/dist/src/lib/layout/json-tree/JsonNode.svelte +548 -0
- package/dist/src/lib/layout/json-tree/JsonTree.svelte +1230 -0
- package/dist/src/lib/layout/json-tree/JsonValue.svelte +334 -0
- package/dist/src/lib/layout/json-tree/index.ts +3 -0
- package/dist/src/lib/layout/json-tree/types.ts +126 -0
- package/dist/src/lib/layout/json-tree/utils.ts +682 -0
- package/dist/src/lib/marching-cubes.ts +614 -0
- package/dist/src/lib/math.ts +1081 -0
- package/dist/src/lib/overlays/ContextMenu.svelte +162 -0
- package/dist/src/lib/overlays/CopyButton.svelte +45 -0
- package/dist/src/lib/overlays/DragControlTab.svelte +98 -0
- package/dist/src/lib/overlays/DraggablePane.svelte +487 -0
- package/dist/src/lib/overlays/InfoPaneCards.svelte +149 -0
- package/dist/src/lib/overlays/index.ts +3 -0
- package/dist/src/lib/periodic-table/PeriodicTable.svelte +469 -0
- package/dist/src/lib/periodic-table/PeriodicTableControls.svelte +557 -0
- package/dist/src/lib/periodic-table/PropertySelect.svelte +37 -0
- package/dist/src/lib/periodic-table/index.ts +12 -0
- package/dist/src/lib/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +1086 -0
- package/dist/src/lib/phase-diagram/PhaseDiagramControls.svelte +444 -0
- package/dist/src/lib/phase-diagram/PhaseDiagramEditorPane.svelte +126 -0
- package/dist/src/lib/phase-diagram/PhaseDiagramExportPane.svelte +184 -0
- package/dist/src/lib/phase-diagram/PhaseDiagramTooltip.svelte +391 -0
- package/dist/src/lib/phase-diagram/TdbInfoPanel.svelte +203 -0
- package/dist/src/lib/phase-diagram/build-diagram.ts +186 -0
- package/dist/src/lib/phase-diagram/colors.ts +58 -0
- package/dist/src/lib/phase-diagram/diagram-input.ts +40 -0
- package/dist/src/lib/phase-diagram/index.ts +13 -0
- package/dist/src/lib/phase-diagram/parse.ts +348 -0
- package/dist/src/lib/phase-diagram/svg-to-diagram.ts +1023 -0
- package/dist/src/lib/phase-diagram/types.ts +144 -0
- package/dist/src/lib/phase-diagram/utils.ts +775 -0
- package/dist/src/lib/plot/AxisLabel.svelte +51 -0
- package/dist/src/lib/plot/BarPlot.svelte +2113 -0
- package/dist/src/lib/plot/BarPlotControls.svelte +66 -0
- package/dist/src/lib/plot/BinnedScatterPlot.svelte +1114 -0
- package/dist/src/lib/plot/ColorBar.svelte +721 -0
- package/dist/src/lib/plot/ColorScaleSelect.svelte +54 -0
- package/dist/src/lib/plot/ElementScatter.svelte +63 -0
- package/dist/src/lib/plot/FillArea.svelte +223 -0
- package/dist/src/lib/plot/Histogram.svelte +1558 -0
- package/dist/src/lib/plot/HistogramControls.svelte +212 -0
- package/dist/src/lib/plot/InteractiveAxisLabel.svelte +96 -0
- package/dist/src/lib/plot/Line.svelte +84 -0
- package/dist/src/lib/plot/PlotAxis.svelte +169 -0
- package/dist/src/lib/plot/PlotControls.svelte +537 -0
- package/dist/src/lib/plot/PlotLegend.svelte +569 -0
- package/dist/src/lib/plot/PlotTooltip.svelte +67 -0
- package/dist/src/lib/plot/PortalSelect.svelte +253 -0
- package/dist/src/lib/plot/ReferenceLine3D.svelte +156 -0
- package/dist/src/lib/plot/ReferencePlane.svelte +175 -0
- package/dist/src/lib/plot/ScatterPlot.svelte +2778 -0
- package/dist/src/lib/plot/ScatterPlot3D.svelte +529 -0
- package/dist/src/lib/plot/ScatterPlot3DControls.svelte +437 -0
- package/dist/src/lib/plot/ScatterPlot3DScene.svelte +912 -0
- package/dist/src/lib/plot/ScatterPlotControls.svelte +306 -0
- package/dist/src/lib/plot/ScatterPoint.svelte +182 -0
- package/dist/src/lib/plot/SpacegroupBarPlot.svelte +293 -0
- package/dist/src/lib/plot/Surface3D.svelte +197 -0
- package/dist/src/lib/plot/ZeroLines.svelte +97 -0
- package/dist/src/lib/plot/ZoomRect.svelte +23 -0
- package/dist/src/lib/plot/adaptive-density.ts +316 -0
- package/dist/src/lib/plot/auto-place.ts +184 -0
- package/dist/src/lib/plot/axis-utils.ts +122 -0
- package/dist/src/lib/plot/binned-scatter-types.ts +83 -0
- package/dist/src/lib/plot/data-cleaning.ts +1069 -0
- package/dist/src/lib/plot/data-transform.ts +69 -0
- package/dist/src/lib/plot/defaults.ts +9 -0
- package/dist/src/lib/plot/fill-utils.ts +494 -0
- package/dist/src/lib/plot/hover-lock.svelte.ts +60 -0
- package/dist/src/lib/plot/index.ts +53 -0
- package/dist/src/lib/plot/interactions.ts +119 -0
- package/dist/src/lib/plot/layout.ts +425 -0
- package/dist/src/lib/plot/reference-line.ts +426 -0
- package/dist/src/lib/plot/scales.ts +654 -0
- package/dist/src/lib/plot/svg.ts +23 -0
- package/dist/src/lib/plot/types.ts +1144 -0
- package/dist/src/lib/plot/utils/label-placement.ts +541 -0
- package/dist/src/lib/plot/utils/series-visibility.ts +140 -0
- package/dist/src/lib/plot/utils.ts +11 -0
- package/dist/src/lib/rdf/RdfPlot.svelte +247 -0
- package/dist/src/lib/rdf/calc-rdf.ts +167 -0
- package/dist/src/lib/rdf/index.ts +27 -0
- package/dist/src/lib/sanitize.ts +126 -0
- package/dist/src/lib/settings.ts +1479 -0
- package/dist/src/lib/spectral/Bands.svelte +1040 -0
- package/dist/src/lib/spectral/BandsAndDos.svelte +134 -0
- package/dist/src/lib/spectral/BrillouinBandsDos.svelte +252 -0
- package/dist/src/lib/spectral/Dos.svelte +697 -0
- package/dist/src/lib/spectral/helpers.ts +1381 -0
- package/dist/src/lib/spectral/index.ts +8 -0
- package/dist/src/lib/spectral/types.ts +112 -0
- package/dist/src/lib/state.svelte.ts +64 -0
- package/dist/src/lib/structure/Arrow.svelte +72 -0
- package/dist/src/lib/structure/AtomLegend.svelte +815 -0
- package/dist/src/lib/structure/Bond.svelte +140 -0
- package/dist/src/lib/structure/CanvasTooltip.svelte +33 -0
- package/dist/src/lib/structure/CellSelect.svelte +349 -0
- package/dist/src/lib/structure/Cylinder.svelte +45 -0
- package/dist/src/lib/structure/Lattice.svelte +196 -0
- package/dist/src/lib/structure/Structure.svelte +2248 -0
- package/dist/src/lib/structure/StructureControls.svelte +1273 -0
- package/dist/src/lib/structure/StructureExportPane.svelte +252 -0
- package/dist/src/lib/structure/StructureInfoPane.svelte +737 -0
- package/dist/src/lib/structure/StructureScene.svelte +2255 -0
- package/dist/src/lib/structure/atom-properties.ts +316 -0
- package/dist/src/lib/structure/bond-order-perception.ts +447 -0
- package/dist/src/lib/structure/bonding.ts +944 -0
- package/dist/src/lib/structure/export.ts +861 -0
- package/dist/src/lib/structure/index.ts +291 -0
- package/dist/src/lib/structure/label-placement.ts +130 -0
- package/dist/src/lib/structure/measure.ts +45 -0
- package/dist/src/lib/structure/parse.ts +1705 -0
- package/dist/src/lib/structure/partial-occupancy.ts +183 -0
- package/dist/src/lib/structure/pbc.ts +164 -0
- package/dist/src/lib/structure/supercell.ts +226 -0
- package/dist/src/lib/structure/validation.ts +11 -0
- package/dist/src/lib/symmetry/SymmetryStats.svelte +226 -0
- package/dist/src/lib/symmetry/WyckoffTable.svelte +120 -0
- package/dist/src/lib/symmetry/cell-transform.ts +118 -0
- package/dist/src/lib/symmetry/index.ts +348 -0
- package/dist/src/lib/symmetry/spacegroups.ts +404 -0
- package/dist/src/lib/table/HeatmapTable.svelte +1833 -0
- package/dist/src/lib/table/ToggleMenu.svelte +385 -0
- package/dist/src/lib/table/index.ts +139 -0
- package/dist/src/lib/theme/ThemeControl.svelte +53 -0
- package/dist/src/lib/theme/index.ts +107 -0
- package/dist/src/lib/theme/themes.mjs +297 -0
- package/dist/src/lib/time.ts +71 -0
- package/dist/src/lib/tooltip/TooltipContent.svelte +58 -0
- package/dist/src/lib/tooltip/index.ts +2 -0
- package/dist/src/lib/tooltip/types.ts +13 -0
- package/dist/src/lib/trajectory/Trajectory.svelte +1545 -0
- package/dist/src/lib/trajectory/TrajectoryError.svelte +128 -0
- package/dist/src/lib/trajectory/TrajectoryExportPane.svelte +357 -0
- package/dist/src/lib/trajectory/TrajectoryInfoPane.svelte +313 -0
- package/dist/src/lib/trajectory/constants.ts +7 -0
- package/dist/src/lib/trajectory/extract.ts +196 -0
- package/dist/src/lib/trajectory/format-detect.ts +96 -0
- package/dist/src/lib/trajectory/frame-reader.ts +456 -0
- package/dist/src/lib/trajectory/helpers.ts +217 -0
- package/dist/src/lib/trajectory/index.ts +218 -0
- package/dist/src/lib/trajectory/parse/ase.ts +109 -0
- package/dist/src/lib/trajectory/parse/hdf5.ts +173 -0
- package/dist/src/lib/trajectory/parse/index.ts +411 -0
- package/dist/src/lib/trajectory/parse/lammps.ts +215 -0
- package/dist/src/lib/trajectory/parse/vasp.ts +102 -0
- package/dist/src/lib/trajectory/parse/xyz.ts +143 -0
- package/dist/src/lib/trajectory/plotting.ts +599 -0
- package/dist/src/lib/trajectory/types.ts +13 -0
- package/dist/src/lib/utils.ts +56 -0
- package/dist/src/lib/xrd/XrdPlot.svelte +615 -0
- package/dist/src/lib/xrd/broadening.ts +130 -0
- package/dist/src/lib/xrd/calc-xrd.ts +397 -0
- package/dist/src/lib/xrd/index.ts +38 -0
- package/dist/src/lib/xrd/parse.ts +858 -0
- package/dist/webview.js +29421 -0
- package/icon.png +0 -0
- package/license +1 -1
- package/matterviz-0.3.2.vsix +0 -0
- package/matterviz-0.3.4.vsix +0 -0
- package/matterviz-0.3.5.vsix +0 -0
- package/package.json +1461 -231
- package/readme.md +171 -98
- package/scripts/sync-config.ts +101 -0
- package/src/declarations.d.ts +2 -0
- package/src/extension.ts +972 -0
- package/src/node-io.ts +65 -0
- package/src/types.ts +17 -0
- package/src/webview/JsonBrowser.svelte +1079 -0
- package/src/webview/PlotPanel.svelte +346 -0
- package/src/webview/detect.ts +444 -0
- package/src/webview/main.ts +764 -0
- package/src/webview/plot-utils.ts +250 -0
- package/test-fixtures/all-viz-types.json.gz +0 -0
- package/test-fixtures/plot-demo-data.json.gz +0 -0
- package/tests/detect.test.ts +604 -0
- package/tests/extension.test.ts +2041 -0
- package/tests/node-io.test.ts +39 -0
- package/tests/plot-utils.test.ts +302 -0
- package/tests/vite-plugin-json-gz.test.ts +114 -0
- package/tests/vscode-mock.ts +18 -0
- package/tests/webview.test.ts +231 -0
- package/tsconfig.json +20 -0
- package/vite-plugin-json-gz.ts +29 -0
- package/vite.config.ts +34 -0
- package/vite.extension.config.ts +34 -0
- package/dist/EmptyState.svelte.d.ts +0 -9
- package/dist/FilePicker.svelte +0 -360
- package/dist/FilePicker.svelte.d.ts +0 -17
- package/dist/Icon.svelte.d.ts +0 -13
- package/dist/MillerIndexInput.svelte +0 -66
- package/dist/MillerIndexInput.svelte.d.ts +0 -7
- package/dist/api/mp.d.ts +0 -6
- package/dist/api/mp.js +0 -22
- package/dist/api/optimade.d.ts +0 -45
- package/dist/api/optimade.js +0 -135
- package/dist/app.css +0 -240
- package/dist/brillouin/BrillouinZone.svelte +0 -543
- package/dist/brillouin/BrillouinZone.svelte.d.ts +0 -83
- package/dist/brillouin/BrillouinZoneControls.svelte +0 -144
- package/dist/brillouin/BrillouinZoneControls.svelte.d.ts +0 -17
- package/dist/brillouin/BrillouinZoneExportPane.svelte +0 -148
- package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +0 -15
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +0 -146
- package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +0 -13
- package/dist/brillouin/BrillouinZoneScene.svelte +0 -476
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +0 -48
- package/dist/brillouin/BrillouinZoneTooltip.svelte +0 -92
- package/dist/brillouin/BrillouinZoneTooltip.svelte.d.ts +0 -8
- package/dist/brillouin/compute.d.ts +0 -17
- package/dist/brillouin/compute.js +0 -422
- package/dist/brillouin/index.d.ts +0 -8
- package/dist/brillouin/index.js +0 -8
- package/dist/brillouin/types.d.ts +0 -48
- package/dist/brillouin/types.js +0 -1
- package/dist/chempot-diagram/ChemPotDiagram.svelte +0 -327
- package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +0 -13
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +0 -847
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +0 -16
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +0 -3194
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +0 -16
- package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +0 -7
- package/dist/chempot-diagram/async-compute.svelte.d.ts +0 -3
- package/dist/chempot-diagram/async-compute.svelte.js +0 -77
- package/dist/chempot-diagram/chempot-worker.d.ts +0 -1
- package/dist/chempot-diagram/chempot-worker.js +0 -11
- package/dist/chempot-diagram/color.d.ts +0 -10
- package/dist/chempot-diagram/color.js +0 -32
- package/dist/chempot-diagram/compute.d.ts +0 -48
- package/dist/chempot-diagram/compute.js +0 -812
- package/dist/chempot-diagram/index.d.ts +0 -6
- package/dist/chempot-diagram/index.js +0 -6
- package/dist/chempot-diagram/pointer.d.ts +0 -16
- package/dist/chempot-diagram/pointer.js +0 -40
- package/dist/chempot-diagram/temperature.d.ts +0 -15
- package/dist/chempot-diagram/temperature.js +0 -36
- package/dist/chempot-diagram/types.d.ts +0 -86
- package/dist/chempot-diagram/types.js +0 -28
- package/dist/colors/index.d.ts +0 -47
- package/dist/colors/index.js +0 -203
- package/dist/composition/BarChart.svelte +0 -297
- package/dist/composition/BarChart.svelte.d.ts +0 -39
- package/dist/composition/BubbleChart.svelte +0 -218
- package/dist/composition/BubbleChart.svelte.d.ts +0 -28
- package/dist/composition/Composition.svelte +0 -164
- package/dist/composition/Composition.svelte.d.ts +0 -15
- package/dist/composition/Formula.svelte +0 -265
- package/dist/composition/Formula.svelte.d.ts +0 -19
- package/dist/composition/FormulaFilter.svelte +0 -1259
- package/dist/composition/FormulaFilter.svelte.d.ts +0 -51
- package/dist/composition/PieChart.svelte +0 -323
- package/dist/composition/PieChart.svelte.d.ts +0 -37
- package/dist/composition/format.d.ts +0 -15
- package/dist/composition/format.js +0 -109
- package/dist/composition/index.d.ts +0 -20
- package/dist/composition/index.js +0 -14
- package/dist/composition/parse.d.ts +0 -55
- package/dist/composition/parse.js +0 -459
- package/dist/constants.d.ts +0 -29
- package/dist/constants.js +0 -105
- package/dist/controls.d.ts +0 -14
- package/dist/controls.js +0 -30
- package/dist/convex-hull/ConvexHull.svelte +0 -157
- package/dist/convex-hull/ConvexHull.svelte.d.ts +0 -13
- package/dist/convex-hull/ConvexHull2D.svelte +0 -813
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +0 -11
- package/dist/convex-hull/ConvexHull3D.svelte +0 -1788
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +0 -8
- package/dist/convex-hull/ConvexHull4D.svelte +0 -1374
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +0 -8
- package/dist/convex-hull/ConvexHullControls.svelte +0 -546
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +0 -48
- package/dist/convex-hull/ConvexHullInfoPane.svelte +0 -115
- package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +0 -18
- package/dist/convex-hull/ConvexHullStats.svelte +0 -905
- package/dist/convex-hull/ConvexHullStats.svelte.d.ts +0 -15
- package/dist/convex-hull/ConvexHullTooltip.svelte +0 -131
- package/dist/convex-hull/ConvexHullTooltip.svelte.d.ts +0 -33
- package/dist/convex-hull/GasPressureControls.svelte +0 -247
- package/dist/convex-hull/GasPressureControls.svelte.d.ts +0 -11
- package/dist/convex-hull/StructurePopup.svelte +0 -116
- package/dist/convex-hull/StructurePopup.svelte.d.ts +0 -18
- package/dist/convex-hull/TemperatureSlider.svelte +0 -137
- package/dist/convex-hull/TemperatureSlider.svelte.d.ts +0 -8
- package/dist/convex-hull/barycentric-coords.d.ts +0 -18
- package/dist/convex-hull/barycentric-coords.js +0 -182
- package/dist/convex-hull/demo-temperature.d.ts +0 -6
- package/dist/convex-hull/demo-temperature.js +0 -40
- package/dist/convex-hull/gas-thermodynamics.d.ts +0 -16
- package/dist/convex-hull/gas-thermodynamics.js +0 -316
- package/dist/convex-hull/helpers.d.ts +0 -103
- package/dist/convex-hull/helpers.js +0 -671
- package/dist/convex-hull/index.d.ts +0 -118
- package/dist/convex-hull/index.js +0 -57
- package/dist/convex-hull/thermodynamics.d.ts +0 -66
- package/dist/convex-hull/thermodynamics.js +0 -1752
- package/dist/convex-hull/types.d.ts +0 -162
- package/dist/convex-hull/types.js +0 -36
- package/dist/coordination/CoordinationBarPlot.svelte +0 -311
- package/dist/coordination/CoordinationBarPlot.svelte.d.ts +0 -30
- package/dist/coordination/calc-coordination.d.ts +0 -15
- package/dist/coordination/calc-coordination.js +0 -63
- package/dist/coordination/index.d.ts +0 -8
- package/dist/coordination/index.js +0 -7
- package/dist/element/BohrAtom.svelte +0 -149
- package/dist/element/BohrAtom.svelte.d.ts +0 -20
- package/dist/element/ElementHeading.svelte +0 -26
- package/dist/element/ElementHeading.svelte.d.ts +0 -8
- package/dist/element/ElementPhoto.svelte +0 -57
- package/dist/element/ElementPhoto.svelte.d.ts +0 -9
- package/dist/element/ElementStats.svelte +0 -80
- package/dist/element/ElementStats.svelte.d.ts +0 -8
- package/dist/element/ElementTile.svelte +0 -484
- package/dist/element/ElementTile.svelte.d.ts +0 -29
- package/dist/element/Nucleus.svelte.d.ts +0 -17
- package/dist/element/data.d.ts +0 -3
- package/dist/element/data.js +0 -2
- package/dist/element/data.json.gz.d.ts +0 -2
- package/dist/element/index.d.ts +0 -8
- package/dist/element/index.js +0 -8
- package/dist/element/types.d.ts +0 -57
- package/dist/element/types.js +0 -1
- package/dist/feedback/ClickFeedback.svelte +0 -58
- package/dist/feedback/ClickFeedback.svelte.d.ts +0 -12
- package/dist/feedback/DragOverlay.svelte +0 -42
- package/dist/feedback/DragOverlay.svelte.d.ts +0 -7
- package/dist/feedback/Spinner.svelte.d.ts +0 -7
- package/dist/feedback/StatusMessage.svelte.d.ts +0 -9
- package/dist/feedback/index.d.ts +0 -4
- package/dist/feedback/index.js +0 -4
- package/dist/fermi-surface/FermiSlice.svelte +0 -189
- package/dist/fermi-surface/FermiSlice.svelte.d.ts +0 -24
- package/dist/fermi-surface/FermiSurface.svelte +0 -597
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +0 -83
- package/dist/fermi-surface/FermiSurfaceControls.svelte +0 -452
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +0 -35
- package/dist/fermi-surface/FermiSurfaceScene.svelte +0 -792
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +0 -50
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +0 -111
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte.d.ts +0 -8
- package/dist/fermi-surface/compute.d.ts +0 -5
- package/dist/fermi-surface/compute.js +0 -538
- package/dist/fermi-surface/constants.d.ts +0 -9
- package/dist/fermi-surface/constants.js +0 -27
- package/dist/fermi-surface/export.d.ts +0 -5
- package/dist/fermi-surface/export.js +0 -63
- package/dist/fermi-surface/index.d.ts +0 -12
- package/dist/fermi-surface/index.js +0 -13
- package/dist/fermi-surface/marching-cubes.d.ts +0 -2
- package/dist/fermi-surface/marching-cubes.js +0 -2
- package/dist/fermi-surface/parse.d.ts +0 -2
- package/dist/fermi-surface/parse.js +0 -495
- package/dist/fermi-surface/symmetry.d.ts +0 -3
- package/dist/fermi-surface/symmetry.js +0 -46
- package/dist/fermi-surface/types.d.ts +0 -113
- package/dist/fermi-surface/types.js +0 -4
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +0 -1527
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +0 -110
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +0 -225
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +0 -30
- package/dist/heatmap-matrix/index.d.ts +0 -53
- package/dist/heatmap-matrix/index.js +0 -100
- package/dist/heatmap-matrix/shared.d.ts +0 -2
- package/dist/heatmap-matrix/shared.js +0 -4
- package/dist/icons.d.ts +0 -569
- package/dist/icons.js +0 -648
- package/dist/index.d.ts +0 -39
- package/dist/index.js +0 -39
- package/dist/io/decompress.d.ts +0 -10
- package/dist/io/decompress.js +0 -69
- package/dist/io/export.d.ts +0 -16
- package/dist/io/export.js +0 -312
- package/dist/io/fetch.d.ts +0 -5
- package/dist/io/fetch.js +0 -39
- package/dist/io/file-drop.d.ts +0 -7
- package/dist/io/file-drop.js +0 -43
- package/dist/io/index.d.ts +0 -7
- package/dist/io/index.js +0 -7
- package/dist/io/is-binary.d.ts +0 -1
- package/dist/io/is-binary.js +0 -5
- package/dist/io/types.d.ts +0 -8
- package/dist/io/types.js +0 -1
- package/dist/io/url-drop.d.ts +0 -2
- package/dist/io/url-drop.js +0 -117
- package/dist/isosurface/Isosurface.svelte +0 -285
- package/dist/isosurface/Isosurface.svelte.d.ts +0 -8
- package/dist/isosurface/IsosurfaceControls.svelte +0 -291
- package/dist/isosurface/IsosurfaceControls.svelte.d.ts +0 -9
- package/dist/isosurface/index.d.ts +0 -5
- package/dist/isosurface/index.js +0 -6
- package/dist/isosurface/parse.d.ts +0 -6
- package/dist/isosurface/parse.js +0 -553
- package/dist/isosurface/slice.d.ts +0 -11
- package/dist/isosurface/slice.js +0 -140
- package/dist/isosurface/types.d.ts +0 -56
- package/dist/isosurface/types.js +0 -227
- package/dist/labels.d.ts +0 -53
- package/dist/labels.js +0 -274
- package/dist/layout/FullscreenToggle.svelte +0 -50
- package/dist/layout/FullscreenToggle.svelte.d.ts +0 -7
- package/dist/layout/InfoCard.svelte +0 -120
- package/dist/layout/InfoCard.svelte.d.ts +0 -21
- package/dist/layout/InfoTag.svelte +0 -183
- package/dist/layout/InfoTag.svelte.d.ts +0 -19
- package/dist/layout/PropertyFilter.svelte +0 -244
- package/dist/layout/PropertyFilter.svelte.d.ts +0 -24
- package/dist/layout/SettingsSection.svelte +0 -148
- package/dist/layout/SettingsSection.svelte.d.ts +0 -17
- package/dist/layout/SubpageGrid.svelte +0 -82
- package/dist/layout/SubpageGrid.svelte.d.ts +0 -14
- package/dist/layout/fullscreen.d.ts +0 -9
- package/dist/layout/fullscreen.js +0 -53
- package/dist/layout/index.d.ts +0 -10
- package/dist/layout/index.js +0 -8
- package/dist/layout/json-tree/JsonNode.svelte +0 -547
- package/dist/layout/json-tree/JsonNode.svelte.d.ts +0 -11
- package/dist/layout/json-tree/JsonTree.svelte +0 -1222
- package/dist/layout/json-tree/JsonTree.svelte.d.ts +0 -6
- package/dist/layout/json-tree/JsonValue.svelte +0 -334
- package/dist/layout/json-tree/JsonValue.svelte.d.ts +0 -9
- package/dist/layout/json-tree/index.d.ts +0 -3
- package/dist/layout/json-tree/index.js +0 -3
- package/dist/layout/json-tree/types.d.ts +0 -73
- package/dist/layout/json-tree/types.js +0 -3
- package/dist/layout/json-tree/utils.d.ts +0 -29
- package/dist/layout/json-tree/utils.js +0 -648
- package/dist/marching-cubes.d.ts +0 -14
- package/dist/marching-cubes.js +0 -542
- package/dist/math.d.ts +0 -91
- package/dist/math.js +0 -896
- package/dist/overlays/ContextMenu.svelte +0 -162
- package/dist/overlays/ContextMenu.svelte.d.ts +0 -25
- package/dist/overlays/DraggablePane.svelte +0 -564
- package/dist/overlays/DraggablePane.svelte.d.ts +0 -36
- package/dist/overlays/index.d.ts +0 -2
- package/dist/overlays/index.js +0 -2
- package/dist/periodic-table/PeriodicTable.svelte +0 -469
- package/dist/periodic-table/PeriodicTable.svelte.d.ts +0 -55
- package/dist/periodic-table/PeriodicTableControls.svelte +0 -557
- package/dist/periodic-table/PeriodicTableControls.svelte.d.ts +0 -24
- package/dist/periodic-table/PropertySelect.svelte +0 -37
- package/dist/periodic-table/PropertySelect.svelte.d.ts +0 -13
- package/dist/periodic-table/TableInset.svelte.d.ts +0 -9
- package/dist/periodic-table/index.d.ts +0 -10
- package/dist/periodic-table/index.js +0 -4
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +0 -1086
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +0 -44
- package/dist/phase-diagram/PhaseDiagramControls.svelte +0 -451
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +0 -30
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +0 -126
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +0 -15
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +0 -192
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +0 -19
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +0 -392
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +0 -16
- package/dist/phase-diagram/TdbInfoPanel.svelte +0 -203
- package/dist/phase-diagram/TdbInfoPanel.svelte.d.ts +0 -12
- package/dist/phase-diagram/build-diagram.d.ts +0 -11
- package/dist/phase-diagram/build-diagram.js +0 -167
- package/dist/phase-diagram/colors.d.ts +0 -35
- package/dist/phase-diagram/colors.js +0 -51
- package/dist/phase-diagram/diagram-input.d.ts +0 -33
- package/dist/phase-diagram/diagram-input.js +0 -3
- package/dist/phase-diagram/index.d.ts +0 -13
- package/dist/phase-diagram/index.js +0 -13
- package/dist/phase-diagram/parse.d.ts +0 -55
- package/dist/phase-diagram/parse.js +0 -276
- package/dist/phase-diagram/svg-to-diagram.d.ts +0 -2
- package/dist/phase-diagram/svg-to-diagram.js +0 -869
- package/dist/phase-diagram/types.d.ts +0 -99
- package/dist/phase-diagram/types.js +0 -1
- package/dist/phase-diagram/utils.d.ts +0 -118
- package/dist/phase-diagram/utils.js +0 -606
- package/dist/plot/AxisLabel.svelte +0 -51
- package/dist/plot/AxisLabel.svelte.d.ts +0 -16
- package/dist/plot/BarPlot.svelte +0 -2256
- package/dist/plot/BarPlot.svelte.d.ts +0 -82
- package/dist/plot/BarPlotControls.svelte +0 -66
- package/dist/plot/BarPlotControls.svelte.d.ts +0 -18
- package/dist/plot/ColorBar.svelte +0 -719
- package/dist/plot/ColorBar.svelte.d.ts +0 -31
- package/dist/plot/ColorScaleSelect.svelte +0 -54
- package/dist/plot/ColorScaleSelect.svelte.d.ts +0 -15
- package/dist/plot/ElementScatter.svelte +0 -63
- package/dist/plot/ElementScatter.svelte.d.ts +0 -14
- package/dist/plot/FillArea.svelte +0 -226
- package/dist/plot/FillArea.svelte.d.ts +0 -20
- package/dist/plot/Histogram.svelte +0 -1654
- package/dist/plot/Histogram.svelte.d.ts +0 -50
- package/dist/plot/HistogramControls.svelte +0 -212
- package/dist/plot/HistogramControls.svelte.d.ts +0 -22
- package/dist/plot/InteractiveAxisLabel.svelte +0 -94
- package/dist/plot/InteractiveAxisLabel.svelte.d.ts +0 -14
- package/dist/plot/Line.svelte +0 -85
- package/dist/plot/Line.svelte.d.ts +0 -15
- package/dist/plot/PlotControls.svelte +0 -537
- package/dist/plot/PlotControls.svelte.d.ts +0 -4
- package/dist/plot/PlotLegend.svelte +0 -498
- package/dist/plot/PlotLegend.svelte.d.ts +0 -25
- package/dist/plot/PlotTooltip.svelte +0 -67
- package/dist/plot/PlotTooltip.svelte.d.ts +0 -17
- package/dist/plot/PortalSelect.svelte +0 -253
- package/dist/plot/PortalSelect.svelte.d.ts +0 -16
- package/dist/plot/ReferenceLine.svelte.d.ts +0 -20
- package/dist/plot/ReferenceLine3D.svelte +0 -154
- package/dist/plot/ReferenceLine3D.svelte.d.ts +0 -14
- package/dist/plot/ReferencePlane.svelte +0 -178
- package/dist/plot/ReferencePlane.svelte.d.ts +0 -14
- package/dist/plot/ScatterPlot.svelte +0 -2831
- package/dist/plot/ScatterPlot.svelte.d.ts +0 -92
- package/dist/plot/ScatterPlot3D.svelte +0 -499
- package/dist/plot/ScatterPlot3D.svelte.d.ts +0 -94
- package/dist/plot/ScatterPlot3DControls.svelte +0 -437
- package/dist/plot/ScatterPlot3DControls.svelte.d.ts +0 -20
- package/dist/plot/ScatterPlot3DScene.svelte +0 -912
- package/dist/plot/ScatterPlot3DScene.svelte.d.ts +0 -74
- package/dist/plot/ScatterPlotControls.svelte +0 -307
- package/dist/plot/ScatterPlotControls.svelte.d.ts +0 -17
- package/dist/plot/ScatterPoint.svelte +0 -185
- package/dist/plot/ScatterPoint.svelte.d.ts +0 -19
- package/dist/plot/SpacegroupBarPlot.svelte +0 -292
- package/dist/plot/SpacegroupBarPlot.svelte.d.ts +0 -9
- package/dist/plot/Surface3D.svelte +0 -200
- package/dist/plot/Surface3D.svelte.d.ts +0 -13
- package/dist/plot/ZeroLines.svelte +0 -96
- package/dist/plot/ZeroLines.svelte.d.ts +0 -32
- package/dist/plot/ZoomRect.svelte +0 -23
- package/dist/plot/ZoomRect.svelte.d.ts +0 -8
- package/dist/plot/axis-utils.d.ts +0 -19
- package/dist/plot/axis-utils.js +0 -80
- package/dist/plot/data-cleaning.d.ts +0 -37
- package/dist/plot/data-cleaning.js +0 -855
- package/dist/plot/data-transform.d.ts +0 -16
- package/dist/plot/data-transform.js +0 -45
- package/dist/plot/defaults.d.ts +0 -19
- package/dist/plot/defaults.js +0 -9
- package/dist/plot/fill-utils.d.ts +0 -51
- package/dist/plot/fill-utils.js +0 -337
- package/dist/plot/hover-lock.svelte.d.ts +0 -14
- package/dist/plot/hover-lock.svelte.js +0 -46
- package/dist/plot/index.d.ts +0 -43
- package/dist/plot/index.js +0 -37
- package/dist/plot/interactions.d.ts +0 -12
- package/dist/plot/interactions.js +0 -100
- package/dist/plot/layout.d.ts +0 -60
- package/dist/plot/layout.js +0 -230
- package/dist/plot/reference-line.d.ts +0 -60
- package/dist/plot/reference-line.js +0 -316
- package/dist/plot/scales.d.ts +0 -48
- package/dist/plot/scales.js +0 -484
- package/dist/plot/svg.d.ts +0 -1
- package/dist/plot/svg.js +0 -11
- package/dist/plot/types.d.ts +0 -863
- package/dist/plot/types.js +0 -103
- package/dist/plot/utils/label-placement.d.ts +0 -47
- package/dist/plot/utils/label-placement.js +0 -256
- package/dist/plot/utils/series-visibility.d.ts +0 -9
- package/dist/plot/utils/series-visibility.js +0 -67
- package/dist/plot/utils.d.ts +0 -1
- package/dist/plot/utils.js +0 -14
- package/dist/rdf/RdfPlot.svelte +0 -247
- package/dist/rdf/RdfPlot.svelte.d.ts +0 -27
- package/dist/rdf/calc-rdf.d.ts +0 -4
- package/dist/rdf/calc-rdf.js +0 -111
- package/dist/rdf/index.d.ts +0 -23
- package/dist/rdf/index.js +0 -2
- package/dist/sanitize.d.ts +0 -4
- package/dist/sanitize.js +0 -107
- package/dist/settings.d.ts +0 -253
- package/dist/settings.js +0 -1123
- package/dist/spectral/Bands.svelte +0 -1040
- package/dist/spectral/Bands.svelte.d.ts +0 -40
- package/dist/spectral/BandsAndDos.svelte +0 -128
- package/dist/spectral/BandsAndDos.svelte.d.ts +0 -18
- package/dist/spectral/BrillouinBandsDos.svelte +0 -248
- package/dist/spectral/BrillouinBandsDos.svelte.d.ts +0 -20
- package/dist/spectral/Dos.svelte +0 -697
- package/dist/spectral/Dos.svelte.d.ts +0 -29
- package/dist/spectral/helpers.d.ts +0 -117
- package/dist/spectral/helpers.js +0 -1023
- package/dist/spectral/index.d.ts +0 -6
- package/dist/spectral/index.js +0 -7
- package/dist/spectral/types.d.ts +0 -84
- package/dist/spectral/types.js +0 -2
- package/dist/state.svelte.d.ts +0 -25
- package/dist/state.svelte.js +0 -45
- package/dist/structure/Arrow.svelte +0 -72
- package/dist/structure/Arrow.svelte.d.ts +0 -15
- package/dist/structure/AtomLegend.svelte +0 -798
- package/dist/structure/AtomLegend.svelte.d.ts +0 -34
- package/dist/structure/Bond.svelte +0 -140
- package/dist/structure/Bond.svelte.d.ts +0 -9
- package/dist/structure/CanvasTooltip.svelte +0 -33
- package/dist/structure/CanvasTooltip.svelte.d.ts +0 -12
- package/dist/structure/CellSelect.svelte +0 -351
- package/dist/structure/CellSelect.svelte.d.ts +0 -13
- package/dist/structure/Cylinder.svelte +0 -45
- package/dist/structure/Cylinder.svelte.d.ts +0 -10
- package/dist/structure/Lattice.svelte +0 -196
- package/dist/structure/Lattice.svelte.d.ts +0 -17
- package/dist/structure/Structure.svelte +0 -1857
- package/dist/structure/Structure.svelte.d.ts +0 -83
- package/dist/structure/StructureControls.svelte +0 -1184
- package/dist/structure/StructureControls.svelte.d.ts +0 -31
- package/dist/structure/StructureExportPane.svelte +0 -251
- package/dist/structure/StructureExportPane.svelte.d.ts +0 -17
- package/dist/structure/StructureInfoPane.svelte +0 -434
- package/dist/structure/StructureInfoPane.svelte.d.ts +0 -18
- package/dist/structure/StructureScene.svelte +0 -1574
- package/dist/structure/StructureScene.svelte.d.ts +0 -104
- package/dist/structure/atom-properties.d.ts +0 -37
- package/dist/structure/atom-properties.js +0 -198
- package/dist/structure/bonding.d.ts +0 -33
- package/dist/structure/bonding.js +0 -304
- package/dist/structure/export.d.ts +0 -20
- package/dist/structure/export.js +0 -725
- package/dist/structure/ferrox-wasm-types.d.ts +0 -46
- package/dist/structure/ferrox-wasm-types.js +0 -18
- package/dist/structure/ferrox-wasm.d.ts +0 -94
- package/dist/structure/ferrox-wasm.js +0 -249
- package/dist/structure/index.d.ts +0 -110
- package/dist/structure/index.js +0 -168
- package/dist/structure/measure.d.ts +0 -6
- package/dist/structure/measure.js +0 -29
- package/dist/structure/parse.d.ts +0 -65
- package/dist/structure/parse.js +0 -1374
- package/dist/structure/partial-occupancy.d.ts +0 -25
- package/dist/structure/partial-occupancy.js +0 -99
- package/dist/structure/pbc.d.ts +0 -9
- package/dist/structure/pbc.js +0 -123
- package/dist/structure/supercell.d.ts +0 -8
- package/dist/structure/supercell.js +0 -137
- package/dist/structure/validation.d.ts +0 -2
- package/dist/structure/validation.js +0 -10
- package/dist/symmetry/SymmetryStats.svelte +0 -226
- package/dist/symmetry/SymmetryStats.svelte.d.ts +0 -21
- package/dist/symmetry/WyckoffTable.svelte +0 -113
- package/dist/symmetry/WyckoffTable.svelte.d.ts +0 -11
- package/dist/symmetry/cell-transform.d.ts +0 -12
- package/dist/symmetry/cell-transform.js +0 -77
- package/dist/symmetry/index.d.ts +0 -43
- package/dist/symmetry/index.js +0 -229
- package/dist/symmetry/spacegroups.d.ts +0 -9
- package/dist/symmetry/spacegroups.js +0 -394
- package/dist/table/HeatmapTable.svelte +0 -1854
- package/dist/table/HeatmapTable.svelte.d.ts +0 -49
- package/dist/table/ToggleMenu.svelte +0 -376
- package/dist/table/ToggleMenu.svelte.d.ts +0 -11
- package/dist/table/index.d.ts +0 -74
- package/dist/table/index.js +0 -38
- package/dist/theme/ThemeControl.svelte +0 -53
- package/dist/theme/ThemeControl.svelte.d.ts +0 -9
- package/dist/theme/index.d.ts +0 -29
- package/dist/theme/index.js +0 -79
- package/dist/theme/themes.mjs +0 -285
- package/dist/time.d.ts +0 -4
- package/dist/time.js +0 -70
- package/dist/tooltip/TooltipContent.svelte +0 -58
- package/dist/tooltip/TooltipContent.svelte.d.ts +0 -31
- package/dist/tooltip/index.d.ts +0 -2
- package/dist/tooltip/index.js +0 -2
- package/dist/tooltip/types.d.ts +0 -8
- package/dist/tooltip/types.js +0 -1
- package/dist/trajectory/Trajectory.svelte +0 -1517
- package/dist/trajectory/Trajectory.svelte.d.ts +0 -77
- package/dist/trajectory/TrajectoryError.svelte +0 -128
- package/dist/trajectory/TrajectoryError.svelte.d.ts +0 -13
- package/dist/trajectory/TrajectoryExportPane.svelte +0 -357
- package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +0 -17
- package/dist/trajectory/TrajectoryInfoPane.svelte +0 -387
- package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +0 -17
- package/dist/trajectory/constants.d.ts +0 -6
- package/dist/trajectory/constants.js +0 -7
- package/dist/trajectory/extract.d.ts +0 -5
- package/dist/trajectory/extract.js +0 -162
- package/dist/trajectory/format-detect.d.ts +0 -9
- package/dist/trajectory/format-detect.js +0 -76
- package/dist/trajectory/frame-reader.d.ts +0 -17
- package/dist/trajectory/frame-reader.js +0 -332
- package/dist/trajectory/helpers.d.ts +0 -14
- package/dist/trajectory/helpers.js +0 -172
- package/dist/trajectory/index.d.ts +0 -63
- package/dist/trajectory/index.js +0 -126
- package/dist/trajectory/parse/ase.d.ts +0 -2
- package/dist/trajectory/parse/ase.js +0 -77
- package/dist/trajectory/parse/hdf5.d.ts +0 -2
- package/dist/trajectory/parse/hdf5.js +0 -129
- package/dist/trajectory/parse/index.d.ts +0 -12
- package/dist/trajectory/parse/index.js +0 -299
- package/dist/trajectory/parse/lammps.d.ts +0 -5
- package/dist/trajectory/parse/lammps.js +0 -179
- package/dist/trajectory/parse/vasp.d.ts +0 -2
- package/dist/trajectory/parse/vasp.js +0 -68
- package/dist/trajectory/parse/xyz.d.ts +0 -2
- package/dist/trajectory/parse/xyz.js +0 -110
- package/dist/trajectory/plotting.d.ts +0 -28
- package/dist/trajectory/plotting.js +0 -423
- package/dist/trajectory/types.d.ts +0 -11
- package/dist/trajectory/types.js +0 -1
- package/dist/utils.d.ts +0 -5
- package/dist/utils.js +0 -36
- package/dist/xrd/XrdPlot.svelte +0 -615
- package/dist/xrd/XrdPlot.svelte.d.ts +0 -28
- package/dist/xrd/broadening.d.ts +0 -20
- package/dist/xrd/broadening.js +0 -97
- package/dist/xrd/calc-xrd.d.ts +0 -37
- package/dist/xrd/calc-xrd.js +0 -337
- package/dist/xrd/index.d.ts +0 -37
- package/dist/xrd/index.js +0 -4
- package/dist/xrd/parse.d.ts +0 -13
- package/dist/xrd/parse.js +0 -749
- /package/dist/{EmptyState.svelte → src/lib/EmptyState.svelte} +0 -0
- /package/dist/{Icon.svelte → src/lib/Icon.svelte} +0 -0
- /package/dist/{chempot-diagram → src/lib/chempot-diagram}/ChemPotScene3D.svelte +0 -0
- /package/dist/{colors → src/lib/colors}/alloy-colors.json +0 -0
- /package/dist/{colors → src/lib/colors}/dark-mode-colors.json +0 -0
- /package/dist/{colors → src/lib/colors}/jmol-colors.json +0 -0
- /package/dist/{colors → src/lib/colors}/muted-colors.json +0 -0
- /package/dist/{colors → src/lib/colors}/pastel-colors.json +0 -0
- /package/dist/{colors → src/lib/colors}/vesta-colors.json +0 -0
- /package/dist/{element → src/lib/element}/Nucleus.svelte +0 -0
- /package/dist/{element → src/lib/element}/data.json +0 -0
- /package/dist/{element → src/lib/element}/data.json.gz +0 -0
- /package/dist/{element → src/lib/element}/data.schema.json +0 -0
- /package/dist/{element-image-urls.json → src/lib/element-image-urls.json} +0 -0
- /package/dist/{feedback → src/lib/feedback}/Spinner.svelte +0 -0
- /package/dist/{feedback → src/lib/feedback}/StatusMessage.svelte +0 -0
- /package/dist/{periodic-table → src/lib/periodic-table}/TableInset.svelte +0 -0
- /package/dist/{plot → src/lib/plot}/ReferenceLine.svelte +0 -0
- /package/dist/{xrd → src/lib/xrd}/atomic_scattering_params.json +0 -0
|
@@ -0,0 +1,1381 @@
|
|
|
1
|
+
// Helper utilities for band structure and DOS data processing
|
|
2
|
+
import { SUBSCRIPT_MAP } from '$lib/labels'
|
|
3
|
+
import { centered_frac, euclidean_dist } from '$lib/math'
|
|
4
|
+
import type { Matrix3x3, Vec2, Vec3 } from '$lib/math'
|
|
5
|
+
import type { AxisConfig } from '$lib/plot/types'
|
|
6
|
+
import type * as types from './types'
|
|
7
|
+
import type { RibbonConfig } from './types'
|
|
8
|
+
|
|
9
|
+
const is_subscript_key = (key: string): key is keyof typeof SUBSCRIPT_MAP =>
|
|
10
|
+
key in SUBSCRIPT_MAP
|
|
11
|
+
|
|
12
|
+
// Check if range is a valid [min, max] tuple (strict 2-element array of finite numbers)
|
|
13
|
+
export const is_valid_range = (range: unknown): range is Vec2 =>
|
|
14
|
+
Array.isArray(range) &&
|
|
15
|
+
range.length === 2 &&
|
|
16
|
+
Number.isFinite(range[0]) &&
|
|
17
|
+
Number.isFinite(range[1])
|
|
18
|
+
|
|
19
|
+
// Check if two ranges are approximately equal (within tolerance)
|
|
20
|
+
// Returns false if either range is invalid (null, undefined, or fails is_valid_range)
|
|
21
|
+
// Negative tol is clamped to 0; tol=0 checks exact equality
|
|
22
|
+
export const ranges_equal = (
|
|
23
|
+
a: Vec2 | undefined | null,
|
|
24
|
+
b: Vec2 | undefined | null,
|
|
25
|
+
tol = 0.001,
|
|
26
|
+
): boolean => {
|
|
27
|
+
const safe_tol = Math.max(0, tol)
|
|
28
|
+
return (
|
|
29
|
+
is_valid_range(a) &&
|
|
30
|
+
is_valid_range(b) &&
|
|
31
|
+
Math.abs(a[0] - b[0]) <= safe_tol &&
|
|
32
|
+
Math.abs(a[1] - b[1]) <= safe_tol
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const axis_with_range = (
|
|
37
|
+
axis: AxisConfig | undefined,
|
|
38
|
+
range: Vec2 | undefined,
|
|
39
|
+
label?: string,
|
|
40
|
+
): AxisConfig => ({
|
|
41
|
+
...axis,
|
|
42
|
+
...(label !== undefined && { label }),
|
|
43
|
+
...(is_valid_range(range) && { range }),
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
// Detect which plot triggered a zoom change and return the new synced range.
|
|
47
|
+
// Returns null to reset to shared range, undefined for no change, or Vec2 for new zoom.
|
|
48
|
+
export function detect_zoom_change(
|
|
49
|
+
bands_range: unknown,
|
|
50
|
+
dos_range: unknown,
|
|
51
|
+
shared_range: Vec2,
|
|
52
|
+
current_synced: Vec2 | null,
|
|
53
|
+
dos_enabled = true,
|
|
54
|
+
): Vec2 | null | undefined {
|
|
55
|
+
const bands_valid = is_valid_range(bands_range)
|
|
56
|
+
const dos_valid = dos_enabled && is_valid_range(dos_range)
|
|
57
|
+
|
|
58
|
+
// Reset if either becomes invalid (auto-range reset) or returns to shared range
|
|
59
|
+
if (current_synced !== null) {
|
|
60
|
+
const bands_at_shared = bands_valid && ranges_equal(bands_range, shared_range)
|
|
61
|
+
const dos_at_shared = dos_valid && ranges_equal(dos_range, shared_range)
|
|
62
|
+
if (bands_at_shared || dos_at_shared || !bands_valid || (dos_enabled && !dos_valid)) {
|
|
63
|
+
return null
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Check for new zoom from either plot
|
|
68
|
+
const bands_is_new =
|
|
69
|
+
bands_valid &&
|
|
70
|
+
!ranges_equal(bands_range, shared_range) &&
|
|
71
|
+
!ranges_equal(bands_range, current_synced)
|
|
72
|
+
const dos_is_new =
|
|
73
|
+
dos_valid &&
|
|
74
|
+
!ranges_equal(dos_range, shared_range) &&
|
|
75
|
+
!ranges_equal(dos_range, current_synced)
|
|
76
|
+
|
|
77
|
+
if (bands_is_new && !dos_is_new) return bands_range
|
|
78
|
+
if (dos_is_new && !bands_is_new) return dos_range
|
|
79
|
+
return undefined // no change
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Physical constants for unit conversions (SI units)
|
|
83
|
+
const PLANCK = 6.62607015e-34 // J⋅s
|
|
84
|
+
const EV_TO_J = 1.602176634e-19 // J
|
|
85
|
+
const C_LIGHT = 299792458 // m/s
|
|
86
|
+
const THz_TO_HZ = 1e12
|
|
87
|
+
const THz_TO_EV = (PLANCK * THz_TO_HZ) / EV_TO_J
|
|
88
|
+
const THz_TO_MEV = THz_TO_EV * 1000
|
|
89
|
+
const THz_TO_HA = THz_TO_EV / 27.211386245988 // Hartree
|
|
90
|
+
const THz_TO_CM = THz_TO_HZ / (C_LIGHT * 100) // cm^-1 (c in cm/s)
|
|
91
|
+
|
|
92
|
+
// Band structure constants
|
|
93
|
+
export const IMAGINARY_MODE_NOISE_THRESHOLD = 0.005 // Clamp negatives < 0.5% as noise
|
|
94
|
+
|
|
95
|
+
// Convert symmetry point symbols to pretty-printed versions.
|
|
96
|
+
// Handles Greek letters (both plain and LaTeX backslash-prefixed) and subscripts.
|
|
97
|
+
export function pretty_sym_point(symbol: string): string {
|
|
98
|
+
if (!symbol) return ``
|
|
99
|
+
|
|
100
|
+
// Remove underscores (htmlify maps S0 → S<sub>0</sub> but leaves S_0 as is)
|
|
101
|
+
// Replace common symmetry point names with Greek letters
|
|
102
|
+
// Handle both plain names (GAMMA) and LaTeX notation (\Gamma) from pymatgen
|
|
103
|
+
// Handle subscripts: convert S0 to S₀, K1 to K₁, Γ1 to Γ₁, etc.
|
|
104
|
+
// Use \p{L} to match any Unicode letter (not just ASCII A-Z)
|
|
105
|
+
return symbol
|
|
106
|
+
.replace(/_/g, ``)
|
|
107
|
+
.replace(/\\?GAMMA/gi, `Γ`)
|
|
108
|
+
.replace(/\\?DELTA/gi, `Δ`)
|
|
109
|
+
.replace(/\\?SIGMA/gi, `Σ`)
|
|
110
|
+
.replace(/\\?LAMBDA/gi, `Λ`)
|
|
111
|
+
.replace(
|
|
112
|
+
/(\p{L})(\d+)/gu,
|
|
113
|
+
(_, letter, num) =>
|
|
114
|
+
letter +
|
|
115
|
+
num
|
|
116
|
+
.split(``)
|
|
117
|
+
.map((digit: string) => (is_subscript_key(digit) ? SUBSCRIPT_MAP[digit] : digit))
|
|
118
|
+
.join(``),
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Create segment key from start and end labels
|
|
123
|
+
export const get_segment_key = (start_label?: string, end_label?: string) =>
|
|
124
|
+
`${start_label ?? `null`}_${end_label ?? `null`}`
|
|
125
|
+
|
|
126
|
+
// Get ordered segment keys from a band structure, preserving physical path order.
|
|
127
|
+
export const get_ordered_segments = (
|
|
128
|
+
band_struct: types.BaseBandStructure | null,
|
|
129
|
+
segments: Set<string>,
|
|
130
|
+
) => {
|
|
131
|
+
if (!band_struct) return Array.from(segments)
|
|
132
|
+
|
|
133
|
+
const ordered = band_struct.branches.map((branch) =>
|
|
134
|
+
get_segment_key(
|
|
135
|
+
band_struct.qpoints[branch.start_index]?.label ?? undefined,
|
|
136
|
+
band_struct.qpoints[branch.end_index]?.label ?? undefined,
|
|
137
|
+
),
|
|
138
|
+
)
|
|
139
|
+
const remaining = Array.from(segments).filter((seg) => !ordered.includes(seg))
|
|
140
|
+
return [...ordered, ...remaining]
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Scale segment distances to a target x-axis range [x_start, x_end].
|
|
144
|
+
// Used by both band line series and fat band ribbons for consistent x-axis positioning.
|
|
145
|
+
export function scale_segment_distances(
|
|
146
|
+
segment_distances: number[],
|
|
147
|
+
x_start: number,
|
|
148
|
+
x_end: number,
|
|
149
|
+
): number[] {
|
|
150
|
+
if (segment_distances.length === 0) return []
|
|
151
|
+
|
|
152
|
+
const dist_min = segment_distances[0]
|
|
153
|
+
const dist_range = (segment_distances.at(-1) ?? dist_min) - dist_min
|
|
154
|
+
|
|
155
|
+
if (dist_range === 0) {
|
|
156
|
+
// All points at same distance - place at midpoint
|
|
157
|
+
return segment_distances.map(() => (x_start + x_end) / 2)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return segment_distances.map(
|
|
161
|
+
(dist) => x_start + ((dist - dist_min) / dist_range) * (x_end - x_start),
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Get ribbon config for a specific band structure label.
|
|
166
|
+
// Supports both single global config (with primitive keys like opacity, max_width, scale, color)
|
|
167
|
+
// and per-structure config (keyed by structure label).
|
|
168
|
+
// Distinguishes between a global config and a per-structure config by checking if any
|
|
169
|
+
// primitive-typed keys (opacity, max_width, scale, color) exist at the top level.
|
|
170
|
+
export function get_ribbon_config(
|
|
171
|
+
ribbon_config: RibbonConfig | Record<string, RibbonConfig>,
|
|
172
|
+
label: string,
|
|
173
|
+
): RibbonConfig {
|
|
174
|
+
const defaults: RibbonConfig = { opacity: 0.3, max_width: 6, scale: 1 }
|
|
175
|
+
const config_record = ribbon_config as Record<string, unknown>
|
|
176
|
+
|
|
177
|
+
// Check for primitive config values (not objects) to distinguish single vs per-structure config
|
|
178
|
+
const has_primitive = [`opacity`, `max_width`, `scale`, `color`].some((key) => {
|
|
179
|
+
const value = config_record[key]
|
|
180
|
+
return value !== undefined && typeof value !== `object`
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
if (has_primitive) {
|
|
184
|
+
// Single global config with primitive values - apply to all structures
|
|
185
|
+
return { ...defaults, ...ribbon_config }
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Otherwise, treat as Record<string, RibbonConfig> and look up by label
|
|
189
|
+
// Empty label skips lookup and uses defaults only
|
|
190
|
+
const label_config = label ? config_record[label] : undefined
|
|
191
|
+
return {
|
|
192
|
+
...defaults,
|
|
193
|
+
...(label_config && typeof label_config === `object` ? label_config : {}),
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Extract tick positions and labels for a band structure plot.
|
|
198
|
+
export function get_band_xaxis_ticks(
|
|
199
|
+
band_struct: types.BaseBandStructure,
|
|
200
|
+
branches: string[] | Set<string> = [],
|
|
201
|
+
): [number[], string[]] {
|
|
202
|
+
const ticks_x_pos: number[] = []
|
|
203
|
+
const tick_labels: string[] = []
|
|
204
|
+
let prev_label = band_struct.qpoints[0]?.label || null
|
|
205
|
+
let prev_branch = band_struct.branches[0]?.name || null
|
|
206
|
+
|
|
207
|
+
// Convert branches to Set for consistent handling
|
|
208
|
+
const branches_set = Array.isArray(branches) ? new Set(branches) : branches
|
|
209
|
+
|
|
210
|
+
for (let idx = 0; idx < band_struct.qpoints.length; idx++) {
|
|
211
|
+
const point = band_struct.qpoints[idx]
|
|
212
|
+
if (point.label === null) continue
|
|
213
|
+
|
|
214
|
+
// Find which branch this point belongs to
|
|
215
|
+
const branch_names = band_struct.branches
|
|
216
|
+
.filter((branch) => branch.start_index <= idx && idx <= branch.end_index)
|
|
217
|
+
.map((branch) => branch.name)
|
|
218
|
+
const this_branch = branch_names[0] || null
|
|
219
|
+
|
|
220
|
+
if (point.label !== prev_label && prev_branch !== this_branch) {
|
|
221
|
+
// Branch transition - combine labels
|
|
222
|
+
tick_labels[tick_labels.length - 1] = `${prev_label || ``}|${point.label}`
|
|
223
|
+
ticks_x_pos[ticks_x_pos.length - 1] = band_struct.distance[idx]
|
|
224
|
+
} else if (branches_set.size === 0 || (this_branch && branches_set.has(this_branch))) {
|
|
225
|
+
tick_labels.push(point.label)
|
|
226
|
+
ticks_x_pos.push(band_struct.distance[idx])
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
prev_label = point.label
|
|
230
|
+
prev_branch = this_branch
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return [ticks_x_pos, tick_labels.map(pretty_sym_point)]
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Convert frequencies from THz to specified units.
|
|
237
|
+
export function convert_frequencies(
|
|
238
|
+
frequencies: number[],
|
|
239
|
+
unit: types.FrequencyUnit = `THz`,
|
|
240
|
+
): number[] {
|
|
241
|
+
const conversion_factors: Record<types.FrequencyUnit, number> = {
|
|
242
|
+
THz: 1,
|
|
243
|
+
eV: THz_TO_EV,
|
|
244
|
+
meV: THz_TO_MEV,
|
|
245
|
+
Ha: THz_TO_HA,
|
|
246
|
+
'cm-1': THz_TO_CM,
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const factor = conversion_factors[unit]
|
|
250
|
+
if (!factor) {
|
|
251
|
+
const valid_units = Object.keys(conversion_factors).join(`, `)
|
|
252
|
+
throw new Error(`Invalid unit: ${unit}. Must be one of ${valid_units}`)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return frequencies.map((freq) => freq * factor)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Normalize DOS densities according to specified mode.
|
|
259
|
+
export function normalize_densities(
|
|
260
|
+
densities: number[],
|
|
261
|
+
freqs_or_energies: number[],
|
|
262
|
+
mode: types.NormalizationMode,
|
|
263
|
+
): number[] {
|
|
264
|
+
if (!mode) return densities
|
|
265
|
+
|
|
266
|
+
const normalized = [...densities]
|
|
267
|
+
|
|
268
|
+
if (mode === `max`) {
|
|
269
|
+
const max_val = Math.max(...normalized)
|
|
270
|
+
if (max_val === 0) return normalized
|
|
271
|
+
return normalized.map((dens) => dens / max_val)
|
|
272
|
+
} else if (mode === `sum`) {
|
|
273
|
+
const sum = normalized.reduce((acc, d) => acc + d, 0)
|
|
274
|
+
if (sum === 0) return normalized
|
|
275
|
+
return normalized.map((dens) => dens / sum)
|
|
276
|
+
} else if (mode === `integral`) {
|
|
277
|
+
if (freqs_or_energies.length < 2) return normalized
|
|
278
|
+
const bin_width = freqs_or_energies[1] - freqs_or_energies[0]
|
|
279
|
+
if (bin_width === 0) return normalized
|
|
280
|
+
const sum = normalized.reduce((acc, d) => acc + d, 0)
|
|
281
|
+
if (sum === 0) return normalized
|
|
282
|
+
return normalized.map((dens) => dens / (sum * bin_width))
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return normalized
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Simple LRU cache for Gaussian smearing results
|
|
289
|
+
// Key: hash of (frequencies, densities, sigma), Value: smeared densities
|
|
290
|
+
const SMEARING_CACHE_MAX_SIZE = 10
|
|
291
|
+
const smearing_cache = new Map<string, number[]>()
|
|
292
|
+
|
|
293
|
+
// FNV-1a hash for number arrays (fast, good distribution, O(n))
|
|
294
|
+
function fnv1a_hash(arr: number[]): number {
|
|
295
|
+
let hash = 2166136261 // FNV offset basis
|
|
296
|
+
for (const val of arr) {
|
|
297
|
+
// Convert float to int32 bits for consistent hashing
|
|
298
|
+
const bits = new Float64Array([val])
|
|
299
|
+
const int_view = new Uint32Array(bits.buffer)
|
|
300
|
+
hash ^= int_view[0]
|
|
301
|
+
hash = Math.imul(hash, 16777619) // FNV prime
|
|
302
|
+
hash ^= int_view[1]
|
|
303
|
+
hash = Math.imul(hash, 16777619)
|
|
304
|
+
}
|
|
305
|
+
return hash >>> 0 // Ensure unsigned
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Generate cache key using FNV-1a hash over full arrays (O(n), low collision risk)
|
|
309
|
+
function generate_smearing_cache_key(
|
|
310
|
+
freqs_or_energies: number[],
|
|
311
|
+
densities: number[],
|
|
312
|
+
sigma: number,
|
|
313
|
+
): string {
|
|
314
|
+
const len = freqs_or_energies.length
|
|
315
|
+
if (len === 0) return `0:${sigma.toFixed(6)}:0:0`
|
|
316
|
+
return `${len}:${sigma.toFixed(6)}:${fnv1a_hash(freqs_or_energies).toString(16)}:${fnv1a_hash(
|
|
317
|
+
densities,
|
|
318
|
+
).toString(16)}`
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Core Gaussian smearing computation (unmemoized)
|
|
322
|
+
function apply_gaussian_smearing_core(
|
|
323
|
+
freqs_or_energies: number[],
|
|
324
|
+
densities: number[],
|
|
325
|
+
sigma: number,
|
|
326
|
+
): number[] {
|
|
327
|
+
const orig_sum = densities.reduce((acc, d) => acc + d, 0)
|
|
328
|
+
if (sigma <= 0 || orig_sum === 0) return densities
|
|
329
|
+
|
|
330
|
+
const smeared = Array(densities.length).fill(0)
|
|
331
|
+
const truncation_width = 4 // Truncate Gaussian at ±4σ (contribution < 0.01%)
|
|
332
|
+
|
|
333
|
+
for (let idx = 0; idx < freqs_or_energies.length; idx++) {
|
|
334
|
+
const energy = freqs_or_energies[idx]
|
|
335
|
+
const cutoff = truncation_width * sigma
|
|
336
|
+
|
|
337
|
+
for (let jdx = 0; jdx < freqs_or_energies.length; jdx++) {
|
|
338
|
+
const e_j = freqs_or_energies[jdx]
|
|
339
|
+
const delta = Math.abs(energy - e_j)
|
|
340
|
+
|
|
341
|
+
// Skip points beyond truncation width
|
|
342
|
+
if (delta > cutoff) continue
|
|
343
|
+
|
|
344
|
+
const gaussian = Math.exp(-((energy - e_j) ** 2) / (2 * sigma ** 2))
|
|
345
|
+
smeared[idx] += densities[jdx] * gaussian
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Normalize to preserve integral
|
|
350
|
+
const smeared_sum = smeared.reduce((acc, d) => acc + d, 0)
|
|
351
|
+
if (smeared_sum === 0) return densities
|
|
352
|
+
const normalization = orig_sum / smeared_sum
|
|
353
|
+
return smeared.map((dens) => dens * normalization)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Apply Gaussian smearing to DOS densities with memoization.
|
|
357
|
+
// Uses truncated Gaussian (±4σ) for O(n·w) complexity instead of O(n²).
|
|
358
|
+
// Results are cached using an LRU cache to avoid recomputation on reactive updates.
|
|
359
|
+
export function apply_gaussian_smearing(
|
|
360
|
+
freqs_or_energies: number[],
|
|
361
|
+
densities: number[],
|
|
362
|
+
sigma: number,
|
|
363
|
+
): number[] {
|
|
364
|
+
// Fast path: no smearing needed
|
|
365
|
+
if (sigma <= 0) return densities
|
|
366
|
+
|
|
367
|
+
const cache_key = generate_smearing_cache_key(freqs_or_energies, densities, sigma)
|
|
368
|
+
|
|
369
|
+
// Check cache
|
|
370
|
+
const cached = smearing_cache.get(cache_key)
|
|
371
|
+
if (cached) {
|
|
372
|
+
// Move to end (LRU behavior: most recently used last)
|
|
373
|
+
smearing_cache.delete(cache_key)
|
|
374
|
+
smearing_cache.set(cache_key, cached)
|
|
375
|
+
return cached
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Compute and cache
|
|
379
|
+
const result = apply_gaussian_smearing_core(freqs_or_energies, densities, sigma)
|
|
380
|
+
|
|
381
|
+
// Evict oldest entry if cache is full (LRU: first entry is oldest)
|
|
382
|
+
if (smearing_cache.size >= SMEARING_CACHE_MAX_SIZE) {
|
|
383
|
+
const oldest_key = smearing_cache.keys().next().value
|
|
384
|
+
if (oldest_key !== undefined) smearing_cache.delete(oldest_key)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
smearing_cache.set(cache_key, result)
|
|
388
|
+
return result
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Clear the smearing cache (useful for testing or memory management)
|
|
392
|
+
export function clear_smearing_cache(): void {
|
|
393
|
+
smearing_cache.clear()
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Type guards for pymatgen qpoint formats
|
|
397
|
+
const is_vec3 = (val: unknown): val is Vec3 =>
|
|
398
|
+
Array.isArray(val) && val.length >= 3 && val.slice(0, 3).every(Number.isFinite)
|
|
399
|
+
|
|
400
|
+
interface PymatgenKpoint {
|
|
401
|
+
frac_coords: Vec3
|
|
402
|
+
label?: string | null
|
|
403
|
+
}
|
|
404
|
+
const is_kpoint = (val: unknown): val is PymatgenKpoint =>
|
|
405
|
+
!!val && typeof val === `object` && `frac_coords` in val && is_vec3(val.frac_coords)
|
|
406
|
+
|
|
407
|
+
const is_pymatgen_format = (obj: Record<string, unknown>): boolean => {
|
|
408
|
+
// Check for explicit pymatgen markers
|
|
409
|
+
if (typeof obj[`@class`] === `string` || typeof obj[`@module`] === `string`) {
|
|
410
|
+
return true
|
|
411
|
+
}
|
|
412
|
+
// Check for pymatgen-style qpoints (phonon) or kpoints (electronic) without branches
|
|
413
|
+
const points = obj.qpoints ?? obj.kpoints
|
|
414
|
+
if (Array.isArray(points) && points.length > 0 && !Array.isArray(obj.branches)) {
|
|
415
|
+
return is_vec3(points[0]) || is_kpoint(points[0])
|
|
416
|
+
}
|
|
417
|
+
return false
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Extract frac_coords/label from pymatgen qpoint, matching label from labels_dict if needed
|
|
421
|
+
const parse_qpoint = (
|
|
422
|
+
qpt: unknown,
|
|
423
|
+
labels_dict?: Record<string, Vec3>,
|
|
424
|
+
): types.QPoint | null => {
|
|
425
|
+
const frac_coords = is_vec3(qpt)
|
|
426
|
+
? ([qpt[0], qpt[1], qpt[2]] as Vec3)
|
|
427
|
+
: is_kpoint(qpt)
|
|
428
|
+
? qpt.frac_coords
|
|
429
|
+
: null
|
|
430
|
+
if (!frac_coords) return null
|
|
431
|
+
|
|
432
|
+
const label =
|
|
433
|
+
(is_kpoint(qpt) && typeof qpt.label === `string` && qpt.label) ||
|
|
434
|
+
Object.entries(labels_dict ?? {}).find(
|
|
435
|
+
([, c]) => euclidean_dist(frac_coords, c) < 1e-4,
|
|
436
|
+
)?.[0] ||
|
|
437
|
+
null
|
|
438
|
+
return { label, frac_coords }
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Inverse conversion factors (derived from THz_TO_* for consistency)
|
|
442
|
+
const EV_TO_THZ = 1 / THz_TO_EV
|
|
443
|
+
const CM_TO_THZ = 1 / THz_TO_CM
|
|
444
|
+
|
|
445
|
+
// Spin key constants for pymatgen spin-polarized data
|
|
446
|
+
const SPIN_UP_KEYS = [`1`, `Spin.up`]
|
|
447
|
+
const SPIN_DOWN_KEYS = [`-1`, `Spin.down`]
|
|
448
|
+
|
|
449
|
+
// Extract both spin channels from pymatgen spin-keyed data.
|
|
450
|
+
// Returns { up: T, down: T | null } where down is null for non-spin-polarized data.
|
|
451
|
+
export function extract_spin_channels<T>(data: unknown): { up: T; down: T | null } | null {
|
|
452
|
+
if (Array.isArray(data)) return { up: data as T, down: null }
|
|
453
|
+
if (data && typeof data === `object`) {
|
|
454
|
+
const record = data as Record<string, T>
|
|
455
|
+
let spin_up: T | null = null
|
|
456
|
+
let spin_down: T | null = null
|
|
457
|
+
|
|
458
|
+
// Extract spin-up channel
|
|
459
|
+
for (const key of SPIN_UP_KEYS) {
|
|
460
|
+
if (key in record) {
|
|
461
|
+
spin_up = record[key]
|
|
462
|
+
break
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
// Extract spin-down channel
|
|
466
|
+
for (const key of SPIN_DOWN_KEYS) {
|
|
467
|
+
if (key in record) {
|
|
468
|
+
spin_down = record[key]
|
|
469
|
+
break
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Fall back to first key if no spin-up key found
|
|
474
|
+
if (spin_up === null) {
|
|
475
|
+
const keys = Object.keys(record)
|
|
476
|
+
if (keys.length > 0) spin_up = record[keys[0]]
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (spin_up === null) return null
|
|
480
|
+
return { up: spin_up, down: spin_down }
|
|
481
|
+
}
|
|
482
|
+
return null
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Convert pymatgen PhononBandStructureSymmLine or BandStructure to matterviz format
|
|
486
|
+
function convert_pymatgen_band_structure(
|
|
487
|
+
pmg: Record<string, unknown>,
|
|
488
|
+
): types.BaseBandStructure | null {
|
|
489
|
+
// Support both qpoints (phonon) and kpoints (electronic)
|
|
490
|
+
const raw_qpts = (pmg.qpoints ?? pmg.kpoints) as unknown[] | undefined
|
|
491
|
+
|
|
492
|
+
// Handle bands in multiple formats:
|
|
493
|
+
// 1. Standard pymatgen: bands as dict with spin keys {1: [[...], ...]}
|
|
494
|
+
// 2. Custom phonon format: frequencies_cm as 2D array [[...], ...]
|
|
495
|
+
// 3. Already normalized: bands as 2D array [[...], ...]
|
|
496
|
+
const spin_channels = extract_spin_channels<number[][]>(pmg.bands)
|
|
497
|
+
let raw_bands = spin_channels?.up ?? null
|
|
498
|
+
let raw_spin_down_bands = spin_channels?.down ?? null
|
|
499
|
+
const has_frequencies_cm = Array.isArray(pmg.frequencies_cm)
|
|
500
|
+
if (!raw_bands && has_frequencies_cm) {
|
|
501
|
+
// Phonon format: frequencies_cm is [n_qpoints x n_branches] - needs transpose
|
|
502
|
+
const freqs = pmg.frequencies_cm as number[][]
|
|
503
|
+
if (freqs.length > 0 && Array.isArray(freqs[0])) {
|
|
504
|
+
// Transpose: [n_qpoints x n_branches] -> [n_branches x n_qpoints]
|
|
505
|
+
raw_bands = Array.from({ length: freqs[0].length }, (_, band_idx) =>
|
|
506
|
+
freqs.map((qpt_freqs) => qpt_freqs[band_idx]),
|
|
507
|
+
)
|
|
508
|
+
raw_spin_down_bands = null
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const labels_dict = pmg.labels_dict as Record<string, Vec3> | undefined
|
|
513
|
+
const lattice_rec = pmg.lattice_rec as { matrix?: Matrix3x3 } | undefined
|
|
514
|
+
// Determine unit: cm-1 if frequencies_cm present, else check explicit unit or default to THz
|
|
515
|
+
const unit =
|
|
516
|
+
(pmg.unit as string | undefined)?.toLowerCase() ?? (has_frequencies_cm ? `cm-1` : `thz`)
|
|
517
|
+
|
|
518
|
+
if (
|
|
519
|
+
!Array.isArray(raw_qpts) ||
|
|
520
|
+
!Array.isArray(raw_bands) ||
|
|
521
|
+
!raw_qpts.length ||
|
|
522
|
+
!raw_bands.length
|
|
523
|
+
)
|
|
524
|
+
return null
|
|
525
|
+
|
|
526
|
+
const qpoints = raw_qpts
|
|
527
|
+
.map((qpoint) => parse_qpoint(qpoint, labels_dict))
|
|
528
|
+
.filter((qpoint): qpoint is types.QPoint => qpoint !== null)
|
|
529
|
+
if (!qpoints.length) return null
|
|
530
|
+
|
|
531
|
+
// Step distances and discontinuity detection (5x median threshold)
|
|
532
|
+
const steps = qpoints
|
|
533
|
+
.slice(1)
|
|
534
|
+
.map((qpoint, idx) => euclidean_dist(qpoints[idx].frac_coords, qpoint.frac_coords))
|
|
535
|
+
const sorted = steps.slice().sort((a, b) => a - b)
|
|
536
|
+
const threshold = (sorted[Math.floor(sorted.length / 2)] ?? 0) * 5
|
|
537
|
+
const disc_set = new Set(
|
|
538
|
+
steps
|
|
539
|
+
.map((step, idx) => (step > threshold ? idx + 1 : -1))
|
|
540
|
+
.filter((disc_idx) => disc_idx >= 0),
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
// Cumulative distance (skip discontinuities)
|
|
544
|
+
const distance = steps.reduce(
|
|
545
|
+
(acc, step, idx) => [...acc, disc_set.has(idx + 1) ? acc[idx] : acc[idx] + step],
|
|
546
|
+
[0],
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
// Use pymatgen's branches if available - they correctly handle discontinuities
|
|
550
|
+
// Otherwise, infer branches from discontinuities (robust fallback covering all qpoints)
|
|
551
|
+
const pmg_branches = pmg.branches as types.Branch[] | undefined
|
|
552
|
+
let branches: types.Branch[] = []
|
|
553
|
+
|
|
554
|
+
if (Array.isArray(pmg_branches) && pmg_branches.length > 0) {
|
|
555
|
+
// Validate and use pymatgen branches directly
|
|
556
|
+
branches = pmg_branches.filter(
|
|
557
|
+
(branch) =>
|
|
558
|
+
typeof branch.start_index === `number` &&
|
|
559
|
+
typeof branch.end_index === `number` &&
|
|
560
|
+
branch.start_index >= 0 &&
|
|
561
|
+
branch.end_index < qpoints.length &&
|
|
562
|
+
branch.start_index <= branch.end_index,
|
|
563
|
+
)
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Fallback: infer branches from discontinuities when none provided or all invalid
|
|
567
|
+
if (branches.length === 0) {
|
|
568
|
+
console.warn(
|
|
569
|
+
`Band structure missing 'branches' field - inferring from path discontinuities`,
|
|
570
|
+
)
|
|
571
|
+
// Discontinuity indices mark points where the path jumps (disc before that index)
|
|
572
|
+
// Create continuous segments between discontinuities
|
|
573
|
+
const disc_indices = [...disc_set].sort((a, b) => a - b)
|
|
574
|
+
// Segment boundaries: [0, first_disc), [first_disc, second_disc), ..., [last_disc, end]
|
|
575
|
+
const segment_starts = [0, ...disc_indices]
|
|
576
|
+
const segment_ends = [...disc_indices.map((idx) => idx - 1), qpoints.length - 1]
|
|
577
|
+
|
|
578
|
+
branches = segment_starts
|
|
579
|
+
.map((start, idx) => {
|
|
580
|
+
const end = segment_ends[idx]
|
|
581
|
+
const start_label = qpoints[start]?.label ?? `?`
|
|
582
|
+
const end_label = qpoints[end]?.label ?? `?`
|
|
583
|
+
return {
|
|
584
|
+
start_index: start,
|
|
585
|
+
end_index: end,
|
|
586
|
+
name: `${start_label}-${end_label}`,
|
|
587
|
+
}
|
|
588
|
+
})
|
|
589
|
+
.filter((branch) => branch.start_index <= branch.end_index)
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
if (!branches.length) {
|
|
593
|
+
branches.push({ start_index: 0, end_index: qpoints.length - 1, name: `path` })
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Convert bands to THz based on input unit
|
|
597
|
+
const convert_to_thz = (val: number): number => {
|
|
598
|
+
if (unit === `ev`) return val * EV_TO_THZ
|
|
599
|
+
if (unit === `cm-1`) return val * CM_TO_THZ
|
|
600
|
+
return val // THz (default) - no conversion
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const converted_bands = raw_bands.map((band) => band.map(convert_to_thz))
|
|
604
|
+
const valid_spin_down_bands =
|
|
605
|
+
Array.isArray(raw_spin_down_bands) &&
|
|
606
|
+
raw_spin_down_bands.length === raw_bands.length &&
|
|
607
|
+
raw_spin_down_bands.every(
|
|
608
|
+
(band, band_idx) => Array.isArray(band) && band.length === raw_bands[band_idx]?.length,
|
|
609
|
+
)
|
|
610
|
+
? raw_spin_down_bands
|
|
611
|
+
: null
|
|
612
|
+
const converted_spin_down_bands = valid_spin_down_bands?.map((band) =>
|
|
613
|
+
band.map(convert_to_thz),
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
return {
|
|
617
|
+
qpoints,
|
|
618
|
+
branches,
|
|
619
|
+
distance,
|
|
620
|
+
bands: converted_bands,
|
|
621
|
+
spin_down_bands: converted_spin_down_bands,
|
|
622
|
+
nb_bands: raw_bands.length,
|
|
623
|
+
labels_dict: labels_dict ?? {},
|
|
624
|
+
recip_lattice: {
|
|
625
|
+
matrix: lattice_rec?.matrix ?? [
|
|
626
|
+
[1, 0, 0],
|
|
627
|
+
[0, 1, 0],
|
|
628
|
+
[0, 0, 1],
|
|
629
|
+
],
|
|
630
|
+
},
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
export function normalize_band_structure(bs: unknown): types.BaseBandStructure | null {
|
|
635
|
+
if (!bs || typeof bs !== `object`) return null
|
|
636
|
+
|
|
637
|
+
const band_struct = bs as Record<string, unknown>
|
|
638
|
+
|
|
639
|
+
// Check if this is pymatgen format and convert if so
|
|
640
|
+
if (is_pymatgen_format(band_struct)) {
|
|
641
|
+
return convert_pymatgen_band_structure(band_struct)
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Standard matterviz format validation
|
|
645
|
+
const { qpoints, branches, bands, distance } =
|
|
646
|
+
band_struct as Partial<types.BaseBandStructure>
|
|
647
|
+
if (
|
|
648
|
+
!Array.isArray(qpoints) ||
|
|
649
|
+
!Array.isArray(branches) ||
|
|
650
|
+
!Array.isArray(bands) ||
|
|
651
|
+
!Array.isArray(distance)
|
|
652
|
+
)
|
|
653
|
+
return null
|
|
654
|
+
|
|
655
|
+
// Validate array lengths and branch indices
|
|
656
|
+
const n_qpts = qpoints.length
|
|
657
|
+
if (
|
|
658
|
+
distance.length !== n_qpts ||
|
|
659
|
+
bands.some((band) => !Array.isArray(band) || band.length !== n_qpts) ||
|
|
660
|
+
branches.some(
|
|
661
|
+
(branch) =>
|
|
662
|
+
typeof branch.start_index !== `number` ||
|
|
663
|
+
typeof branch.end_index !== `number` ||
|
|
664
|
+
branch.start_index < 0 ||
|
|
665
|
+
branch.end_index >= n_qpts ||
|
|
666
|
+
branch.start_index > branch.end_index,
|
|
667
|
+
)
|
|
668
|
+
)
|
|
669
|
+
return null
|
|
670
|
+
|
|
671
|
+
return band_struct as unknown as types.BaseBandStructure
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Validate and normalize a DOS object.
|
|
675
|
+
// Supports both matterviz and pymatgen formats.
|
|
676
|
+
// Also auto-detects and converts cm⁻¹ to THz for legacy data (disable with auto_convert_units: false).
|
|
677
|
+
export function normalize_dos(
|
|
678
|
+
dos: unknown,
|
|
679
|
+
options: { auto_convert_units?: boolean } = {},
|
|
680
|
+
): types.DosData | null {
|
|
681
|
+
const { auto_convert_units = true } = options
|
|
682
|
+
if (!dos || typeof dos !== `object`) return null
|
|
683
|
+
|
|
684
|
+
const dos_obj = dos as Record<string, unknown>
|
|
685
|
+
|
|
686
|
+
// Check for pymatgen format (has @class or @module)
|
|
687
|
+
const is_pymatgen =
|
|
688
|
+
typeof dos_obj[`@class`] === `string` || typeof dos_obj[`@module`] === `string`
|
|
689
|
+
|
|
690
|
+
const { frequencies, energies, spin_polarized } = dos_obj
|
|
691
|
+
|
|
692
|
+
// Handle densities as either array or dict with spin keys (pymatgen format)
|
|
693
|
+
// Pymatgen stores densities as {1: [...], -1: [...]} or {"Spin.up": [...], ...}
|
|
694
|
+
const spin_channels = extract_spin_channels<number[]>(dos_obj.densities)
|
|
695
|
+
if (!spin_channels) return null
|
|
696
|
+
|
|
697
|
+
const densities = spin_channels.up
|
|
698
|
+
// Use extracted spin-down or fallback to explicit field (for already-normalized DosData)
|
|
699
|
+
const spin_down_densities =
|
|
700
|
+
spin_channels.down ?? (dos_obj.spin_down_densities as number[] | undefined) ?? null
|
|
701
|
+
|
|
702
|
+
if (!Array.isArray(densities)) return null
|
|
703
|
+
|
|
704
|
+
// Phonon DOS: has frequencies
|
|
705
|
+
if (Array.isArray(frequencies)) {
|
|
706
|
+
if (frequencies.length !== densities.length) return null
|
|
707
|
+
|
|
708
|
+
// Auto-detect if frequencies are in cm⁻¹ instead of THz (unless disabled)
|
|
709
|
+
// Typical phonon frequencies are < 50 THz for most materials
|
|
710
|
+
// If max frequency > 100, it's almost certainly in cm⁻¹
|
|
711
|
+
const max_freq = Math.max(...(frequencies as number[]))
|
|
712
|
+
let final_frequencies = frequencies as number[]
|
|
713
|
+
|
|
714
|
+
if (auto_convert_units && max_freq > 100) {
|
|
715
|
+
// Likely in cm⁻¹, convert to THz
|
|
716
|
+
final_frequencies = (frequencies as number[]).map((frequency) => frequency * CM_TO_THZ)
|
|
717
|
+
console.warn(
|
|
718
|
+
`Phonon DOS frequencies appear to be in cm⁻¹ (max: ${max_freq.toFixed(1)}). ` +
|
|
719
|
+
`Converting to THz (max: ${(max_freq * CM_TO_THZ).toFixed(1)} THz).`,
|
|
720
|
+
)
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
return { type: `phonon`, frequencies: final_frequencies, densities }
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// Electronic DOS: has energies
|
|
727
|
+
if (Array.isArray(energies)) {
|
|
728
|
+
if (energies.length !== densities.length) return null
|
|
729
|
+
// Detect spin-polarized from data if not explicitly set
|
|
730
|
+
const is_spin_polarized =
|
|
731
|
+
(spin_polarized as boolean | undefined) ??
|
|
732
|
+
(spin_down_densities !== null && spin_down_densities.length === densities.length)
|
|
733
|
+
return {
|
|
734
|
+
type: `electronic`,
|
|
735
|
+
energies,
|
|
736
|
+
densities,
|
|
737
|
+
spin_down_densities: is_spin_polarized ? (spin_down_densities ?? undefined) : undefined,
|
|
738
|
+
spin_polarized: is_spin_polarized,
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// For pymatgen format, log a helpful message if format wasn't recognized
|
|
743
|
+
if (is_pymatgen) {
|
|
744
|
+
console.warn(
|
|
745
|
+
`Pymatgen DOS format detected but missing required fields. ` +
|
|
746
|
+
`Expected 'frequencies' (phonon) or 'energies' (electronic) arrays.`,
|
|
747
|
+
)
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
return null
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// Extract k-path points from band structure and convert to reciprocal space coordinates
|
|
754
|
+
// Accepts a reciprocal lattice matrix (should include 2π factor for consistency with BZ)
|
|
755
|
+
// Handles both matterviz format (qpoints as objects) and normalized pymatgen format
|
|
756
|
+
// Optionally wraps fractional coordinates to first BZ (default: true)
|
|
757
|
+
export function extract_k_path_points(
|
|
758
|
+
band_struct: types.BaseBandStructure,
|
|
759
|
+
recip_lattice_matrix: Matrix3x3,
|
|
760
|
+
options: { wrap_to_bz?: boolean } = {},
|
|
761
|
+
): Vec3[] {
|
|
762
|
+
const { wrap_to_bz = true } = options
|
|
763
|
+
if (!band_struct?.qpoints || !recip_lattice_matrix) return []
|
|
764
|
+
|
|
765
|
+
if (
|
|
766
|
+
recip_lattice_matrix.length !== 3 ||
|
|
767
|
+
recip_lattice_matrix.some((row) => row?.length !== 3)
|
|
768
|
+
)
|
|
769
|
+
throw new Error(`reciprocal_lattice_matrix must be a 3×3 matrix`)
|
|
770
|
+
|
|
771
|
+
const [[m00, m01, m02], [m10, m11, m12], [m20, m21, m22]] = recip_lattice_matrix
|
|
772
|
+
|
|
773
|
+
return band_struct.qpoints.map((qpoint): Vec3 => {
|
|
774
|
+
let [x, y, z] = qpoint.frac_coords
|
|
775
|
+
// Wrap to first BZ if enabled (handles [0,1] vs [-0.5,0.5] convention difference)
|
|
776
|
+
if (wrap_to_bz) {
|
|
777
|
+
x = centered_frac(x)
|
|
778
|
+
y = centered_frac(y)
|
|
779
|
+
z = centered_frac(z)
|
|
780
|
+
}
|
|
781
|
+
const kx = x * m00 + y * m10 + z * m20
|
|
782
|
+
const ky = x * m01 + y * m11 + z * m21
|
|
783
|
+
const kz = x * m02 + y * m12 + z * m22
|
|
784
|
+
return [kx, ky, kz]
|
|
785
|
+
})
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// Find the q-point index closest to a given distance along the band structure path
|
|
789
|
+
export function find_qpoint_at_distance(
|
|
790
|
+
band_struct: types.BaseBandStructure,
|
|
791
|
+
target: number,
|
|
792
|
+
): number | null {
|
|
793
|
+
const { distance } = band_struct
|
|
794
|
+
if (!distance?.length) return null
|
|
795
|
+
|
|
796
|
+
return distance.reduce(
|
|
797
|
+
(closest: number, dist: number, idx: number) =>
|
|
798
|
+
Math.abs(dist - target) < Math.abs(distance[closest] - target) ? idx : closest,
|
|
799
|
+
0,
|
|
800
|
+
)
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// Find q-point index from rescaled x-coordinate (used in band structure plots)
|
|
804
|
+
// This handles the case where the plot uses custom x-axis scaling per segment
|
|
805
|
+
export function find_qpoint_at_rescaled_x(
|
|
806
|
+
band_struct: types.BaseBandStructure,
|
|
807
|
+
rescaled_x: number,
|
|
808
|
+
x_positions: Record<string, [number, number]>,
|
|
809
|
+
): number | null {
|
|
810
|
+
if (!band_struct?.branches?.length || !x_positions) return null
|
|
811
|
+
|
|
812
|
+
// Find which segment contains this x coordinate
|
|
813
|
+
for (const branch of band_struct.branches) {
|
|
814
|
+
const start_idx = branch.start_index
|
|
815
|
+
const end_idx = branch.end_index
|
|
816
|
+
const start_label = band_struct.qpoints[start_idx]?.label ?? undefined
|
|
817
|
+
const end_label = band_struct.qpoints[end_idx]?.label ?? undefined
|
|
818
|
+
const segment_key = get_segment_key(start_label, end_label)
|
|
819
|
+
|
|
820
|
+
const segment_range = x_positions[segment_key]
|
|
821
|
+
if (!segment_range) continue
|
|
822
|
+
|
|
823
|
+
const [x_start, x_end] = segment_range
|
|
824
|
+
|
|
825
|
+
// Check if discontinuity (zero-length segment)
|
|
826
|
+
const is_discontinuity = Math.abs(x_end - x_start) < 1e-6
|
|
827
|
+
if (is_discontinuity) {
|
|
828
|
+
// For discontinuities, check if x is exactly at this point
|
|
829
|
+
if (Math.abs(rescaled_x - x_start) < 1e-6) {
|
|
830
|
+
return start_idx
|
|
831
|
+
}
|
|
832
|
+
continue
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// Check if x is within this segment (with small tolerance for edges)
|
|
836
|
+
if (rescaled_x >= x_start - 1e-6 && rescaled_x <= x_end + 1e-6) {
|
|
837
|
+
// Map from rescaled x back to original distance
|
|
838
|
+
const segment_distances = band_struct.distance.slice(start_idx, end_idx + 1)
|
|
839
|
+
const dist_min = segment_distances[0]
|
|
840
|
+
const dist_max = segment_distances.at(-1) ?? dist_min
|
|
841
|
+
const dist_range = dist_max - dist_min
|
|
842
|
+
|
|
843
|
+
// Handle zero-length segments
|
|
844
|
+
if (dist_range === 0) {
|
|
845
|
+
return start_idx
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// Inverse of the scaling: x = x_start + ((dist - dist_min) / dist_range) * (x_end - x_start)
|
|
849
|
+
// Solving for dist: dist = dist_min + ((x - x_start) / (x_end - x_start)) * dist_range
|
|
850
|
+
const normalized_x = (rescaled_x - x_start) / (x_end - x_start)
|
|
851
|
+
const target_dist = dist_min + normalized_x * dist_range
|
|
852
|
+
|
|
853
|
+
// Find closest qpoint in this branch to the target distance
|
|
854
|
+
let closest_idx = start_idx
|
|
855
|
+
let min_diff = Math.abs(band_struct.distance[start_idx] - target_dist)
|
|
856
|
+
|
|
857
|
+
for (let idx = start_idx; idx <= end_idx; idx++) {
|
|
858
|
+
const diff = Math.abs(band_struct.distance[idx] - target_dist)
|
|
859
|
+
if (diff < min_diff) {
|
|
860
|
+
min_diff = diff
|
|
861
|
+
closest_idx = idx
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
return closest_idx
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Fallback: find closest labeled point
|
|
870
|
+
let closest_idx = 0
|
|
871
|
+
let min_dist = Infinity
|
|
872
|
+
|
|
873
|
+
for (const branch of band_struct.branches) {
|
|
874
|
+
const start_idx = branch.start_index
|
|
875
|
+
const end_idx = branch.end_index
|
|
876
|
+
const start_label = band_struct.qpoints[start_idx]?.label ?? undefined
|
|
877
|
+
const end_label = band_struct.qpoints[end_idx]?.label ?? undefined
|
|
878
|
+
const segment_key = get_segment_key(start_label, end_label)
|
|
879
|
+
const segment_range = x_positions[segment_key]
|
|
880
|
+
|
|
881
|
+
if (!segment_range) continue
|
|
882
|
+
|
|
883
|
+
const [x_start, x_end] = segment_range
|
|
884
|
+
|
|
885
|
+
for (const [x_pos, idx] of [
|
|
886
|
+
[x_start, start_idx],
|
|
887
|
+
[x_end, end_idx],
|
|
888
|
+
] as const) {
|
|
889
|
+
const dist = Math.abs(rescaled_x - x_pos)
|
|
890
|
+
if (dist < min_dist) {
|
|
891
|
+
min_dist = dist
|
|
892
|
+
closest_idx = idx
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
return closest_idx
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// Type definitions for pymatgen DOS formats
|
|
901
|
+
// Densities can be spin-keyed: {1: number[], -1: number[]} or {"Spin.up": number[], ...}
|
|
902
|
+
type SpinDensities = Record<string, number[]>
|
|
903
|
+
|
|
904
|
+
// Pymatgen Dos base class format
|
|
905
|
+
export interface PymatgenDos {
|
|
906
|
+
'@class': string
|
|
907
|
+
'@module': string
|
|
908
|
+
energies: number[]
|
|
909
|
+
densities: SpinDensities | number[]
|
|
910
|
+
efermi: number
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// Pymatgen CompleteDos format (includes projected DOS)
|
|
914
|
+
export interface PymatgenCompleteDos extends PymatgenDos {
|
|
915
|
+
'@class': `CompleteDos` | `LobsterCompleteDos`
|
|
916
|
+
structure?: Record<string, unknown>
|
|
917
|
+
pdos?: Record<string, SpinDensities>[]
|
|
918
|
+
atom_dos?: Record<string, PymatgenDos>
|
|
919
|
+
spd_dos?: Record<string, PymatgenDos>
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// Extract projected DOS from pymatgen CompleteDos format.
|
|
923
|
+
// Returns a dict of label → DosData for each atom or orbital.
|
|
924
|
+
// filter_keys: optional list of keys to include (e.g., ["Fe", "O"] for atoms or ["s", "p", "d"] for orbitals)
|
|
925
|
+
export function extract_pdos(
|
|
926
|
+
dos: unknown,
|
|
927
|
+
pdos_type: types.PdosType,
|
|
928
|
+
filter_keys?: string[],
|
|
929
|
+
): Record<string, types.ElectronicDos> | null {
|
|
930
|
+
if (!dos || typeof dos !== `object`) return null
|
|
931
|
+
|
|
932
|
+
const dos_obj = dos as Record<string, unknown>
|
|
933
|
+
|
|
934
|
+
// Get the appropriate projected DOS dict
|
|
935
|
+
const pdos_dict =
|
|
936
|
+
pdos_type === `atom`
|
|
937
|
+
? (dos_obj.atom_dos as Record<string, PymatgenDos> | undefined)
|
|
938
|
+
: (dos_obj.spd_dos as Record<string, PymatgenDos> | undefined)
|
|
939
|
+
|
|
940
|
+
if (!pdos_dict || typeof pdos_dict !== `object`) return null
|
|
941
|
+
|
|
942
|
+
const result: Record<string, types.ElectronicDos> = {}
|
|
943
|
+
|
|
944
|
+
for (const [key, nested_dos] of Object.entries(pdos_dict)) {
|
|
945
|
+
// Apply filter if provided
|
|
946
|
+
if (filter_keys && filter_keys.length > 0 && !filter_keys.includes(key)) continue
|
|
947
|
+
|
|
948
|
+
if (!nested_dos || typeof nested_dos !== `object`) continue
|
|
949
|
+
|
|
950
|
+
const energies = nested_dos.energies
|
|
951
|
+
const spin_channels = extract_spin_channels<number[]>(nested_dos.densities)
|
|
952
|
+
|
|
953
|
+
if (!Array.isArray(energies) || !spin_channels) continue
|
|
954
|
+
|
|
955
|
+
const densities = spin_channels.up
|
|
956
|
+
if (!Array.isArray(densities) || energies.length !== densities.length) continue
|
|
957
|
+
|
|
958
|
+
const is_spin_polarized =
|
|
959
|
+
spin_channels.down !== null && spin_channels.down.length === densities.length
|
|
960
|
+
|
|
961
|
+
result[key] = {
|
|
962
|
+
type: `electronic`,
|
|
963
|
+
energies,
|
|
964
|
+
densities,
|
|
965
|
+
spin_down_densities: is_spin_polarized ? (spin_channels.down ?? undefined) : undefined,
|
|
966
|
+
spin_polarized: is_spin_polarized,
|
|
967
|
+
efermi: nested_dos.efermi,
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
return Object.keys(result).length > 0 ? result : null
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// Shift a single DOS object's energies by the given amount
|
|
975
|
+
const shift_dos_energies = <T extends PymatgenDos>(dos: T, shift: number): T => ({
|
|
976
|
+
...dos,
|
|
977
|
+
efermi: dos.efermi - shift,
|
|
978
|
+
energies: dos.energies.map((energy) => energy - shift),
|
|
979
|
+
})
|
|
980
|
+
|
|
981
|
+
// Shift DOS energies relative to Fermi energy so E_F = 0
|
|
982
|
+
// Recursively shifts nested DOS in atom_dos and spd_dos for consistency
|
|
983
|
+
export function shift_to_fermi(dos: PymatgenCompleteDos): PymatgenCompleteDos {
|
|
984
|
+
const shift = dos.efermi
|
|
985
|
+
|
|
986
|
+
// Shift root DOS energies using the shared helper
|
|
987
|
+
const shifted_root = shift_dos_energies(dos, shift)
|
|
988
|
+
|
|
989
|
+
// Shift nested atom_dos if present
|
|
990
|
+
const atom_dos = dos.atom_dos
|
|
991
|
+
? Object.fromEntries(
|
|
992
|
+
Object.entries(dos.atom_dos).map(([key, nested_dos]) => [
|
|
993
|
+
key,
|
|
994
|
+
shift_dos_energies(nested_dos, shift),
|
|
995
|
+
]),
|
|
996
|
+
)
|
|
997
|
+
: undefined
|
|
998
|
+
|
|
999
|
+
// Shift nested spd_dos if present
|
|
1000
|
+
const spd_dos = dos.spd_dos
|
|
1001
|
+
? Object.fromEntries(
|
|
1002
|
+
Object.entries(dos.spd_dos).map(([key, nested_dos]) => [
|
|
1003
|
+
key,
|
|
1004
|
+
shift_dos_energies(nested_dos, shift),
|
|
1005
|
+
]),
|
|
1006
|
+
)
|
|
1007
|
+
: undefined
|
|
1008
|
+
|
|
1009
|
+
return {
|
|
1010
|
+
...shifted_root,
|
|
1011
|
+
efermi: 0, // Explicitly set to 0 (shift_dos_energies would give efermi - shift)
|
|
1012
|
+
...(atom_dos && { atom_dos }),
|
|
1013
|
+
...(spd_dos && { spd_dos }),
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// Generate an SVG path for a fat band ribbon.
|
|
1018
|
+
// Creates a closed polygon by tracing the upper edge (y - half_width) forward,
|
|
1019
|
+
// then tracing the lower edge (y + half_width) backward.
|
|
1020
|
+
// Non-finite or non-positive widths are clamped to 0.
|
|
1021
|
+
export function generate_ribbon_path(
|
|
1022
|
+
x_values: number[],
|
|
1023
|
+
y_values: number[],
|
|
1024
|
+
width_values: number[],
|
|
1025
|
+
x_scale_fn: (x: number) => number,
|
|
1026
|
+
y_scale_fn: (y: number) => number,
|
|
1027
|
+
max_width_px: number,
|
|
1028
|
+
scale: number = 1,
|
|
1029
|
+
): string {
|
|
1030
|
+
const len = x_values.length
|
|
1031
|
+
if (len < 2 || len !== y_values.length || len !== width_values.length) return ``
|
|
1032
|
+
|
|
1033
|
+
// Normalize width values to [0, 1] range based on the max positive finite value
|
|
1034
|
+
const finite_positive_widths = width_values.filter(
|
|
1035
|
+
(width) => Number.isFinite(width) && width > 0,
|
|
1036
|
+
)
|
|
1037
|
+
if (finite_positive_widths.length === 0) return ``
|
|
1038
|
+
const max_width_val = Math.max(...finite_positive_widths)
|
|
1039
|
+
|
|
1040
|
+
// Build upper edge path (forward direction)
|
|
1041
|
+
const upper_points: string[] = []
|
|
1042
|
+
const lower_points: string[] = []
|
|
1043
|
+
|
|
1044
|
+
for (let idx = 0; idx < x_values.length; idx++) {
|
|
1045
|
+
const x_px = x_scale_fn(x_values[idx])
|
|
1046
|
+
const y_data = y_values[idx]
|
|
1047
|
+
const raw_width = width_values[idx] ?? 0
|
|
1048
|
+
const width_normalized =
|
|
1049
|
+
Number.isFinite(raw_width) && raw_width > 0 ? raw_width / max_width_val : 0
|
|
1050
|
+
const half_width_px = width_normalized * max_width_px * scale
|
|
1051
|
+
|
|
1052
|
+
// In SVG, y increases downward, so upper edge has smaller y value
|
|
1053
|
+
const y_upper_px = y_scale_fn(y_data) - half_width_px
|
|
1054
|
+
const y_lower_px = y_scale_fn(y_data) + half_width_px
|
|
1055
|
+
|
|
1056
|
+
upper_points.push(`${x_px.toFixed(2)},${y_upper_px.toFixed(2)}`)
|
|
1057
|
+
lower_points.push(`${x_px.toFixed(2)},${y_lower_px.toFixed(2)}`)
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// Combine: upper edge forward, lower edge backward, close path
|
|
1061
|
+
const path_parts = [
|
|
1062
|
+
`M${upper_points[0]}`,
|
|
1063
|
+
...upper_points.slice(1).map((pt) => `L${pt}`),
|
|
1064
|
+
...lower_points.toReversed().map((pt) => `L${pt}`),
|
|
1065
|
+
`Z`,
|
|
1066
|
+
]
|
|
1067
|
+
|
|
1068
|
+
return path_parts.join(` `)
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
// Extract efermi from a data source (band structure or DOS).
|
|
1072
|
+
// Handles both single objects with an efermi field and dicts of objects.
|
|
1073
|
+
// Returns undefined if no valid efermi is found or if the source is empty.
|
|
1074
|
+
export function extract_efermi(data: unknown): number | undefined {
|
|
1075
|
+
if (!data || typeof data !== `object`) return undefined
|
|
1076
|
+
const obj = data as Record<string, unknown>
|
|
1077
|
+
|
|
1078
|
+
// Direct efermi field on the object
|
|
1079
|
+
if (`efermi` in obj && typeof obj.efermi === `number`) return obj.efermi
|
|
1080
|
+
|
|
1081
|
+
// Dict of objects - try to get efermi from first value
|
|
1082
|
+
const values = Object.values(obj)
|
|
1083
|
+
if (values.length === 0) return undefined
|
|
1084
|
+
|
|
1085
|
+
const first_val = values[0]
|
|
1086
|
+
if (first_val && typeof first_val === `object`) {
|
|
1087
|
+
const efermi = (first_val as Record<string, unknown>).efermi
|
|
1088
|
+
if (typeof efermi === `number`) return efermi
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
return undefined
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
// Calculate fraction of |values| that are negative. Used to detect imaginary phonon modes.
|
|
1095
|
+
export function negative_fraction(values: number[]): number {
|
|
1096
|
+
let [neg, total] = [0, 0]
|
|
1097
|
+
for (const val of values) {
|
|
1098
|
+
if (!Number.isFinite(val)) continue
|
|
1099
|
+
const abs_val = Math.abs(val)
|
|
1100
|
+
total += abs_val
|
|
1101
|
+
if (val < 0) neg += abs_val
|
|
1102
|
+
}
|
|
1103
|
+
return total > 0 ? neg / total : 0
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// Check if raw band structure input has electronic markers (efermi, kpoints, or electronic @class).
|
|
1107
|
+
// Must be called on raw input before normalization since these fields aren't preserved.
|
|
1108
|
+
function is_electronic_band_struct(bs: unknown): boolean {
|
|
1109
|
+
if (!bs || typeof bs !== `object`) return false
|
|
1110
|
+
const obj = bs as Record<string, unknown>
|
|
1111
|
+
// Electronic band structures have efermi field
|
|
1112
|
+
if (`efermi` in obj && typeof obj.efermi === `number`) return true
|
|
1113
|
+
// Pymatgen electronic format uses kpoints (not qpoints)
|
|
1114
|
+
if (`kpoints` in obj && Array.isArray(obj.kpoints) && obj.kpoints.length > 0) {
|
|
1115
|
+
return true
|
|
1116
|
+
}
|
|
1117
|
+
// Pymatgen @class: BandStructure* but not Phonon*
|
|
1118
|
+
const raw_class = obj[`@class`]
|
|
1119
|
+
const py_class_name = typeof raw_class === `string` ? raw_class : ``
|
|
1120
|
+
if (py_class_name.startsWith(`BandStructure`) && !py_class_name.includes(`Phonon`)) {
|
|
1121
|
+
return true
|
|
1122
|
+
}
|
|
1123
|
+
return false
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
// Compute frequency/energy range from bands and DOS. Clamps phonon min to 0 if noise < 0.5%.
|
|
1127
|
+
export function compute_frequency_range(
|
|
1128
|
+
band_structs: unknown,
|
|
1129
|
+
doses: unknown,
|
|
1130
|
+
padding_factor = 0.02,
|
|
1131
|
+
): [number, number] | undefined {
|
|
1132
|
+
let [min_val, max_val, is_phonon] = [Infinity, -Infinity, false]
|
|
1133
|
+
const all_freqs: number[] = []
|
|
1134
|
+
|
|
1135
|
+
// Check raw band_structs for electronic markers before normalization
|
|
1136
|
+
// (normalized structures always have qpoints, so we can't detect from them)
|
|
1137
|
+
let has_electronic_bs = false
|
|
1138
|
+
// Support both qpoints (phonon) and kpoints (electronic) to detect single vs dict
|
|
1139
|
+
const is_single_bs =
|
|
1140
|
+
band_structs &&
|
|
1141
|
+
typeof band_structs === `object` &&
|
|
1142
|
+
(`qpoints` in band_structs || `kpoints` in band_structs)
|
|
1143
|
+
if (band_structs && typeof band_structs === `object`) {
|
|
1144
|
+
// Single structure check
|
|
1145
|
+
if (is_electronic_band_struct(band_structs)) {
|
|
1146
|
+
has_electronic_bs = true
|
|
1147
|
+
} else if (!is_single_bs) {
|
|
1148
|
+
// Dict of band structures - check each value
|
|
1149
|
+
for (const bs_val of Object.values(band_structs)) {
|
|
1150
|
+
if (is_electronic_band_struct(bs_val)) {
|
|
1151
|
+
has_electronic_bs = true
|
|
1152
|
+
break
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
const bs_list = band_structs
|
|
1159
|
+
? is_single_bs
|
|
1160
|
+
? [normalize_band_structure(band_structs)]
|
|
1161
|
+
: Object.values(band_structs as object).map(normalize_band_structure)
|
|
1162
|
+
: []
|
|
1163
|
+
|
|
1164
|
+
// If band structures exist and aren't electronic, mark as phonon
|
|
1165
|
+
const has_band_structs = bs_list.some(Boolean)
|
|
1166
|
+
if (has_band_structs && !has_electronic_bs) is_phonon = true
|
|
1167
|
+
|
|
1168
|
+
for (const bs of bs_list) {
|
|
1169
|
+
if (!bs) continue
|
|
1170
|
+
for (const band of bs.bands) {
|
|
1171
|
+
for (const val of band) {
|
|
1172
|
+
if (!Number.isFinite(val)) continue
|
|
1173
|
+
all_freqs.push(val)
|
|
1174
|
+
min_val = Math.min(min_val, val)
|
|
1175
|
+
max_val = Math.max(max_val, val)
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
const dos_list = doses
|
|
1181
|
+
? `densities` in (doses as object)
|
|
1182
|
+
? [normalize_dos(doses)]
|
|
1183
|
+
: Object.values(doses as object).map((dos) => normalize_dos(dos))
|
|
1184
|
+
: []
|
|
1185
|
+
for (const dos of dos_list) {
|
|
1186
|
+
if (!dos) continue
|
|
1187
|
+
// DOS type detection: explicit type field is authoritative
|
|
1188
|
+
if (dos.type === `phonon`) is_phonon = true
|
|
1189
|
+
if (dos.type === `electronic`) is_phonon = false
|
|
1190
|
+
for (const val of dos.type === `phonon` ? dos.frequencies : dos.energies) {
|
|
1191
|
+
if (!Number.isFinite(val)) continue
|
|
1192
|
+
all_freqs.push(val)
|
|
1193
|
+
min_val = Math.min(min_val, val)
|
|
1194
|
+
max_val = Math.max(max_val, val)
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
if (!Number.isFinite(min_val) || !Number.isFinite(max_val)) return undefined
|
|
1199
|
+
const clamp_min =
|
|
1200
|
+
is_phonon &&
|
|
1201
|
+
min_val < 0 && // clamp phonon noise to 0
|
|
1202
|
+
negative_fraction(all_freqs) < IMAGINARY_MODE_NOISE_THRESHOLD
|
|
1203
|
+
if (clamp_min) min_val = 0
|
|
1204
|
+
// Calculate padding from (possibly clamped) range for consistency with Bands.svelte
|
|
1205
|
+
const padding = (max_val - min_val) * padding_factor
|
|
1206
|
+
return [min_val === 0 ? 0 : min_val - padding, max_val + padding]
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
// Parse axis label: "Frequency (THz)" → { name: "Frequency", unit: "THz" }
|
|
1210
|
+
function parse_axis_label(label: string): { name: string; unit?: string } {
|
|
1211
|
+
const match = /^(.+?)\s*\(([^)]+)\)$/.exec(label)
|
|
1212
|
+
return match ? { name: match[1], unit: match[2] } : { name: label }
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
const format_tooltip_line = (name: string, value: string, unit?: string) =>
|
|
1216
|
+
`${name}: ${value}${unit ? ` ${unit}` : ``}`
|
|
1217
|
+
|
|
1218
|
+
// Format DOS tooltip content from axis labels and values
|
|
1219
|
+
export function format_dos_tooltip(
|
|
1220
|
+
x_formatted: string,
|
|
1221
|
+
y_formatted: string,
|
|
1222
|
+
label: string | null,
|
|
1223
|
+
is_horizontal: boolean,
|
|
1224
|
+
is_phonon: boolean,
|
|
1225
|
+
units: types.FrequencyUnit,
|
|
1226
|
+
x_axis_label: string,
|
|
1227
|
+
y_axis_label: string,
|
|
1228
|
+
num_series: number,
|
|
1229
|
+
): { title?: string; lines: string[] } {
|
|
1230
|
+
const x_parsed = parse_axis_label(x_axis_label)
|
|
1231
|
+
const y_parsed = parse_axis_label(y_axis_label)
|
|
1232
|
+
const freq_defaults = {
|
|
1233
|
+
name: is_phonon ? `Frequency` : `Energy`,
|
|
1234
|
+
unit: is_phonon ? units : `eV`,
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
const lines = is_horizontal
|
|
1238
|
+
? [
|
|
1239
|
+
format_tooltip_line(
|
|
1240
|
+
y_parsed.name || freq_defaults.name,
|
|
1241
|
+
y_formatted,
|
|
1242
|
+
y_parsed.unit || freq_defaults.unit,
|
|
1243
|
+
),
|
|
1244
|
+
format_tooltip_line(x_parsed.name || `Density`, x_formatted),
|
|
1245
|
+
]
|
|
1246
|
+
: [
|
|
1247
|
+
format_tooltip_line(y_parsed.name || `Density`, y_formatted),
|
|
1248
|
+
format_tooltip_line(
|
|
1249
|
+
x_parsed.name || freq_defaults.name,
|
|
1250
|
+
x_formatted,
|
|
1251
|
+
x_parsed.unit || freq_defaults.unit,
|
|
1252
|
+
),
|
|
1253
|
+
]
|
|
1254
|
+
|
|
1255
|
+
return { title: num_series > 1 && label ? label : undefined, lines }
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// Spin mode options for DOS visualization
|
|
1259
|
+
export const SPIN_MODES = [
|
|
1260
|
+
{ value: `mirror`, label: `↕`, title: `Mirror: spin-up above, spin-down below zero` },
|
|
1261
|
+
{ value: `overlay`, label: `≡`, title: `Overlay: both spins on same axis` },
|
|
1262
|
+
{ value: `up_only`, label: `↑`, title: `Show spin-up only` },
|
|
1263
|
+
{ value: `down_only`, label: `↓`, title: `Show spin-down only` },
|
|
1264
|
+
] as const satisfies readonly { value: types.SpinMode; label: string; title: string }[]
|
|
1265
|
+
|
|
1266
|
+
// Normalization mode options
|
|
1267
|
+
export const NORMALIZATION_MODES = [
|
|
1268
|
+
{ value: null, label: `None` },
|
|
1269
|
+
{ value: `max`, label: `Max=1` },
|
|
1270
|
+
{ value: `sum`, label: `Sum=1` },
|
|
1271
|
+
{ value: `integral`, label: `∫=1` },
|
|
1272
|
+
] as const satisfies readonly { value: types.NormalizationMode; label: string }[]
|
|
1273
|
+
|
|
1274
|
+
// Available frequency units for phonon DOS
|
|
1275
|
+
export const FREQUENCY_UNITS: types.FrequencyUnit[] = [`THz`, `eV`, `meV`, `cm-1`, `Ha`]
|
|
1276
|
+
|
|
1277
|
+
// Default values for DOS controls
|
|
1278
|
+
export const DEFAULT_SPIN_MODE: types.SpinMode = `mirror`
|
|
1279
|
+
export const DEFAULT_SIGMA = 0
|
|
1280
|
+
export const DEFAULT_NORMALIZE: types.NormalizationMode = null
|
|
1281
|
+
export const DEFAULT_UNITS: types.FrequencyUnit = `THz`
|
|
1282
|
+
|
|
1283
|
+
// Format sigma with adaptive precision: 0→"0", <0.01→exp, <1→3dp, else→2dp
|
|
1284
|
+
export function format_sigma(val: number): string {
|
|
1285
|
+
if (val === 0) return `0`
|
|
1286
|
+
if (val < 0.01) return val.toExponential(1)
|
|
1287
|
+
return val.toFixed(val < 1 ? 3 : 2)
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
// Validate sigma_range: ensures min < max, returns [0, 1] if invalid
|
|
1291
|
+
export const validate_sigma_range = ([min, max]: [number, number]): [number, number] =>
|
|
1292
|
+
Number.isFinite(min) && Number.isFinite(max) && min < max ? [min, max] : [0, 1]
|
|
1293
|
+
|
|
1294
|
+
// Calculate slider step: 1/100th of range, or 0.01 fallback
|
|
1295
|
+
export function calculate_sigma_step(range: [number, number]): number {
|
|
1296
|
+
const [min, max] = validate_sigma_range(range)
|
|
1297
|
+
return (max - min) / 100 || 0.01
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
// === Band Tooltip Helpers ===
|
|
1301
|
+
|
|
1302
|
+
// Per-point metadata for band tooltip display
|
|
1303
|
+
export interface BandPointMeta extends Record<string, unknown> {
|
|
1304
|
+
band_idx: number
|
|
1305
|
+
spin: `up` | `down`
|
|
1306
|
+
is_acoustic: boolean | null
|
|
1307
|
+
nb_bands: number
|
|
1308
|
+
frac_coords: Vec3 | null
|
|
1309
|
+
qpoint_label: string | null
|
|
1310
|
+
band_width: number | null
|
|
1311
|
+
slope: number | null
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// Central difference for local slope (dω/dk or dE/dk).
|
|
1315
|
+
// Uses forward/backward difference at endpoints, central difference for interior points.
|
|
1316
|
+
export function compute_slope(x_vals: number[], y_vals: number[], idx: number): number | null {
|
|
1317
|
+
const len = Math.min(x_vals.length, y_vals.length)
|
|
1318
|
+
if (len < 2 || idx < 0 || idx >= len) return null
|
|
1319
|
+
const lo = idx === 0 ? 0 : idx - 1
|
|
1320
|
+
const hi = idx >= len - 1 ? len - 1 : idx + 1
|
|
1321
|
+
const dx = x_vals[hi] - x_vals[lo]
|
|
1322
|
+
return dx ? (y_vals[hi] - y_vals[lo]) / dx : null
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// Find Gamma-point indices (q ≈ integer lattice point) in a band structure.
|
|
1326
|
+
// Returns indices of q-points whose fractional coordinates are all within 0.01 of integers.
|
|
1327
|
+
export function find_gamma_indices(bs: types.BaseBandStructure): number[] {
|
|
1328
|
+
const indices: number[] = []
|
|
1329
|
+
for (let q_idx = 0; q_idx < bs.qpoints.length; q_idx++) {
|
|
1330
|
+
const coords = bs.qpoints[q_idx]?.frac_coords
|
|
1331
|
+
if (coords?.every((coord) => Math.abs(coord - Math.round(coord)) < 0.01)) {
|
|
1332
|
+
indices.push(q_idx)
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return indices
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
// Threshold below which a band's frequency at Gamma is considered acoustic (THz).
|
|
1339
|
+
// Assumes bands are stored in THz (normalize_band_structure converts to THz).
|
|
1340
|
+
export const ACOUSTIC_FREQ_THRESHOLD = 0.5
|
|
1341
|
+
|
|
1342
|
+
// Classify a band as acoustic based on near-zero frequency at Gamma points.
|
|
1343
|
+
// Returns true (acoustic), false (optical), or null (no Gamma points → can't determine).
|
|
1344
|
+
export function classify_acoustic(
|
|
1345
|
+
bs: types.BaseBandStructure,
|
|
1346
|
+
band_idx: number,
|
|
1347
|
+
gamma_indices: number[],
|
|
1348
|
+
threshold = ACOUSTIC_FREQ_THRESHOLD,
|
|
1349
|
+
): boolean | null {
|
|
1350
|
+
if (gamma_indices.length === 0) return null
|
|
1351
|
+
return gamma_indices.some(
|
|
1352
|
+
(gamma_idx) => Math.abs(bs.bands[band_idx]?.[gamma_idx] ?? Infinity) < threshold,
|
|
1353
|
+
)
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
// Build per-point metadata array for a band series in the tooltip.
|
|
1357
|
+
export function build_point_metadata(opts: {
|
|
1358
|
+
x_vals: number[]
|
|
1359
|
+
y_vals: number[]
|
|
1360
|
+
band_idx: number
|
|
1361
|
+
spin: `up` | `down`
|
|
1362
|
+
is_acoustic: boolean | null
|
|
1363
|
+
bs: types.BaseBandStructure
|
|
1364
|
+
start_idx: number
|
|
1365
|
+
}): BandPointMeta[] {
|
|
1366
|
+
const { x_vals, y_vals, band_idx, spin, is_acoustic, bs, start_idx } = opts
|
|
1367
|
+
return x_vals.map((_, pt_idx) => {
|
|
1368
|
+
const global_idx = start_idx + pt_idx
|
|
1369
|
+
const qpoint = bs.qpoints[global_idx]
|
|
1370
|
+
return {
|
|
1371
|
+
band_idx,
|
|
1372
|
+
spin,
|
|
1373
|
+
is_acoustic,
|
|
1374
|
+
nb_bands: bs.nb_bands,
|
|
1375
|
+
frac_coords: qpoint?.frac_coords ?? null,
|
|
1376
|
+
qpoint_label: qpoint?.label ?? null,
|
|
1377
|
+
band_width: bs.band_widths?.[band_idx]?.[global_idx] ?? null,
|
|
1378
|
+
slope: compute_slope(x_vals, y_vals, pt_idx),
|
|
1379
|
+
}
|
|
1380
|
+
})
|
|
1381
|
+
}
|