matterviz 0.3.7 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Icon.svelte +7 -4
- package/dist/MillerIndexInput.svelte +1 -1
- package/dist/api/optimade.js +32 -26
- package/dist/app.css +0 -3
- package/dist/brillouin/BrillouinZone.svelte +8 -3
- package/dist/brillouin/BrillouinZone.svelte.d.ts +2 -1
- package/dist/brillouin/BrillouinZoneScene.svelte +52 -6
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -0
- package/dist/brillouin/BrillouinZoneTooltip.svelte +16 -25
- package/dist/brillouin/compute.js +10 -14
- package/dist/chempot-diagram/ChemPotDiagram.svelte +14 -13
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +12 -15
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +8 -10
- package/dist/chempot-diagram/async-compute.svelte.js +3 -1
- package/dist/chempot-diagram/chempot-worker.js +2 -1
- package/dist/chempot-diagram/compute.d.ts +1 -1
- package/dist/chempot-diagram/compute.js +17 -19
- package/dist/colors/index.js +6 -5
- package/dist/composition/FormulaFilter.svelte +12 -6
- package/dist/composition/PieChart.svelte +6 -5
- package/dist/composition/chem-sys.d.ts +8 -0
- package/dist/composition/chem-sys.js +85 -0
- package/dist/composition/format.js +4 -2
- package/dist/composition/index.d.ts +1 -0
- package/dist/composition/index.js +1 -0
- package/dist/composition/parse.js +25 -13
- package/dist/convex-hull/ConvexHull2D.svelte +12 -10
- package/dist/convex-hull/ConvexHull3D.svelte +5 -5
- package/dist/convex-hull/ConvexHull4D.svelte +5 -9
- package/dist/convex-hull/ConvexHullStats.svelte +12 -12
- package/dist/convex-hull/GasPressureControls.svelte +4 -4
- package/dist/convex-hull/TemperatureSlider.svelte +2 -2
- package/dist/convex-hull/demo-temperature.d.ts +1 -1
- package/dist/convex-hull/demo-temperature.js +20 -22
- package/dist/convex-hull/gas-thermodynamics.d.ts +2 -2
- package/dist/convex-hull/gas-thermodynamics.js +22 -30
- package/dist/convex-hull/helpers.d.ts +3 -0
- package/dist/convex-hull/helpers.js +17 -9
- package/dist/convex-hull/index.d.ts +1 -1
- package/dist/convex-hull/thermodynamics.js +83 -78
- package/dist/convex-hull/types.d.ts +1 -1
- package/dist/coordination/CoordinationBarPlot.svelte +23 -23
- package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
- package/dist/element/ElementTile.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSlice.svelte +13 -5
- package/dist/fermi-surface/FermiSurface.svelte +11 -5
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +3 -0
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +8 -34
- package/dist/fermi-surface/compute.js +59 -59
- package/dist/fermi-surface/export.js +3 -2
- package/dist/fermi-surface/parse.js +7 -4
- package/dist/fermi-surface/types.d.ts +1 -0
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +23 -21
- package/dist/heatmap-matrix/index.js +1 -1
- package/dist/io/decompress.js +4 -2
- package/dist/io/export.d.ts +4 -4
- package/dist/io/export.js +47 -25
- package/dist/io/fetch.js +5 -1
- package/dist/io/file-drop.d.ts +1 -1
- package/dist/io/file-drop.js +35 -36
- package/dist/io/url-drop.js +64 -33
- package/dist/isosurface/parse.js +6 -7
- package/dist/isosurface/slice.js +5 -4
- package/dist/isosurface/types.js +1 -1
- package/dist/keyboard.d.ts +3 -0
- package/dist/keyboard.js +23 -0
- package/dist/labels.d.ts +1 -1
- package/dist/labels.js +8 -7
- package/dist/layout/PropertyFilter.svelte +3 -2
- package/dist/layout/SettingsSection.svelte +1 -1
- package/dist/layout/json-tree/JsonNode.svelte +1 -1
- package/dist/layout/json-tree/JsonTree.svelte +2 -2
- package/dist/layout/json-tree/utils.js +5 -4
- package/dist/marching-cubes.js +8 -13
- package/dist/math.d.ts +5 -1
- package/dist/math.js +24 -9
- package/dist/overlays/DraggablePane.svelte +4 -4
- package/dist/periodic-table/PeriodicTable.svelte +20 -9
- package/dist/periodic-table/PropertySelect.svelte +1 -0
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +9 -3
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +1 -1
- package/dist/phase-diagram/build-diagram.js +2 -2
- package/dist/phase-diagram/parse.js +6 -5
- package/dist/phase-diagram/types.d.ts +1 -1
- package/dist/phase-diagram/utils.d.ts +3 -3
- package/dist/phase-diagram/utils.js +8 -12
- package/dist/plot/{BarPlot.svelte → bar/BarPlot.svelte} +229 -587
- package/dist/plot/{BarPlot.svelte.d.ts → bar/BarPlot.svelte.d.ts} +5 -5
- package/dist/plot/{BarPlotControls.svelte → bar/BarPlotControls.svelte} +6 -5
- package/dist/plot/{BarPlotControls.svelte.d.ts → bar/BarPlotControls.svelte.d.ts} +3 -3
- package/dist/plot/{SpacegroupBarPlot.svelte → bar/SpacegroupBarPlot.svelte} +6 -6
- package/dist/plot/{SpacegroupBarPlot.svelte.d.ts → bar/SpacegroupBarPlot.svelte.d.ts} +1 -1
- package/dist/plot/bar/data.d.ts +40 -0
- package/dist/plot/bar/data.js +154 -0
- package/dist/plot/bar/geometry.d.ts +39 -0
- package/dist/plot/bar/geometry.js +60 -0
- package/dist/plot/bar/index.d.ts +3 -0
- package/dist/plot/bar/index.js +3 -0
- package/dist/plot/box/BoxPlot.svelte +1462 -0
- package/dist/plot/box/BoxPlot.svelte.d.ts +94 -0
- package/dist/plot/box/BoxPlotControls.svelte +109 -0
- package/dist/plot/box/BoxPlotControls.svelte.d.ts +19 -0
- package/dist/plot/box/Violin.svelte +14 -0
- package/dist/plot/box/Violin.svelte.d.ts +70 -0
- package/dist/plot/box/box-plot.d.ts +55 -0
- package/dist/plot/box/box-plot.js +126 -0
- package/dist/plot/box/index.d.ts +5 -0
- package/dist/plot/box/index.js +5 -0
- package/dist/plot/box/kde.d.ts +16 -0
- package/dist/plot/box/kde.js +160 -0
- package/dist/plot/box/quantile.d.ts +3 -0
- package/dist/plot/box/quantile.js +53 -0
- package/dist/plot/{auto-place.js → core/auto-place.js} +2 -2
- package/dist/plot/core/axis-utils.d.ts +46 -0
- package/dist/plot/core/axis-utils.js +110 -0
- package/dist/plot/{AxisLabel.svelte → core/components/AxisLabel.svelte} +2 -2
- package/dist/plot/{AxisLabel.svelte.d.ts → core/components/AxisLabel.svelte.d.ts} +1 -1
- package/dist/plot/{ColorBar.svelte → core/components/ColorBar.svelte} +36 -33
- package/dist/plot/{ColorBar.svelte.d.ts → core/components/ColorBar.svelte.d.ts} +2 -2
- package/dist/plot/{ColorScaleSelect.svelte → core/components/ColorScaleSelect.svelte} +4 -3
- package/dist/plot/{ColorScaleSelect.svelte.d.ts → core/components/ColorScaleSelect.svelte.d.ts} +2 -2
- package/dist/plot/core/components/ControlPane.svelte +46 -0
- package/dist/plot/core/components/ControlPane.svelte.d.ts +13 -0
- package/dist/plot/{FillArea.svelte → core/components/FillArea.svelte} +17 -6
- package/dist/plot/{FillArea.svelte.d.ts → core/components/FillArea.svelte.d.ts} +1 -1
- package/dist/plot/{InteractiveAxisLabel.svelte → core/components/InteractiveAxisLabel.svelte} +3 -3
- package/dist/plot/{InteractiveAxisLabel.svelte.d.ts → core/components/InteractiveAxisLabel.svelte.d.ts} +2 -2
- package/dist/plot/{Line.svelte → core/components/Line.svelte} +30 -13
- package/dist/plot/{PlotAxis.svelte → core/components/PlotAxis.svelte} +7 -5
- package/dist/plot/{PlotAxis.svelte.d.ts → core/components/PlotAxis.svelte.d.ts} +3 -2
- package/dist/plot/{PlotControls.svelte → core/components/PlotControls.svelte} +17 -29
- package/dist/plot/core/components/PlotControls.svelte.d.ts +4 -0
- package/dist/plot/{PlotLegend.svelte → core/components/PlotLegend.svelte} +21 -10
- package/dist/plot/{PlotLegend.svelte.d.ts → core/components/PlotLegend.svelte.d.ts} +3 -2
- package/dist/plot/{PlotTooltip.svelte → core/components/PlotTooltip.svelte} +17 -1
- package/dist/plot/{PlotTooltip.svelte.d.ts → core/components/PlotTooltip.svelte.d.ts} +8 -0
- package/dist/plot/{PortalSelect.svelte → core/components/PortalSelect.svelte} +11 -7
- package/dist/plot/{ReferenceLine.svelte → core/components/ReferenceLine.svelte} +3 -3
- package/dist/plot/{ReferenceLine.svelte.d.ts → core/components/ReferenceLine.svelte.d.ts} +1 -1
- package/dist/plot/{ReferenceLine3D.svelte → core/components/ReferenceLine3D.svelte} +4 -4
- package/dist/plot/{ReferenceLine3D.svelte.d.ts → core/components/ReferenceLine3D.svelte.d.ts} +2 -2
- package/dist/plot/{ReferencePlane.svelte → core/components/ReferencePlane.svelte} +7 -7
- package/dist/plot/{ReferencePlane.svelte.d.ts → core/components/ReferencePlane.svelte.d.ts} +2 -2
- package/dist/plot/{ZeroLines.svelte → core/components/ZeroLines.svelte} +3 -3
- package/dist/plot/{ZeroLines.svelte.d.ts → core/components/ZeroLines.svelte.d.ts} +3 -3
- package/dist/plot/{ZoomRect.svelte → core/components/ZoomRect.svelte} +1 -1
- package/dist/plot/{ZoomRect.svelte.d.ts → core/components/ZoomRect.svelte.d.ts} +1 -1
- package/dist/plot/core/components/index.d.ts +17 -0
- package/dist/plot/core/components/index.js +17 -0
- package/dist/plot/{data-cleaning.d.ts → core/data-cleaning.d.ts} +71 -1
- package/dist/plot/{data-cleaning.js → core/data-cleaning.js} +3 -5
- package/dist/plot/{data-transform.d.ts → core/data-transform.d.ts} +2 -2
- package/dist/plot/{data-transform.js → core/data-transform.js} +3 -3
- package/dist/plot/core/fill-utils.d.ts +33 -0
- package/dist/plot/core/fill-utils.js +388 -0
- package/dist/plot/{hover-lock.svelte.js → core/hover-lock.svelte.js} +5 -6
- package/dist/plot/core/index.d.ts +10 -0
- package/dist/plot/core/index.js +11 -0
- package/dist/plot/core/interactions.d.ts +35 -0
- package/dist/plot/core/interactions.js +195 -0
- package/dist/plot/{layout.d.ts → core/layout.d.ts} +1 -0
- package/dist/plot/{layout.js → core/layout.js} +16 -8
- package/dist/plot/{reference-line.d.ts → core/reference-line.d.ts} +1 -1
- package/dist/plot/{reference-line.js → core/reference-line.js} +23 -36
- package/dist/plot/{scales.d.ts → core/scales.d.ts} +2 -2
- package/dist/plot/{scales.js → core/scales.js} +84 -85
- package/dist/plot/core/svg.d.ts +2 -0
- package/dist/plot/core/svg.js +41 -0
- package/dist/plot/{types.d.ts → core/types.d.ts} +19 -79
- package/dist/plot/{types.js → core/types.js} +1 -1
- package/dist/plot/{utils → core/utils}/label-placement.d.ts +2 -2
- package/dist/plot/core/utils/series-visibility.d.ts +26 -0
- package/dist/plot/{utils → core/utils}/series-visibility.js +29 -2
- package/dist/plot/core/utils.d.ts +11 -0
- package/dist/plot/core/utils.js +27 -0
- package/dist/plot/{Histogram.svelte → histogram/Histogram.svelte} +154 -294
- package/dist/plot/{Histogram.svelte.d.ts → histogram/Histogram.svelte.d.ts} +2 -2
- package/dist/plot/{HistogramControls.svelte → histogram/HistogramControls.svelte} +6 -6
- package/dist/plot/{HistogramControls.svelte.d.ts → histogram/HistogramControls.svelte.d.ts} +4 -4
- package/dist/plot/histogram/index.d.ts +2 -0
- package/dist/plot/histogram/index.js +2 -0
- package/dist/plot/index.d.ts +8 -41
- package/dist/plot/index.js +10 -39
- package/dist/plot/sankey/Sankey.svelte +700 -0
- package/dist/plot/sankey/Sankey.svelte.d.ts +74 -0
- package/dist/plot/sankey/SankeyControls.svelte +98 -0
- package/dist/plot/sankey/SankeyControls.svelte.d.ts +19 -0
- package/dist/plot/sankey/index.d.ts +4 -0
- package/dist/plot/sankey/index.js +3 -0
- package/dist/plot/sankey/sankey-types.d.ts +42 -0
- package/dist/plot/sankey/sankey-types.js +4 -0
- package/dist/plot/sankey/sankey.d.ts +52 -0
- package/dist/plot/sankey/sankey.js +187 -0
- package/dist/plot/{BinnedScatterPlot.svelte → scatter/BinnedScatterPlot.svelte} +61 -59
- package/dist/plot/{BinnedScatterPlot.svelte.d.ts → scatter/BinnedScatterPlot.svelte.d.ts} +4 -4
- package/dist/plot/{ElementScatter.svelte → scatter/ElementScatter.svelte} +6 -6
- package/dist/plot/{ElementScatter.svelte.d.ts → scatter/ElementScatter.svelte.d.ts} +2 -2
- package/dist/plot/{ScatterPlot.svelte → scatter/ScatterPlot.svelte} +221 -642
- package/dist/plot/{ScatterPlot.svelte.d.ts → scatter/ScatterPlot.svelte.d.ts} +7 -7
- package/dist/plot/{ScatterPlotControls.svelte → scatter/ScatterPlotControls.svelte} +6 -5
- package/dist/plot/{ScatterPlotControls.svelte.d.ts → scatter/ScatterPlotControls.svelte.d.ts} +1 -1
- package/dist/plot/{ScatterPoint.svelte → scatter/ScatterPoint.svelte} +7 -7
- package/dist/plot/{ScatterPoint.svelte.d.ts → scatter/ScatterPoint.svelte.d.ts} +3 -3
- package/dist/plot/{adaptive-density.d.ts → scatter/adaptive-density.d.ts} +14 -4
- package/dist/plot/{adaptive-density.js → scatter/adaptive-density.js} +46 -20
- package/dist/plot/{binned-scatter-types.d.ts → scatter/binned-scatter-types.d.ts} +3 -3
- package/dist/plot/scatter/index.d.ts +7 -0
- package/dist/plot/scatter/index.js +5 -0
- package/dist/plot/scatter/scatter-data.d.ts +19 -0
- package/dist/plot/scatter/scatter-data.js +212 -0
- package/dist/plot/{ScatterPlot3D.svelte → scatter-3d/ScatterPlot3D.svelte} +12 -10
- package/dist/plot/{ScatterPlot3D.svelte.d.ts → scatter-3d/ScatterPlot3D.svelte.d.ts} +7 -7
- package/dist/plot/{ScatterPlot3DControls.svelte → scatter-3d/ScatterPlot3DControls.svelte} +5 -4
- package/dist/plot/{ScatterPlot3DControls.svelte.d.ts → scatter-3d/ScatterPlot3DControls.svelte.d.ts} +2 -2
- package/dist/plot/{ScatterPlot3DScene.svelte → scatter-3d/ScatterPlot3DScene.svelte} +11 -11
- package/dist/plot/{ScatterPlot3DScene.svelte.d.ts → scatter-3d/ScatterPlot3DScene.svelte.d.ts} +3 -3
- package/dist/plot/{Surface3D.svelte → scatter-3d/Surface3D.svelte} +1 -1
- package/dist/plot/{Surface3D.svelte.d.ts → scatter-3d/Surface3D.svelte.d.ts} +1 -1
- package/dist/plot/scatter-3d/index.d.ts +4 -0
- package/dist/plot/scatter-3d/index.js +4 -0
- package/dist/plot/sunburst/Sunburst.svelte +1045 -0
- package/dist/plot/sunburst/Sunburst.svelte.d.ts +96 -0
- package/dist/plot/sunburst/SunburstControls.svelte +200 -0
- package/dist/plot/sunburst/SunburstControls.svelte.d.ts +26 -0
- package/dist/plot/sunburst/index.d.ts +4 -0
- package/dist/plot/sunburst/index.js +4 -0
- package/dist/plot/sunburst/render.d.ts +34 -0
- package/dist/plot/sunburst/render.js +122 -0
- package/dist/plot/sunburst/sunburst.d.ts +62 -0
- package/dist/plot/sunburst/sunburst.js +266 -0
- package/dist/rdf/RdfPlot.svelte +2 -1
- package/dist/rdf/calc-rdf.js +11 -24
- package/dist/sanitize.js +1 -1
- package/dist/settings.d.ts +65 -1
- package/dist/settings.js +262 -0
- package/dist/spectral/Bands.svelte +39 -29
- package/dist/spectral/Bands.svelte.d.ts +3 -4
- package/dist/spectral/BandsAndDos.svelte +1 -1
- package/dist/spectral/BrillouinBandsDos.svelte +39 -27
- package/dist/spectral/Dos.svelte +10 -19
- package/dist/spectral/Dos.svelte.d.ts +2 -2
- package/dist/spectral/helpers.d.ts +3 -1
- package/dist/spectral/helpers.js +95 -29
- package/dist/structure/AtomLegend.svelte +8 -9
- package/dist/structure/CellSelect.svelte +1 -2
- package/dist/structure/Cylinder.svelte +12 -8
- package/dist/structure/Cylinder.svelte.d.ts +4 -1
- package/dist/structure/Structure.svelte +78 -72
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +5 -6
- package/dist/structure/StructureScene.svelte +11 -10
- package/dist/structure/atom-properties.js +6 -6
- package/dist/structure/bond-order-perception.js +1 -1
- package/dist/structure/bonding.d.ts +1 -0
- package/dist/structure/bonding.js +43 -15
- package/dist/structure/export.js +27 -23
- package/dist/structure/index.d.ts +2 -4
- package/dist/structure/index.js +1 -3
- package/dist/structure/label-placement.js +4 -4
- package/dist/structure/measure.d.ts +3 -2
- package/dist/structure/measure.js +6 -5
- package/dist/structure/parse.js +121 -103
- package/dist/structure/pbc.js +4 -0
- package/dist/symmetry/SymmetryStats.svelte +2 -2
- package/dist/symmetry/index.d.ts +1 -1
- package/dist/symmetry/index.js +22 -24
- package/dist/symmetry/spacegroups.d.ts +7 -0
- package/dist/symmetry/spacegroups.js +48 -13
- package/dist/table/HeatmapTable.svelte +63 -11
- package/dist/table/HeatmapTable.svelte.d.ts +1 -1
- package/dist/table/index.d.ts +1 -3
- package/dist/table/index.js +1 -1
- package/dist/theme/index.js +8 -8
- package/dist/tooltip/KCoords.svelte +45 -0
- package/dist/tooltip/KCoords.svelte.d.ts +8 -0
- package/dist/tooltip/index.d.ts +1 -0
- package/dist/tooltip/index.js +1 -0
- package/dist/trajectory/Trajectory.svelte +66 -40
- package/dist/trajectory/Trajectory.svelte.d.ts +2 -1
- package/dist/trajectory/TrajectoryExportPane.svelte +2 -1
- package/dist/trajectory/TrajectoryInfoPane.svelte +2 -1
- package/dist/trajectory/format-detect.d.ts +1 -0
- package/dist/trajectory/format-detect.js +25 -11
- package/dist/trajectory/frame-reader.js +17 -50
- package/dist/trajectory/helpers.js +1 -1
- package/dist/trajectory/index.js +1 -1
- package/dist/trajectory/parse/hdf5.js +1 -1
- package/dist/trajectory/parse/index.js +14 -6
- package/dist/trajectory/parse/vasp.js +36 -17
- package/dist/trajectory/parse/xyz.d.ts +24 -0
- package/dist/trajectory/parse/xyz.js +102 -89
- package/dist/trajectory/plotting.d.ts +1 -1
- package/dist/trajectory/plotting.js +15 -15
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +6 -4
- package/dist/xrd/XrdPlot.svelte +2 -1
- package/dist/xrd/calc-xrd.js +15 -12
- package/dist/xrd/parse.js +2 -2
- package/package.json +22 -18
- package/dist/plot/PlotControls.svelte.d.ts +0 -4
- package/dist/plot/axis-utils.d.ts +0 -19
- package/dist/plot/axis-utils.js +0 -78
- package/dist/plot/defaults.d.ts +0 -19
- package/dist/plot/defaults.js +0 -9
- package/dist/plot/fill-utils.d.ts +0 -46
- package/dist/plot/fill-utils.js +0 -322
- package/dist/plot/interactions.d.ts +0 -12
- package/dist/plot/interactions.js +0 -101
- package/dist/plot/svg.d.ts +0 -1
- package/dist/plot/svg.js +0 -11
- package/dist/plot/utils/series-visibility.d.ts +0 -15
- package/dist/plot/utils.d.ts +0 -1
- package/dist/plot/utils.js +0 -14
- /package/dist/plot/{auto-place.d.ts → core/auto-place.d.ts} +0 -0
- /package/dist/plot/{Line.svelte.d.ts → core/components/Line.svelte.d.ts} +0 -0
- /package/dist/plot/{PortalSelect.svelte.d.ts → core/components/PortalSelect.svelte.d.ts} +0 -0
- /package/dist/plot/{hover-lock.svelte.d.ts → core/hover-lock.svelte.d.ts} +0 -0
- /package/dist/plot/{utils → core/utils}/label-placement.js +0 -0
- /package/dist/plot/{binned-scatter-types.js → scatter/binned-scatter-types.js} +0 -0
package/dist/structure/parse.js
CHANGED
|
@@ -41,9 +41,9 @@ function parse_coordinate_line(line) {
|
|
|
41
41
|
const sanitized = line
|
|
42
42
|
.trim()
|
|
43
43
|
// Add space when '-' follows a digit and precedes a digit or dot
|
|
44
|
-
.
|
|
44
|
+
.replaceAll(/(\d)-(?=[\d.])/g, `$1 -`)
|
|
45
45
|
// Revert accidental spaces after exponent markers
|
|
46
|
-
.
|
|
46
|
+
.replaceAll(/([eE])\s-\s/g, `$1-`);
|
|
47
47
|
tokens = sanitized.split(/\s+/);
|
|
48
48
|
}
|
|
49
49
|
if (tokens.length < 3)
|
|
@@ -61,6 +61,30 @@ function validate_element_symbol(symbol, index) {
|
|
|
61
61
|
console.warn(`Invalid element symbol '${symbol}', using fallback '${fallback}'`);
|
|
62
62
|
return fallback;
|
|
63
63
|
}
|
|
64
|
+
// Per OPTIMADE spec, species_at_sites holds species NAMES (e.g. 'Si1') resolved via the
|
|
65
|
+
// species list: highest-concentration entry in chemical_symbols wins, non-element entries
|
|
66
|
+
// like 'vacancy' are skipped, and unresolved names are treated as element symbols.
|
|
67
|
+
// Returns the chosen element plus its index into the species' chemical_symbols
|
|
68
|
+
// (sym_idx = -1 on fallback), so callers can read the matching mass/concentration entry.
|
|
69
|
+
function resolve_optimade_element(species_name, species_list, index) {
|
|
70
|
+
const spec = species_list?.find((entry) => entry.name === species_name);
|
|
71
|
+
let best;
|
|
72
|
+
for (const [sym_idx, symbol] of (spec?.chemical_symbols ?? []).entries()) {
|
|
73
|
+
if (!is_known_element_symbol(symbol))
|
|
74
|
+
continue;
|
|
75
|
+
const conc = spec?.concentration?.[sym_idx] ?? 0;
|
|
76
|
+
if (!best || conc > best.conc)
|
|
77
|
+
best = { symbol, conc, sym_idx };
|
|
78
|
+
}
|
|
79
|
+
if (best)
|
|
80
|
+
return { symbol: best.symbol, sym_idx: best.sym_idx };
|
|
81
|
+
// Fallback: the name may be an element with a trailing atom index (e.g. 'O1');
|
|
82
|
+
// element symbols never contain digits, so stripping them is safe
|
|
83
|
+
const stripped = species_name.replace(/\d+$/, ``);
|
|
84
|
+
if (is_known_element_symbol(stripped))
|
|
85
|
+
return { symbol: stripped, sym_idx: -1 };
|
|
86
|
+
return { symbol: validate_element_symbol(species_name, index), sym_idx: -1 };
|
|
87
|
+
}
|
|
64
88
|
const try_create_cart_to_frac = (lattice_matrix) => {
|
|
65
89
|
try {
|
|
66
90
|
return math.create_cart_to_frac(lattice_matrix);
|
|
@@ -77,17 +101,21 @@ const approximate_cart_to_frac = (xyz, axis_lengths) => [
|
|
|
77
101
|
// Parse VASP POSCAR file format
|
|
78
102
|
export function parse_poscar(content) {
|
|
79
103
|
try {
|
|
80
|
-
|
|
104
|
+
// Strip only horizontal whitespace: a blank first (comment) line is valid POSCAR
|
|
105
|
+
const lines = content.replace(/^[ \t]+/, ``).split(/\r?\n/);
|
|
81
106
|
if (lines.length < 8) {
|
|
82
107
|
console.error(`POSCAR file too short`);
|
|
83
108
|
return null;
|
|
84
109
|
}
|
|
85
|
-
//
|
|
86
|
-
|
|
110
|
+
// Scale line: one value (negative = target volume) or three per-axis Cartesian factors
|
|
111
|
+
const scale_tokens = lines[1].trim().split(/\s+/).map(parseFloat);
|
|
112
|
+
let scale_factor = scale_tokens[0];
|
|
87
113
|
if (isNaN(scale_factor)) {
|
|
88
114
|
console.error(`Invalid scaling factor in POSCAR`);
|
|
89
115
|
return null;
|
|
90
116
|
}
|
|
117
|
+
const scale_vec = scale_tokens.slice(0, 3);
|
|
118
|
+
const per_axis_scale = scale_vec.length === 3 && !scale_vec.some(isNaN) ? scale_vec : null;
|
|
91
119
|
// Parse lattice vectors (lines 3-5)
|
|
92
120
|
const parse_vector = (line, line_num) => {
|
|
93
121
|
const coords = line.trim().split(/\s+/).map(parse_coordinate);
|
|
@@ -98,17 +126,15 @@ export function parse_poscar(content) {
|
|
|
98
126
|
parse_vector(lines[3], 4),
|
|
99
127
|
parse_vector(lines[4], 5),
|
|
100
128
|
];
|
|
101
|
-
// Handle negative scale factor (volume-based scaling)
|
|
102
|
-
if (scale_factor < 0) {
|
|
129
|
+
// Handle negative scale factor (volume-based scaling, single-factor form only)
|
|
130
|
+
if (!per_axis_scale && scale_factor < 0) {
|
|
103
131
|
const volume = Math.abs(math.det_3x3(lattice_vecs));
|
|
104
|
-
scale_factor =
|
|
132
|
+
scale_factor = (-scale_factor / volume) ** (1 / 3);
|
|
105
133
|
}
|
|
106
|
-
// Scale lattice vectors
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
math.scale(lattice_vecs[2], scale_factor),
|
|
111
|
-
];
|
|
134
|
+
// Scale lattice vectors (per-axis factors multiply Cartesian components)
|
|
135
|
+
const axis_scale = per_axis_scale ?? [scale_factor, scale_factor, scale_factor];
|
|
136
|
+
const apply_axis_scale = (vec) => vec.map((val, axis) => val * axis_scale[axis]);
|
|
137
|
+
const scaled_lattice = lattice_vecs.map(apply_axis_scale);
|
|
112
138
|
// Parse element symbols and atom counts (may span multiple lines)
|
|
113
139
|
let line_index = 5;
|
|
114
140
|
let element_symbols = [];
|
|
@@ -116,7 +142,7 @@ export function parse_poscar(content) {
|
|
|
116
142
|
// Detect if this is VASP 5+ format (has element symbols)
|
|
117
143
|
// Try to parse the first token as a number - if it succeeds, it's VASP 4 format
|
|
118
144
|
const first_token = lines[line_index].trim().split(/\s+/)[0];
|
|
119
|
-
const first_token_as_number = parseInt(first_token);
|
|
145
|
+
const first_token_as_number = parseInt(first_token, 10);
|
|
120
146
|
const has_element_symbols = isNaN(first_token_as_number);
|
|
121
147
|
if (has_element_symbols) {
|
|
122
148
|
// VASP 5+ format - parse element symbols (may span multiple lines)
|
|
@@ -126,7 +152,7 @@ export function parse_poscar(content) {
|
|
|
126
152
|
if (line_index + lookahead_idx >= lines.length)
|
|
127
153
|
break;
|
|
128
154
|
const next_line_first_token = lines[line_index + lookahead_idx].trim().split(/\s+/)[0];
|
|
129
|
-
const next_token_as_number = parseInt(next_line_first_token);
|
|
155
|
+
const next_token_as_number = parseInt(next_line_first_token, 10);
|
|
130
156
|
if (!isNaN(next_token_as_number)) {
|
|
131
157
|
symbol_lines = lookahead_idx;
|
|
132
158
|
break;
|
|
@@ -213,28 +239,22 @@ export function parse_poscar(content) {
|
|
|
213
239
|
selective_dynamics = [tokens[3] === `T`, tokens[4] === `T`, tokens[5] === `T`];
|
|
214
240
|
}
|
|
215
241
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const raw_abc = poscar_cart_to_frac
|
|
227
|
-
? poscar_cart_to_frac(xyz)
|
|
228
|
-
: approximate_cart_to_frac(xyz, poscar_axis_lengths);
|
|
229
|
-
// Wrap fractional coordinates to [0, 1) range
|
|
230
|
-
abc = wrap_to_unit_cell(raw_abc);
|
|
231
|
-
}
|
|
242
|
+
// Cartesian input is scaled then converted to fractional (axis-length fallback
|
|
243
|
+
// for singular lattices); abc wraps to [0, 1) and xyz is recomputed from it so
|
|
244
|
+
// both stay consistent (singular Cartesian keeps the scaled input as xyz)
|
|
245
|
+
const cart = is_direct ? null : apply_axis_scale(coords);
|
|
246
|
+
const raw_abc = cart
|
|
247
|
+
? (poscar_cart_to_frac?.(cart) ??
|
|
248
|
+
approximate_cart_to_frac(cart, poscar_axis_lengths))
|
|
249
|
+
: coords;
|
|
250
|
+
const abc = wrap_to_unit_cell(raw_abc);
|
|
251
|
+
const xyz = cart && !poscar_cart_to_frac ? cart : poscar_frac_to_cart(abc);
|
|
232
252
|
const site = {
|
|
233
253
|
species: [{ element, occu: 1, oxidation_state: 0 }],
|
|
234
254
|
abc,
|
|
235
255
|
xyz,
|
|
236
256
|
label: `${element}${atom_index + atom_count_idx + 1}`,
|
|
237
|
-
properties: selective_dynamics ? { selective_dynamics
|
|
257
|
+
properties: selective_dynamics ? { selective_dynamics } : {},
|
|
238
258
|
};
|
|
239
259
|
sites.push(site);
|
|
240
260
|
}
|
|
@@ -247,10 +267,8 @@ export function parse_poscar(content) {
|
|
|
247
267
|
};
|
|
248
268
|
return structure;
|
|
249
269
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return null;
|
|
253
|
-
}
|
|
270
|
+
console.error(`Missing coordinate mode line in POSCAR`);
|
|
271
|
+
return null;
|
|
254
272
|
}
|
|
255
273
|
catch (error) {
|
|
256
274
|
console.error(`Error parsing POSCAR file:`, error);
|
|
@@ -292,7 +310,7 @@ export function parse_xyz(content) {
|
|
|
292
310
|
return null;
|
|
293
311
|
}
|
|
294
312
|
// Parse number of atoms (line 1)
|
|
295
|
-
const num_atoms = parseInt(lines[0].trim());
|
|
313
|
+
const num_atoms = parseInt(lines[0].trim(), 10);
|
|
296
314
|
if (isNaN(num_atoms) || num_atoms <= 0) {
|
|
297
315
|
console.error(`Invalid number of atoms in XYZ file`);
|
|
298
316
|
return null;
|
|
@@ -369,7 +387,7 @@ const parse_symmetry_expression = (expr_input) => {
|
|
|
369
387
|
const coefficients = [0, 0, 0];
|
|
370
388
|
let translation = 0;
|
|
371
389
|
// Remove all whitespace
|
|
372
|
-
const expr = expr_input.
|
|
390
|
+
const expr = expr_input.replaceAll(/\s+/g, ``);
|
|
373
391
|
if (!expr)
|
|
374
392
|
return { coefficients, translation };
|
|
375
393
|
// Tokenize: split into terms while preserving signs
|
|
@@ -480,13 +498,15 @@ const extract_cif_cell_parameters = (text, type, strict = true) => text
|
|
|
480
498
|
.split(`\n`)
|
|
481
499
|
.filter((line) => line.startsWith(`_${type}`))
|
|
482
500
|
.map((line) => {
|
|
483
|
-
|
|
501
|
+
// Strip trailing comment (# after whitespace) and take the value right after the tag
|
|
502
|
+
const sans_comment = line.replace(/\s#.*$/, ``);
|
|
503
|
+
const tokens = sans_comment.split(/\s+/).filter(Boolean);
|
|
484
504
|
if (tokens.length < 2) {
|
|
485
505
|
if (strict)
|
|
486
506
|
throw new Error(`Invalid CIF cell parameter line format: ${line}`);
|
|
487
507
|
return null;
|
|
488
508
|
}
|
|
489
|
-
const value = parseFloat(
|
|
509
|
+
const value = parseFloat(tokens[1].split(`(`)[0]);
|
|
490
510
|
if (isNaN(value)) {
|
|
491
511
|
if (strict)
|
|
492
512
|
throw new Error(`Invalid CIF cell parameter in line: ${line}`);
|
|
@@ -541,11 +561,11 @@ const parse_cif_atom_data = (raw_data, indices, coords_type) => {
|
|
|
541
561
|
const occu = occupancy >= 0 && raw_data[occupancy]
|
|
542
562
|
? parseFloat(raw_data[occupancy].split(`(`)[0]) || 1.0
|
|
543
563
|
: 1.0;
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
564
|
+
const from_symbol = symbol >= 0 ? /^([A-Z][a-z]*)/.exec(raw_data[symbol])?.[1] : undefined;
|
|
565
|
+
const element_symbol = from_symbol ?? raw_data[label]?.match(/([A-Z][a-z]*)/g)?.[0];
|
|
566
|
+
if (!element_symbol) {
|
|
567
|
+
throw new Error(`Could not extract element symbol from: ${raw_data.join(` `)}`);
|
|
568
|
+
}
|
|
549
569
|
return {
|
|
550
570
|
id: raw_data[label],
|
|
551
571
|
element: element_symbol,
|
|
@@ -617,7 +637,7 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
617
637
|
if (line.startsWith(`;`)) {
|
|
618
638
|
let multi_line_data = ``;
|
|
619
639
|
while (jj < lines.length && !lines[jj].trim().endsWith(`;`)) {
|
|
620
|
-
multi_line_data += lines[jj]
|
|
640
|
+
multi_line_data += `${lines[jj]}\n`;
|
|
621
641
|
jj++;
|
|
622
642
|
}
|
|
623
643
|
multi_line_data += lines[jj];
|
|
@@ -632,7 +652,7 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
632
652
|
if (atom_data_lines.length > 0)
|
|
633
653
|
break;
|
|
634
654
|
}
|
|
635
|
-
if (
|
|
655
|
+
if (atom_headers.length === 0 || atom_data_lines.length === 0) {
|
|
636
656
|
console.error(`No valid atom site loop found in CIF file`);
|
|
637
657
|
return null;
|
|
638
658
|
}
|
|
@@ -660,8 +680,8 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
660
680
|
.map((line) => {
|
|
661
681
|
// Handle quoted multi-word values by splitting only on whitespace
|
|
662
682
|
// that is not inside quotes.
|
|
663
|
-
const tokens = line.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)
|
|
664
|
-
return tokens.map((token) => token.
|
|
683
|
+
const tokens = line.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? [];
|
|
684
|
+
return tokens.map((token) => token.replaceAll(/['"]/g, ``));
|
|
665
685
|
})
|
|
666
686
|
.filter((tokens) => {
|
|
667
687
|
const { disorder } = header_indices;
|
|
@@ -679,7 +699,7 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
679
699
|
}
|
|
680
700
|
})
|
|
681
701
|
.filter((atom) => atom !== null);
|
|
682
|
-
if (
|
|
702
|
+
if (atoms.length === 0) {
|
|
683
703
|
console.error(`No valid atoms found in CIF file`);
|
|
684
704
|
return null;
|
|
685
705
|
}
|
|
@@ -703,8 +723,8 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
703
723
|
const all_sites = [];
|
|
704
724
|
// Normalize symmetry operations (trim/strip quotes) but preserve duplicates; we deduplicate positions later
|
|
705
725
|
const normalized_ops = symmetry_ops
|
|
706
|
-
.map((op) => /['"]([^'"]+)['"]/.exec(op)?.[1]
|
|
707
|
-
.map((op) => op.
|
|
726
|
+
.map((op) => /['"]([^'"]+)['"]/.exec(op)?.[1] ?? op.trim())
|
|
727
|
+
.map((op) => op.replaceAll(/\s+/g, ``));
|
|
708
728
|
// Rely on symmetry operations list for all centering/translations to avoid double-counting
|
|
709
729
|
// TODO: Support conventional cells with centering by discovering centering from space group metadata
|
|
710
730
|
// when present (e.g. P, I, F, C, R centering types)
|
|
@@ -734,12 +754,12 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
734
754
|
lj++;
|
|
735
755
|
continue;
|
|
736
756
|
}
|
|
737
|
-
const toks = (line.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)
|
|
757
|
+
const toks = (line.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? []).map((tok) => tok.replaceAll(/['"]/g, ``));
|
|
738
758
|
if (toks.length > Math.max(sym_idx, num_idx)) {
|
|
739
759
|
// Normalize type symbol to bare element (e.g. 'Sn2+' -> 'Sn')
|
|
740
760
|
const match = /^([A-Z][a-z]*)/.exec(toks[sym_idx]);
|
|
741
761
|
const sym = match ? match[1] : toks[sym_idx];
|
|
742
|
-
const num = parseInt(toks[num_idx]);
|
|
762
|
+
const num = parseInt(toks[num_idx], 10);
|
|
743
763
|
if (sym && !Number.isNaN(num))
|
|
744
764
|
map[sym] = num;
|
|
745
765
|
}
|
|
@@ -887,10 +907,8 @@ export function parse_phonopy_yaml(content, cell_type) {
|
|
|
887
907
|
const cell = get_phonopy_cell(data, cell_type);
|
|
888
908
|
if (cell)
|
|
889
909
|
return convert_phonopy_cell(cell);
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
return null;
|
|
893
|
-
}
|
|
910
|
+
console.error(`Requested cell type '${cell_type}' not found in phonopy YAML`);
|
|
911
|
+
return null;
|
|
894
912
|
}
|
|
895
913
|
// Auto mode: return preferred structure in order of preference
|
|
896
914
|
// 1. supercell (most detailed)
|
|
@@ -982,6 +1000,19 @@ export function normalize_fractional_coords(structure) {
|
|
|
982
1000
|
});
|
|
983
1001
|
return { ...structure, sites: normalized_sites };
|
|
984
1002
|
}
|
|
1003
|
+
// Detect a structure inside already-stringified JSON (OPTIMADE or pymatgen/nested).
|
|
1004
|
+
// Throws if `content` isn't valid JSON; returns null if it holds no known structure.
|
|
1005
|
+
const detect_json_structure = (content) => {
|
|
1006
|
+
const parsed = JSON.parse(content);
|
|
1007
|
+
if (is_optimade_raw(parsed)) {
|
|
1008
|
+
const result = parse_optimade_from_raw(parsed);
|
|
1009
|
+
if (result)
|
|
1010
|
+
return result;
|
|
1011
|
+
}
|
|
1012
|
+
// Otherwise try parsing as pymatgen/nested structure JSON
|
|
1013
|
+
const structure = find_structure_in_json(parsed);
|
|
1014
|
+
return structure ? normalize_fractional_coords(structure) : null;
|
|
1015
|
+
};
|
|
985
1016
|
// Auto-detect file format and parse accordingly
|
|
986
1017
|
export function parse_structure_file(content, filename) {
|
|
987
1018
|
// If a filename is provided, try to detect format by file extension first
|
|
@@ -998,27 +1029,18 @@ export function parse_structure_file(content, filename) {
|
|
|
998
1029
|
// CIF files
|
|
999
1030
|
if (ext === `cif`)
|
|
1000
1031
|
return parse_cif(content);
|
|
1001
|
-
// JSON files -
|
|
1032
|
+
// JSON files - extension is authoritative, so failures return null
|
|
1002
1033
|
if (ext === `json`) {
|
|
1003
1034
|
try {
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
const result = parse_optimade_from_raw(parsed);
|
|
1008
|
-
if (result)
|
|
1009
|
-
return result;
|
|
1010
|
-
}
|
|
1011
|
-
// Otherwise, try to parse as pymatgen/nested structure JSON
|
|
1012
|
-
const structure = find_structure_in_json(parsed);
|
|
1013
|
-
if (structure)
|
|
1014
|
-
return normalize_fractional_coords(structure);
|
|
1035
|
+
const result = detect_json_structure(content);
|
|
1036
|
+
if (result)
|
|
1037
|
+
return result;
|
|
1015
1038
|
console.error(`JSON file does not contain a valid structure format`);
|
|
1016
|
-
return null;
|
|
1017
1039
|
}
|
|
1018
1040
|
catch (error) {
|
|
1019
1041
|
console.error(`Error parsing JSON file:`, error);
|
|
1020
|
-
return null;
|
|
1021
1042
|
}
|
|
1043
|
+
return null;
|
|
1022
1044
|
}
|
|
1023
1045
|
// YAML files (phonopy)
|
|
1024
1046
|
if (ext === `yaml` || ext === `yml`)
|
|
@@ -1028,31 +1050,24 @@ export function parse_structure_file(content, filename) {
|
|
|
1028
1050
|
return parse_poscar(content);
|
|
1029
1051
|
}
|
|
1030
1052
|
}
|
|
1031
|
-
// Try to auto-detect based on content
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
console.error(`File too short to determine format`);
|
|
1035
|
-
return null;
|
|
1036
|
-
}
|
|
1037
|
-
// JSON format detection: try to parse as JSON first
|
|
1053
|
+
// Try to auto-detect based on content.
|
|
1054
|
+
// JSON detection must come before the line-count guard: minified JSON
|
|
1055
|
+
// (e.g. fetched via extensionless blob: object URLs) is a single line.
|
|
1038
1056
|
try {
|
|
1039
|
-
const
|
|
1040
|
-
if (
|
|
1041
|
-
|
|
1042
|
-
if (result)
|
|
1043
|
-
return result;
|
|
1044
|
-
}
|
|
1045
|
-
// Otherwise try parsing as regular JSON structure
|
|
1046
|
-
const structure = find_structure_in_json(parsed);
|
|
1047
|
-
if (structure) {
|
|
1048
|
-
return normalize_fractional_coords(structure);
|
|
1049
|
-
}
|
|
1057
|
+
const result = detect_json_structure(content);
|
|
1058
|
+
if (result)
|
|
1059
|
+
return result;
|
|
1050
1060
|
}
|
|
1051
1061
|
catch {
|
|
1052
1062
|
// Not JSON, continue with other format detection
|
|
1053
1063
|
}
|
|
1064
|
+
const lines = content.trim().split(/\r?\n/);
|
|
1065
|
+
if (lines.length < 2) {
|
|
1066
|
+
console.error(`File too short to determine format`);
|
|
1067
|
+
return null;
|
|
1068
|
+
}
|
|
1054
1069
|
// XYZ format detection: first line should be a number, second line is comment
|
|
1055
|
-
const first_line_number = parseInt(lines[0].trim());
|
|
1070
|
+
const first_line_number = parseInt(lines[0].trim(), 10);
|
|
1056
1071
|
if (!isNaN(first_line_number) && first_line_number > 0) {
|
|
1057
1072
|
// Check if this looks like XYZ format
|
|
1058
1073
|
if (lines.length >= first_line_number + 2) {
|
|
@@ -1066,7 +1081,7 @@ export function parse_structure_file(content, filename) {
|
|
|
1066
1081
|
const coords = parts.slice(1, 4);
|
|
1067
1082
|
// Check if first token looks like an element symbol (not a number)
|
|
1068
1083
|
// and the next 3 tokens look like coordinates (numbers)
|
|
1069
|
-
const is_element_symbol = isNaN(parseInt(first_token)) && first_token.length <= 3;
|
|
1084
|
+
const is_element_symbol = isNaN(parseInt(first_token, 10)) && first_token.length <= 3;
|
|
1070
1085
|
const are_coordinates = coords.every((coord) => !isNaN(parseFloat(coord)));
|
|
1071
1086
|
if (is_element_symbol && are_coordinates) {
|
|
1072
1087
|
// First token is likely an element symbol, likely XYZ
|
|
@@ -1190,6 +1205,7 @@ export function parse_optimade_from_raw(raw) {
|
|
|
1190
1205
|
if (lattice_matrix && !optimade_exact_cart_to_frac) {
|
|
1191
1206
|
console.warn(`Failed to create exact coordinate converter for OPTIMADE structure`);
|
|
1192
1207
|
}
|
|
1208
|
+
const optimade_species = Array.isArray(attrs.species) ? attrs.species : undefined;
|
|
1193
1209
|
const sites = [];
|
|
1194
1210
|
for (let idx = 0; idx < positions.length; idx++) {
|
|
1195
1211
|
const pos = positions[idx];
|
|
@@ -1202,7 +1218,7 @@ export function parse_optimade_from_raw(raw) {
|
|
|
1202
1218
|
console.warn(`Invalid position data at site ${idx}: ${error}`);
|
|
1203
1219
|
continue;
|
|
1204
1220
|
}
|
|
1205
|
-
const element =
|
|
1221
|
+
const { symbol: element } = resolve_optimade_element(element_symbol, optimade_species, idx);
|
|
1206
1222
|
// Calculate fractional coordinates if lattice is available
|
|
1207
1223
|
const abc = optimade_cart_to_frac ? optimade_cart_to_frac(xyz) : [0, 0, 0];
|
|
1208
1224
|
const site = {
|
|
@@ -1282,24 +1298,26 @@ export function optimade_to_crystal(optimade_structure) {
|
|
|
1282
1298
|
vec3_from_values(lattice_vectors[2], `OPTIMADE lattice vector 3`),
|
|
1283
1299
|
];
|
|
1284
1300
|
const lattice_params = math.calc_lattice_params(lattice_matrix);
|
|
1285
|
-
// Build species lookup for site properties (mass, concentration, etc.)
|
|
1286
|
-
const species_map = new Map(species?.map((spec) => [spec.name, spec]));
|
|
1287
1301
|
const crystal_cart_to_frac = try_create_cart_to_frac(lattice_matrix) ??
|
|
1288
1302
|
((xyz) => approximate_cart_to_frac(xyz, [lattice_params.a, lattice_params.b, lattice_params.c]));
|
|
1289
1303
|
const sites = cartesian_site_positions.map((pos, idx) => {
|
|
1290
1304
|
const element_symbol = species_at_sites[idx];
|
|
1291
1305
|
if (!element_symbol)
|
|
1292
1306
|
throw new Error(`Missing species for site ${idx}`);
|
|
1293
|
-
const element =
|
|
1307
|
+
const { symbol: element, sym_idx } = resolve_optimade_element(element_symbol, species, idx);
|
|
1294
1308
|
const xyz = vec3_from_values(pos, `OPTIMADE atom position ${idx + 1}`);
|
|
1295
1309
|
const abc = crystal_cart_to_frac ? crystal_cart_to_frac(xyz) : [0, 0, 0];
|
|
1296
|
-
// Extract mass/concentration
|
|
1297
|
-
|
|
1310
|
+
// Extract mass/concentration for the chosen element. sym_idx indexes the (parallel)
|
|
1311
|
+
// chemical_symbols/mass/concentration arrays; -1 (name resolved directly, no
|
|
1312
|
+
// chemical_symbols) falls back to index 0 — the single-element entry.
|
|
1313
|
+
const spec = species?.find((entry) => entry.name === element_symbol);
|
|
1314
|
+
const spec_idx = Math.max(sym_idx, 0);
|
|
1298
1315
|
const site_props = {};
|
|
1299
|
-
if (spec?.mass?.[
|
|
1300
|
-
site_props.mass = spec.mass[
|
|
1301
|
-
if (spec?.concentration?.[
|
|
1302
|
-
|
|
1316
|
+
if (spec?.mass?.[spec_idx] !== undefined)
|
|
1317
|
+
site_props.mass = spec.mass[spec_idx];
|
|
1318
|
+
if (spec?.concentration?.[spec_idx] !== undefined &&
|
|
1319
|
+
spec.concentration[spec_idx] !== 1) {
|
|
1320
|
+
site_props.concentration = spec.concentration[spec_idx];
|
|
1303
1321
|
}
|
|
1304
1322
|
return {
|
|
1305
1323
|
species: [{ element, occu: 1, oxidation_state: 0 }],
|
package/dist/structure/pbc.js
CHANGED
|
@@ -43,11 +43,15 @@ export function find_image_atoms(structure, { tolerance } = {}) {
|
|
|
43
43
|
// but fall back to 0.05 fractional tolerance if it does
|
|
44
44
|
return vec_len > 0 ? PHYSICAL_TOLERANCE / vec_len : 0.05;
|
|
45
45
|
});
|
|
46
|
+
// Respect per-axis periodicity: no images across non-periodic (vacuum) directions
|
|
47
|
+
const pbc = structure.lattice.pbc ?? [true, true, true];
|
|
46
48
|
for (const [idx, site] of structure.sites.entries()) {
|
|
47
49
|
// Find edge dimensions and translation directions
|
|
48
50
|
const edge_dims = [];
|
|
49
51
|
// Find boundary dimensions
|
|
50
52
|
for (let dim = 0; dim < 3; dim++) {
|
|
53
|
+
if (!pbc[dim])
|
|
54
|
+
continue;
|
|
51
55
|
const coord = site.abc[dim];
|
|
52
56
|
const dim_tolerance = tolerances[dim];
|
|
53
57
|
if (Math.abs(coord) < dim_tolerance)
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
const wyckoff_count = $derived(
|
|
33
33
|
sym_data ? wyckoff_positions_from_moyo(sym_data).length : 0,
|
|
34
34
|
)
|
|
35
|
-
const display_hm_symbol = $derived(sym_data?.hm_symbol?.
|
|
35
|
+
const display_hm_symbol = $derived(sym_data?.hm_symbol?.replaceAll(/\s+/g, ``) ?? `?`)
|
|
36
36
|
|
|
37
37
|
const sym_ops_counts = $derived.by(() => {
|
|
38
38
|
const EPS = 1e-10
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
function get_step_from_order_of_magnitude(value: number): number {
|
|
84
84
|
if (!Number.isFinite(value) || value <= 0) return 1e-5
|
|
85
85
|
const exponent = Math.floor(Math.log10(value))
|
|
86
|
-
return
|
|
86
|
+
return 10 ** exponent
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
const symprec_step = $derived(get_step_from_order_of_magnitude(settings.symprec))
|
package/dist/symmetry/index.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export type SymmetryDataset = MoyoDataset & {
|
|
|
35
35
|
};
|
|
36
36
|
export declare function ensure_moyo_wasm_ready(wasm_url?: string): Promise<void>;
|
|
37
37
|
export declare function to_cell_json(structure: Crystal): string;
|
|
38
|
-
export declare
|
|
38
|
+
export declare const map_std_to_orig_site_indices: (std_positions: Vec3[], std_numbers: number[], input_positions: Vec3[], input_numbers: number[], orig_site_indices_by_input_idx: number[][]) => number[][];
|
|
39
39
|
export declare function analyze_structure_symmetry(struct_or_mol: AnyStructure, settings: Partial<SymmetrySettings>): Promise<SymmetryDataset>;
|
|
40
40
|
export declare function simplicity_score(vec: number[]): number;
|
|
41
41
|
export declare function wyckoff_positions_from_moyo(sym_data: SymmetryDataset | null): WyckoffPos[];
|
package/dist/symmetry/index.js
CHANGED
|
@@ -93,25 +93,23 @@ export function to_cell_json(structure) {
|
|
|
93
93
|
const fractional_sq_dist = (pos_1, pos_2) => (pos_1[0] - pos_2[0] - Math.round(pos_1[0] - pos_2[0])) ** 2 +
|
|
94
94
|
(pos_1[1] - pos_2[1] - Math.round(pos_1[1] - pos_2[1])) ** 2 +
|
|
95
95
|
(pos_1[2] - pos_2[2] - Math.round(pos_1[2] - pos_2[2])) ** 2;
|
|
96
|
-
export
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
nearest_input_idx = input_idx;
|
|
108
|
-
}
|
|
96
|
+
export const map_std_to_orig_site_indices = (std_positions, std_numbers, input_positions, input_numbers, orig_site_indices_by_input_idx) => std_positions.map((std_pos, std_idx) => {
|
|
97
|
+
const std_number = std_numbers[std_idx];
|
|
98
|
+
let nearest_input_idx = -1;
|
|
99
|
+
let nearest_sq_dist = Infinity;
|
|
100
|
+
for (let input_idx = 0; input_idx < input_positions.length; input_idx += 1) {
|
|
101
|
+
if (input_numbers[input_idx] !== std_number)
|
|
102
|
+
continue;
|
|
103
|
+
const sq_dist = fractional_sq_dist(std_pos, input_positions[input_idx]);
|
|
104
|
+
if (sq_dist < nearest_sq_dist) {
|
|
105
|
+
nearest_sq_dist = sq_dist;
|
|
106
|
+
nearest_input_idx = input_idx;
|
|
109
107
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return
|
|
113
|
-
|
|
114
|
-
}
|
|
108
|
+
}
|
|
109
|
+
if (nearest_input_idx === -1)
|
|
110
|
+
return [];
|
|
111
|
+
return orig_site_indices_by_input_idx[nearest_input_idx] ?? [];
|
|
112
|
+
});
|
|
115
113
|
export async function analyze_structure_symmetry(struct_or_mol, settings) {
|
|
116
114
|
await ensure_moyo_wasm_ready();
|
|
117
115
|
if (!(`lattice` in struct_or_mol)) {
|
|
@@ -178,7 +176,7 @@ export function wyckoff_positions_from_moyo(sym_data) {
|
|
|
178
176
|
};
|
|
179
177
|
});
|
|
180
178
|
rows.sort((w1, w2) => {
|
|
181
|
-
const [w1_mult, w2_mult] = [parseInt(w1.wyckoff), parseInt(w2.wyckoff)];
|
|
179
|
+
const [w1_mult, w2_mult] = [parseInt(w1.wyckoff, 10), parseInt(w2.wyckoff, 10)];
|
|
182
180
|
if (w1_mult !== w2_mult)
|
|
183
181
|
return w1_mult - w2_mult;
|
|
184
182
|
return w1.wyckoff.localeCompare(w2.wyckoff);
|
|
@@ -190,10 +188,10 @@ export function apply_symmetry_operations(position, operations, _tolerance = 1e-
|
|
|
190
188
|
const seen = new Set();
|
|
191
189
|
return operations
|
|
192
190
|
.map(({ rotation, translation }) => {
|
|
193
|
-
//
|
|
194
|
-
const new_pos = [0, 1, 2].map((dim) => rotation[dim
|
|
195
|
-
rotation[dim
|
|
196
|
-
rotation[dim
|
|
191
|
+
// new_pos = W·position + t; moyo serializes W COLUMN-major: W[dim][j] = rotation[dim + 3j]
|
|
192
|
+
const new_pos = [0, 1, 2].map((dim) => rotation[dim] * position[0] +
|
|
193
|
+
rotation[dim + 3] * position[1] +
|
|
194
|
+
rotation[dim + 6] * position[2] +
|
|
197
195
|
translation[dim]);
|
|
198
196
|
return new_pos.map(to_unit);
|
|
199
197
|
})
|
|
@@ -211,7 +209,7 @@ export function map_wyckoff_to_all_atoms(wyckoff_positions, displayed_structure,
|
|
|
211
209
|
return wyckoff_positions;
|
|
212
210
|
}
|
|
213
211
|
return wyckoff_positions.map((wyckoff_pos) => {
|
|
214
|
-
const indices = (wyckoff_pos.site_indices
|
|
212
|
+
const indices = (wyckoff_pos.site_indices ?? [])
|
|
215
213
|
.filter((idx) => idx < orig_structure.sites.length)
|
|
216
214
|
.flatMap((orig_idx) => {
|
|
217
215
|
const { abc: orig_abc, species } = orig_structure.sites[orig_idx];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SunburstNode } from '../plot/core/types';
|
|
1
2
|
export declare const CRYSTAL_SYSTEM_RANGES: Record<CrystalSystem, [number, number]>;
|
|
2
3
|
export declare const CRYSTAL_SYSTEM_COLORS: Record<CrystalSystem, string>;
|
|
3
4
|
export declare const CRYSTAL_SYSTEMS: readonly ["triclinic", "monoclinic", "orthorhombic", "tetragonal", "trigonal", "hexagonal", "cubic"];
|
|
@@ -7,3 +8,9 @@ export declare function spacegroup_to_crystal_sys(spacegroup: number | string):
|
|
|
7
8
|
export declare function normalize_spacegroup(spacegroup: number | string): number | null;
|
|
8
9
|
export declare const SPACEGROUP_SYMBOL_TO_NUM: Record<string, number>;
|
|
9
10
|
export declare const SPACEGROUP_NUM_TO_SYMBOL: Record<number, string>;
|
|
11
|
+
export interface SpacegroupSunburstMetadata {
|
|
12
|
+
spacegroup: number;
|
|
13
|
+
crystal_system: CrystalSystem;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
export declare function spacegroup_sunburst_data(spacegroups: readonly (number | string)[]): SunburstNode<SpacegroupSunburstMetadata>[];
|