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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare function strip_compression_extensions(filename: string): string;
|
|
2
|
+
export declare function ext_hint(filename: string | undefined, ext_regex: RegExp): boolean | null;
|
|
2
3
|
export declare const FORMAT_PATTERNS: {
|
|
3
4
|
readonly ase: (data: unknown, filename?: string) => boolean;
|
|
4
5
|
readonly hdf5: (data: unknown, filename?: string) => boolean;
|
|
@@ -8,26 +8,42 @@ export function strip_compression_extensions(filename) {
|
|
|
8
8
|
}
|
|
9
9
|
return base_name;
|
|
10
10
|
}
|
|
11
|
-
//
|
|
11
|
+
// Extensions that explicitly identify a format — when present, format detection trusts
|
|
12
|
+
// the extension instead of sniffing content
|
|
13
|
+
const KNOWN_FORMAT_EXT_REGEX = /\.(xyz|extxyz|traj|h5|hdf5|lammpstrj|json|cif|poscar|vasp|yaml|yml|xml|csv)$/;
|
|
14
|
+
// Classify the filename hint for a format whose extensions match ext_regex:
|
|
15
|
+
// true = filename matches, false = filename names a different known format,
|
|
16
|
+
// null = no usable hint (missing filename or unrecognized extension, e.g. the UUID
|
|
17
|
+
// basenames of blob: object URLs) — callers should fall back to content detection
|
|
18
|
+
export function ext_hint(filename, ext_regex) {
|
|
19
|
+
if (!filename)
|
|
20
|
+
return null;
|
|
21
|
+
const base = strip_compression_extensions(filename);
|
|
22
|
+
if (ext_regex.test(base))
|
|
23
|
+
return true;
|
|
24
|
+
return KNOWN_FORMAT_EXT_REGEX.test(base) ? false : null;
|
|
25
|
+
}
|
|
26
|
+
// Unified format detection. Each pattern trusts a matching file extension when present
|
|
27
|
+
// but falls back to content/magic-byte detection when the filename gives no hint
|
|
28
|
+
// (e.g. blob: object URLs, extensionless API endpoints).
|
|
12
29
|
export const FORMAT_PATTERNS = {
|
|
13
30
|
ase: (data, filename) => {
|
|
14
|
-
|
|
15
|
-
if (!base_name?.endsWith(`.traj`) || !(data instanceof ArrayBuffer)) {
|
|
31
|
+
if (ext_hint(filename, /\.traj$/) === false || !(data instanceof ArrayBuffer)) {
|
|
16
32
|
return false;
|
|
17
33
|
}
|
|
18
34
|
const view = new Uint8Array(data.slice(0, 24));
|
|
19
35
|
return [0x2d, 0x20, 0x6f, 0x66, 0x20, 0x55, 0x6c, 0x6d].every((byte, idx) => view[idx] === byte);
|
|
20
36
|
},
|
|
21
37
|
hdf5: (data, filename) => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (!
|
|
38
|
+
if (ext_hint(filename, /\.(h5|hdf5)$/) === false)
|
|
39
|
+
return false;
|
|
40
|
+
if (!(data instanceof ArrayBuffer) || data.byteLength < 8)
|
|
25
41
|
return false;
|
|
26
42
|
const signature = new Uint8Array(data.slice(0, 8));
|
|
27
43
|
return [0x89, 0x48, 0x44, 0x46, 0x0d, 0x0a, 0x1a, 0x0a].every((b, idx) => signature[idx] === b);
|
|
28
44
|
},
|
|
29
45
|
vasp: (data, filename) => {
|
|
30
|
-
const basename = filename?.toLowerCase().split(`/`).pop()
|
|
46
|
+
const basename = filename?.toLowerCase().split(`/`).pop() ?? ``;
|
|
31
47
|
if (basename === `xdatcar` || basename.startsWith(`xdatcar`))
|
|
32
48
|
return true;
|
|
33
49
|
const lines = data.trim().split(/\r?\n/);
|
|
@@ -37,14 +53,12 @@ export const FORMAT_PATTERNS = {
|
|
|
37
53
|
lines.slice(2, 5).every((line) => line.trim().split(/\s+/).length === 3));
|
|
38
54
|
},
|
|
39
55
|
xyz_multi: (data, filename) => {
|
|
40
|
-
|
|
41
|
-
if (!/\.(xyz|extxyz)$/.test(base))
|
|
56
|
+
if (ext_hint(filename, /\.(xyz|extxyz)$/) === false)
|
|
42
57
|
return false;
|
|
43
58
|
return count_xyz_frames(data) >= 2;
|
|
44
59
|
},
|
|
45
60
|
lammpstrj: (data, filename) => {
|
|
46
|
-
|
|
47
|
-
if (!base.endsWith(`.lammpstrj`))
|
|
61
|
+
if (ext_hint(filename, /\.lammpstrj$/) === false)
|
|
48
62
|
return false;
|
|
49
63
|
return data.includes(`ITEM: TIMESTEP`) && data.includes(`ITEM: ATOMS`);
|
|
50
64
|
},
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
// Unified frame loader for XYZ and ASE trajectories (large file indexing)
|
|
1
2
|
import * as math from '../math';
|
|
2
3
|
import { MAX_METADATA_SIZE } from './constants';
|
|
3
|
-
import {
|
|
4
|
+
import { convert_atomic_numbers, count_xyz_frames, create_trajectory_frame, read_ndarray_from_view, validate_3x3_matrix, } from './helpers';
|
|
4
5
|
import { strip_compression_extensions } from './format-detect';
|
|
6
|
+
import { parse_extxyz_lattice, parse_xyz_atom_lines, parse_xyz_comment_metadata, } from './parse/xyz';
|
|
5
7
|
export class TrajFrameReader {
|
|
6
8
|
format;
|
|
7
9
|
global_numbers;
|
|
@@ -185,16 +187,12 @@ export class TrajFrameReader {
|
|
|
185
187
|
const lines = data.trim().split(/\r?\n/);
|
|
186
188
|
let [current_frame, line_idx] = [0, 0];
|
|
187
189
|
while (line_idx < lines.length && current_frame < frame_number) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
+
const skip_atoms = parseInt(lines[line_idx].trim(), 10);
|
|
191
|
+
if (isNaN(skip_atoms) || skip_atoms <= 0) {
|
|
192
|
+
line_idx++; // skip blank/invalid lines until the next frame's atom-count line
|
|
190
193
|
continue;
|
|
191
194
|
}
|
|
192
|
-
|
|
193
|
-
if (isNaN(num_atoms) || num_atoms <= 0) {
|
|
194
|
-
line_idx++;
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
line_idx += 2 + num_atoms;
|
|
195
|
+
line_idx += 2 + skip_atoms;
|
|
198
196
|
current_frame++;
|
|
199
197
|
}
|
|
200
198
|
if (line_idx >= lines.length)
|
|
@@ -203,32 +201,14 @@ export class TrajFrameReader {
|
|
|
203
201
|
if (isNaN(num_atoms) || line_idx + num_atoms + 1 >= lines.length)
|
|
204
202
|
return null;
|
|
205
203
|
const comment = lines[line_idx + 1] || ``;
|
|
206
|
-
const
|
|
207
|
-
const elements =
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (!Number.isFinite(x_coord) ||
|
|
215
|
-
!Number.isFinite(y_coord) ||
|
|
216
|
-
!Number.isFinite(z_coord)) {
|
|
217
|
-
console.warn(`Skipping XYZ atom with invalid coordinates in indexed frame ${frame_number} at line ${line_idx + 2 + idx}`);
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
const raw_symbol = parts[0];
|
|
221
|
-
const element_symbol = coerce_element_symbol(raw_symbol);
|
|
222
|
-
if (!element_symbol) {
|
|
223
|
-
console.warn(`Skipping XYZ atom with unknown element symbol "${raw_symbol}" in indexed frame ${frame_number}`);
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
elements.push(element_symbol);
|
|
227
|
-
positions.push([x_coord, y_coord, z_coord]);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
const metadata = this.parse_xyz_metadata(comment, frame_number);
|
|
231
|
-
return create_trajectory_frame(positions, elements, undefined, undefined, metadata.step, metadata.properties);
|
|
204
|
+
const lattice_matrix = parse_extxyz_lattice(comment);
|
|
205
|
+
const { elements, positions, force_stats } = parse_xyz_atom_lines(lines, line_idx + 2, num_atoms, comment, `indexed frame ${frame_number}`);
|
|
206
|
+
const { step, properties } = this.parse_xyz_metadata(comment, frame_number);
|
|
207
|
+
const metadata = { ...properties, ...force_stats };
|
|
208
|
+
// Derive volume from the lattice (parity with the eager parse_xyz_trajectory parser)
|
|
209
|
+
if (lattice_matrix)
|
|
210
|
+
metadata.volume = math.calc_lattice_params(lattice_matrix).volume;
|
|
211
|
+
return create_trajectory_frame(positions, elements, lattice_matrix, lattice_matrix ? [true, true, true] : undefined, step, metadata);
|
|
232
212
|
}
|
|
233
213
|
load_ase_frame(data, frame_number) {
|
|
234
214
|
try {
|
|
@@ -274,21 +254,8 @@ export class TrajFrameReader {
|
|
|
274
254
|
}
|
|
275
255
|
}
|
|
276
256
|
parse_xyz_metadata(comment, frame_number) {
|
|
277
|
-
const properties =
|
|
278
|
-
|
|
279
|
-
energy: /(?:energy|E|etot)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
|
|
280
|
-
volume: /(?:volume|vol|V)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
|
|
281
|
-
pressure: /(?:pressure|press|P)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
|
|
282
|
-
force_max: /(?:max_force|fmax)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
|
|
283
|
-
};
|
|
284
|
-
Object.entries(patterns).forEach(([key, pattern]) => {
|
|
285
|
-
const match = pattern.exec(comment);
|
|
286
|
-
if (match)
|
|
287
|
-
properties[key] = parseFloat(match[1]);
|
|
288
|
-
});
|
|
289
|
-
const step_match = /(?:step|frame)\s*[=:]?\s*(\d+)/i.exec(comment);
|
|
290
|
-
const step = step_match ? parseInt(step_match[1]) : frame_number;
|
|
291
|
-
return { frame_number, step, properties };
|
|
257
|
+
const { step, properties } = parse_xyz_comment_metadata(comment);
|
|
258
|
+
return { frame_number, step: step ?? frame_number, properties };
|
|
292
259
|
}
|
|
293
260
|
parse_ase_metadata(frame_data, frame_number) {
|
|
294
261
|
const properties = {};
|
|
@@ -147,7 +147,7 @@ export function count_xyz_frames(data) {
|
|
|
147
147
|
let valid_coords = 0;
|
|
148
148
|
for (let idx = 0; idx < Math.min(num_atoms, 3); idx++) {
|
|
149
149
|
const parts = lines[line_idx + 2 + idx]?.trim().split(/\s+/);
|
|
150
|
-
if (parts?.length >= 4 && isNaN(parseInt(parts[0])) && parts[0].length <= 3) {
|
|
150
|
+
if (parts?.length >= 4 && isNaN(parseInt(parts[0], 10)) && parts[0].length <= 3) {
|
|
151
151
|
if (parts.slice(1, 4).every((coord) => !isNaN(parseFloat(coord))))
|
|
152
152
|
valid_coords++;
|
|
153
153
|
}
|
package/dist/trajectory/index.js
CHANGED
|
@@ -77,7 +77,7 @@ export function validate_trajectory(trajectory) {
|
|
|
77
77
|
}
|
|
78
78
|
export function get_trajectory_stats(trajectory) {
|
|
79
79
|
const { frames, total_frames, indexed_frames, plot_metadata } = trajectory;
|
|
80
|
-
const frame_count = total_frames
|
|
80
|
+
const frame_count = total_frames ?? frames.length;
|
|
81
81
|
const stats = {
|
|
82
82
|
frame_count,
|
|
83
83
|
is_indexed: trajectory.is_indexed ?? false,
|
|
@@ -9,7 +9,7 @@ export async function parse_torch_sim_hdf5(buffer, filename) {
|
|
|
9
9
|
const file_basename = filename
|
|
10
10
|
?.split(`/`)
|
|
11
11
|
.at(-1)
|
|
12
|
-
?.
|
|
12
|
+
?.replaceAll(/[^\w.-]/g, `_`) ?? `temp`;
|
|
13
13
|
const unique_suffix = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
14
14
|
const temp_filename = `${file_basename}-${unique_suffix}.h5`;
|
|
15
15
|
FS.writeFile(temp_filename, new Uint8Array(buffer));
|
|
@@ -2,9 +2,9 @@ import { is_binary } from '../../io/is-binary';
|
|
|
2
2
|
import * as math from '../../math';
|
|
3
3
|
import { parse_xyz } from '../../structure/parse';
|
|
4
4
|
import { INDEX_SAMPLE_RATE, LARGE_FILE_THRESHOLD } from '../constants';
|
|
5
|
-
import { FORMAT_PATTERNS, is_trajectory_file, strip_compression_extensions, } from '../format-detect';
|
|
5
|
+
import { ext_hint, FORMAT_PATTERNS, is_trajectory_file, strip_compression_extensions, } from '../format-detect';
|
|
6
6
|
import { TrajFrameReader } from '../frame-reader';
|
|
7
|
-
import { create_trajectory_frame, validate_3x3_matrix } from '../helpers';
|
|
7
|
+
import { count_xyz_frames, create_trajectory_frame, validate_3x3_matrix, } from '../helpers';
|
|
8
8
|
import { parse_ase_trajectory } from './ase';
|
|
9
9
|
import { parse_torch_sim_hdf5 } from './hdf5';
|
|
10
10
|
import { parse_lammps_trajectory } from './lammps';
|
|
@@ -33,8 +33,10 @@ export async function parse_trajectory_data(data, filename, atom_type_mapping) {
|
|
|
33
33
|
if (FORMAT_PATTERNS.lammpstrj(content, filename)) {
|
|
34
34
|
return parse_lammps_trajectory(content, filename, atom_type_mapping);
|
|
35
35
|
}
|
|
36
|
-
// Single XYZ fallback
|
|
37
|
-
|
|
36
|
+
// Single XYZ fallback (content-sniffed when the filename gives no format hint,
|
|
37
|
+
// e.g. blob: object URLs whose basenames are UUIDs)
|
|
38
|
+
const xyz_hint = ext_hint(filename, /\.(xyz|extxyz)$/);
|
|
39
|
+
if (xyz_hint || (xyz_hint === null && count_xyz_frames(content) === 1)) {
|
|
38
40
|
try {
|
|
39
41
|
const structure = parse_xyz(content);
|
|
40
42
|
if (structure) {
|
|
@@ -65,7 +67,7 @@ export async function parse_trajectory_data(data, filename, atom_type_mapping) {
|
|
|
65
67
|
const frame_obj = frame_data;
|
|
66
68
|
const frame_step = frame_obj.step;
|
|
67
69
|
return {
|
|
68
|
-
structure: (frame_obj.structure
|
|
70
|
+
structure: (frame_obj.structure ?? frame_obj),
|
|
69
71
|
step: typeof frame_step === `number` ? frame_step : idx,
|
|
70
72
|
metadata: frame_obj.metadata || {},
|
|
71
73
|
};
|
|
@@ -198,8 +200,14 @@ export async function parse_trajectory_async(data, filename, on_progress, option
|
|
|
198
200
|
update_progress(5, `Large file detected (${Math.round(data_size / 1024 / 1024)}MB)`);
|
|
199
201
|
}
|
|
200
202
|
// Use indexed loading for supported large files (including compressed names).
|
|
203
|
+
// When the filename gives no format hint (e.g. blob: URLs), sniff a content
|
|
204
|
+
// prefix for XYZ frames so large extensionless files still get indexed.
|
|
201
205
|
const base_filename = strip_compression_extensions(filename);
|
|
202
|
-
|
|
206
|
+
const can_index = /\.(xyz|extxyz|traj)$/.test(base_filename) ||
|
|
207
|
+
(typeof data === `string` &&
|
|
208
|
+
ext_hint(filename, /\.(xyz|extxyz)$/) === null &&
|
|
209
|
+
count_xyz_frames(data.slice(0, 2 ** 20)) >= 1);
|
|
210
|
+
if (should_use_indexing && can_index) {
|
|
203
211
|
return await parse_with_unified_loader(data, filename, {
|
|
204
212
|
index_sample_rate,
|
|
205
213
|
extract_plot_metadata,
|
|
@@ -1,42 +1,61 @@
|
|
|
1
1
|
import * as math from '../../math';
|
|
2
2
|
import { create_trajectory_frame, is_valid_element_symbol, validate_3x3_matrix, } from '../helpers';
|
|
3
|
+
// Parse the 7-line XDATCAR header at lines[start]: title, scale factor, 3 lattice rows
|
|
4
|
+
// (multiplied by scale), element names, element counts
|
|
5
|
+
function parse_xdatcar_header(lines, start) {
|
|
6
|
+
const scale = parseFloat(lines[start + 1]);
|
|
7
|
+
const rows = lines.slice(start + 2, start + 5).map((line) => line
|
|
8
|
+
.trim()
|
|
9
|
+
.split(/\s+/)
|
|
10
|
+
.map((val) => parseFloat(val) * scale));
|
|
11
|
+
const names = lines[start + 5].trim().split(/\s+/);
|
|
12
|
+
const counts = lines[start + 6].trim().split(/\s+/).map(Number);
|
|
13
|
+
return { scale, rows, names, counts };
|
|
14
|
+
}
|
|
3
15
|
export function parse_vasp_xdatcar(content, filename) {
|
|
4
16
|
const lines = content.trim().split(/\r?\n/);
|
|
5
17
|
if (lines.length < 10)
|
|
6
18
|
throw new Error(`XDATCAR file too short`);
|
|
7
|
-
const
|
|
8
|
-
|
|
19
|
+
const header = parse_xdatcar_header(lines, 0);
|
|
20
|
+
const { names: element_names, counts: element_counts } = header;
|
|
21
|
+
if (isNaN(header.scale))
|
|
9
22
|
throw new Error(`Invalid scale factor`);
|
|
10
|
-
|
|
11
|
-
.trim()
|
|
12
|
-
.split(/\s+/)
|
|
13
|
-
.map((component) => parseFloat(component) * scale)));
|
|
14
|
-
const element_names = lines[5].trim().split(/\s+/);
|
|
15
|
-
const element_counts = lines[6].trim().split(/\s+/).map(Number);
|
|
23
|
+
let lattice_matrix = validate_3x3_matrix(header.rows);
|
|
16
24
|
if (element_names.length !== element_counts.length) {
|
|
17
25
|
throw new Error(`XDATCAR element names/counts mismatch: names=${element_names.length}, counts=${element_counts.length}`);
|
|
18
26
|
}
|
|
19
27
|
if (element_counts.some((count) => !Number.isFinite(count) || !Number.isInteger(count) || count <= 0)) {
|
|
20
28
|
throw new Error(`XDATCAR contains invalid element counts: expected finite positive integers`);
|
|
21
29
|
}
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return name;
|
|
27
|
-
});
|
|
28
|
-
const elements = validated_element_names.flatMap((name, idx) => Array(element_counts[idx]).fill(name));
|
|
30
|
+
const bad_element = element_names.find((name) => !is_valid_element_symbol(name));
|
|
31
|
+
if (bad_element)
|
|
32
|
+
throw new Error(`Invalid element symbol in XDATCAR: ${bad_element}`);
|
|
33
|
+
let elements = element_names.flatMap((name, idx) => Array(element_counts[idx]).fill(name));
|
|
29
34
|
const frames = [];
|
|
30
35
|
let line_idx = 7;
|
|
31
|
-
|
|
36
|
+
let frac_to_cart = math.create_frac_to_cart(lattice_matrix);
|
|
32
37
|
while (line_idx < lines.length) {
|
|
33
38
|
const config_idx = lines.findIndex((line, idx) => idx >= line_idx && line.includes(`Direct configuration=`));
|
|
34
39
|
if (config_idx === -1)
|
|
35
40
|
break;
|
|
41
|
+
// Variable-cell runs (NPT/ISIF=3) repeat the full 7-line header before each configuration
|
|
42
|
+
if (config_idx - line_idx >= 7) {
|
|
43
|
+
const hdr = parse_xdatcar_header(lines, config_idx - 7);
|
|
44
|
+
if (Number.isFinite(hdr.scale) &&
|
|
45
|
+
hdr.rows.every((row) => row.length === 3 && row.every(Number.isFinite))) {
|
|
46
|
+
lattice_matrix = validate_3x3_matrix(hdr.rows);
|
|
47
|
+
frac_to_cart = math.create_frac_to_cart(lattice_matrix);
|
|
48
|
+
if (hdr.names.length === hdr.counts.length &&
|
|
49
|
+
hdr.names.every(is_valid_element_symbol) &&
|
|
50
|
+
hdr.counts.every((count) => Number.isInteger(count) && count > 0)) {
|
|
51
|
+
elements = hdr.names.flatMap((name, idx) => Array(hdr.counts[idx]).fill(name));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
36
55
|
const config_line = lines[config_idx];
|
|
37
56
|
line_idx = config_idx + 1;
|
|
38
57
|
const step_match = /configuration=\s*(\d+)/.exec(config_line);
|
|
39
|
-
const step = step_match ? parseInt(step_match[1]) : frames.length + 1;
|
|
58
|
+
const step = step_match ? parseInt(step_match[1], 10) : frames.length + 1;
|
|
40
59
|
const positions = [];
|
|
41
60
|
for (let idx = 0; idx < elements.length && line_idx < lines.length; idx++) {
|
|
42
61
|
const coords = lines[line_idx].trim().split(/\s+/).slice(0, 3).map(Number);
|
|
@@ -1,2 +1,26 @@
|
|
|
1
|
+
import type { ElementSymbol } from '../../element/types';
|
|
2
|
+
import * as math from '../../math';
|
|
1
3
|
import type { TrajectoryType } from '../index';
|
|
4
|
+
export declare function parse_extxyz_columns(comment: string): {
|
|
5
|
+
species_col: number;
|
|
6
|
+
pos_col: number;
|
|
7
|
+
forces_col: number;
|
|
8
|
+
min_cols: number;
|
|
9
|
+
};
|
|
10
|
+
export declare function parse_extxyz_lattice(comment: string): math.Matrix3x3 | undefined;
|
|
11
|
+
export declare function parse_xyz_comment_metadata(comment: string): {
|
|
12
|
+
step?: number;
|
|
13
|
+
properties: Record<string, number>;
|
|
14
|
+
};
|
|
15
|
+
type ForceStats = {
|
|
16
|
+
forces: number[][];
|
|
17
|
+
force_max: number;
|
|
18
|
+
force_norm: number;
|
|
19
|
+
};
|
|
20
|
+
export declare function parse_xyz_atom_lines(lines: string[], start: number, num_atoms: number, comment: string, frame_label: string): {
|
|
21
|
+
elements: ElementSymbol[];
|
|
22
|
+
positions: number[][];
|
|
23
|
+
force_stats: ForceStats | null;
|
|
24
|
+
};
|
|
2
25
|
export declare function parse_xyz_trajectory(content: string): TrajectoryType;
|
|
26
|
+
export {};
|
|
@@ -1,103 +1,116 @@
|
|
|
1
1
|
import * as math from '../../math';
|
|
2
2
|
import { coerce_element_symbol, create_trajectory_frame } from '../helpers';
|
|
3
|
+
// Resolve species/pos/forces column offsets from an extxyz Properties string of
|
|
4
|
+
// name:type:ncols triples (e.g. "species:S:1:pos:R:3:forces:R:3"), falling back
|
|
5
|
+
// to the conventional "symbol x y z" layout when absent or malformed
|
|
6
|
+
export function parse_extxyz_columns(comment) {
|
|
7
|
+
const fields = /Properties\s*=\s*"?([^"\s]+)"?/i.exec(comment)?.[1].split(`:`) ?? [];
|
|
8
|
+
// Well-formed Properties is name:type:ncols triples; a non-multiple of 3 is malformed,
|
|
9
|
+
// so bail to the conventional default rather than trusting a partial layout
|
|
10
|
+
let layout = fields.length % 3 === 0 ? {} : null;
|
|
11
|
+
for (let idx = 0, offset = 0; layout && idx + 3 <= fields.length; idx += 3) {
|
|
12
|
+
const ncols = parseInt(fields[idx + 2], 10);
|
|
13
|
+
if (Number.isInteger(ncols) && ncols > 0) {
|
|
14
|
+
layout[fields[idx].toLowerCase()] = { offset, ncols };
|
|
15
|
+
offset += ncols;
|
|
16
|
+
}
|
|
17
|
+
else
|
|
18
|
+
layout = null;
|
|
19
|
+
}
|
|
20
|
+
const species_col = layout?.species?.offset ?? 0;
|
|
21
|
+
const pos_col = layout?.pos?.offset ?? 1;
|
|
22
|
+
const forces_col = layout?.forces && layout.forces.ncols >= 3 ? layout.forces.offset : -1;
|
|
23
|
+
return { species_col, pos_col, forces_col, min_cols: Math.max(pos_col + 3, species_col + 1) };
|
|
24
|
+
}
|
|
25
|
+
// Parse Lattice="ax ay az bx by bz cx cy cz" from an extxyz comment line
|
|
26
|
+
export function parse_extxyz_lattice(comment) {
|
|
27
|
+
const vals = /Lattice\s*=\s*"([^"]+)"/i.exec(comment)?.[1].trim().split(/\s+/).map(Number);
|
|
28
|
+
if (vals?.length !== 9 || !vals.every(Number.isFinite))
|
|
29
|
+
return undefined;
|
|
30
|
+
return [vals.slice(0, 3), vals.slice(3, 6), vals.slice(6, 9)];
|
|
31
|
+
}
|
|
32
|
+
// Keys anchored at ^|\s and followed by [=:] so single-letter keys (E/V/P/T) don't match mid-word
|
|
33
|
+
const make_pattern = (keys) => new RegExp(`(?:^|\\s)(?:${keys})\\s*[=:]\\s*([-+]?\\d*\\.?\\d+(?:[eE][-+]?\\d+)?)`, `i`);
|
|
34
|
+
const METADATA_PATTERNS = {
|
|
35
|
+
energy: make_pattern(`energy|E|etot|total_energy`),
|
|
36
|
+
volume: make_pattern(`volume|vol|V`),
|
|
37
|
+
pressure: make_pattern(`pressure|press|P`),
|
|
38
|
+
temperature: make_pattern(`temperature|temp|T`),
|
|
39
|
+
force_max: make_pattern(`max_force|force_max|fmax`),
|
|
40
|
+
bandgap: make_pattern(`bandgap|E_gap|gap`),
|
|
41
|
+
};
|
|
42
|
+
// Extract step number and scalar properties from an (ext)XYZ comment line
|
|
43
|
+
export function parse_xyz_comment_metadata(comment) {
|
|
44
|
+
const properties = {};
|
|
45
|
+
for (const [key, pattern] of Object.entries(METADATA_PATTERNS)) {
|
|
46
|
+
const match = pattern.exec(comment);
|
|
47
|
+
if (match)
|
|
48
|
+
properties[key] = parseFloat(match[1]);
|
|
49
|
+
}
|
|
50
|
+
const step = /(?:^|\s)(?:step|frame|ionic_step)\s*[=:]?\s*(\d+)/i.exec(comment)?.[1];
|
|
51
|
+
return { step: step ? parseInt(step, 10) : undefined, properties };
|
|
52
|
+
}
|
|
53
|
+
// Parse num_atoms atom lines starting at lines[start], reading species/pos/forces from
|
|
54
|
+
// their Properties-declared column offsets; invalid atoms are skipped with a warning.
|
|
55
|
+
// force_stats holds raw forces plus max and RMS force magnitudes when forces are present.
|
|
56
|
+
export function parse_xyz_atom_lines(lines, start, num_atoms, comment, frame_label) {
|
|
57
|
+
const { species_col, pos_col, forces_col, min_cols } = parse_extxyz_columns(comment);
|
|
58
|
+
const elements = [];
|
|
59
|
+
const positions = [];
|
|
60
|
+
const forces = [];
|
|
61
|
+
for (let idx = 0; idx < num_atoms; idx++) {
|
|
62
|
+
const parts = lines[start + idx]?.trim().split(/\s+/) ?? [];
|
|
63
|
+
if (parts.length < min_cols)
|
|
64
|
+
continue;
|
|
65
|
+
const pos = parts.slice(pos_col, pos_col + 3).map(parseFloat);
|
|
66
|
+
if (!pos.every(Number.isFinite)) {
|
|
67
|
+
console.warn(`Skipping XYZ atom with invalid coordinates in ${frame_label} at line ${start + idx + 1}`);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const symbol = parts[species_col];
|
|
71
|
+
const element_symbol = coerce_element_symbol(symbol);
|
|
72
|
+
if (!element_symbol) {
|
|
73
|
+
console.warn(`Skipping XYZ atom with unknown element symbol "${symbol}" in ${frame_label}`);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
elements.push(element_symbol);
|
|
77
|
+
positions.push(pos);
|
|
78
|
+
if (forces_col >= 0 && parts.length >= forces_col + 3) {
|
|
79
|
+
const force_vec = parts.slice(forces_col, forces_col + 3).map(parseFloat);
|
|
80
|
+
if (force_vec.every(Number.isFinite))
|
|
81
|
+
forces.push(force_vec);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (forces.length === 0)
|
|
85
|
+
return { elements, positions, force_stats: null };
|
|
86
|
+
const mags = forces.map((force) => Math.hypot(...force));
|
|
87
|
+
const force_norm = Math.sqrt(mags.reduce((sum, mag) => sum + mag ** 2, 0) / mags.length);
|
|
88
|
+
return {
|
|
89
|
+
elements,
|
|
90
|
+
positions,
|
|
91
|
+
force_stats: { forces, force_max: Math.max(...mags), force_norm },
|
|
92
|
+
};
|
|
93
|
+
}
|
|
3
94
|
export function parse_xyz_trajectory(content) {
|
|
4
95
|
const lines = content.trim().split(/\r?\n/);
|
|
5
96
|
const frames = [];
|
|
6
97
|
let line_idx = 0;
|
|
7
98
|
while (line_idx < lines.length) {
|
|
8
|
-
if (!lines[line_idx]?.trim()) {
|
|
9
|
-
line_idx++;
|
|
10
|
-
continue;
|
|
11
|
-
}
|
|
12
99
|
const num_atoms = parseInt(lines[line_idx].trim(), 10);
|
|
13
100
|
if (isNaN(num_atoms) || num_atoms <= 0 || line_idx + num_atoms + 1 >= lines.length) {
|
|
14
|
-
line_idx++;
|
|
101
|
+
line_idx++; // skip blank/invalid lines until the next frame's atom-count line
|
|
15
102
|
continue;
|
|
16
103
|
}
|
|
17
|
-
const comment = lines[
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
bandgap: /(?:bandgap|E_gap|gap)\s*[=:]?\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)/i,
|
|
28
|
-
};
|
|
29
|
-
const step_match = extractors.step.exec(comment);
|
|
30
|
-
const step = step_match?.[1] ? parseInt(step_match[1]) : frames.length;
|
|
31
|
-
Object.entries(extractors).forEach(([key, pattern]) => {
|
|
32
|
-
if (key === `step`)
|
|
33
|
-
return;
|
|
34
|
-
const match = pattern.exec(comment);
|
|
35
|
-
if (match)
|
|
36
|
-
metadata[key] = parseFloat(match[1]);
|
|
37
|
-
});
|
|
38
|
-
// Extract lattice matrix
|
|
39
|
-
const lattice_match = /Lattice\s*=\s*"([^"]+)"/i.exec(comment);
|
|
40
|
-
let lattice_matrix;
|
|
41
|
-
if (lattice_match) {
|
|
42
|
-
const values = lattice_match[1].split(/\s+/).map(Number);
|
|
43
|
-
if (values.length === 9 && values.every((value) => Number.isFinite(value))) {
|
|
44
|
-
lattice_matrix = [
|
|
45
|
-
[values[0], values[1], values[2]],
|
|
46
|
-
[values[3], values[4], values[5]],
|
|
47
|
-
[values[6], values[7], values[8]],
|
|
48
|
-
];
|
|
49
|
-
metadata.volume = math.calc_lattice_params(lattice_matrix).volume;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
// Parse atoms
|
|
53
|
-
const positions = [];
|
|
54
|
-
const elements = [];
|
|
55
|
-
const forces = [];
|
|
56
|
-
const has_forces = comment.includes(`forces:R:3`);
|
|
57
|
-
for (let idx = 0; idx < num_atoms; idx++) {
|
|
58
|
-
line_idx++;
|
|
59
|
-
if (line_idx >= lines.length)
|
|
60
|
-
break;
|
|
61
|
-
const parts = lines[line_idx].trim().split(/\s+/);
|
|
62
|
-
if (parts.length >= 4) {
|
|
63
|
-
const x_coord = parseFloat(parts[1]);
|
|
64
|
-
const y_coord = parseFloat(parts[2]);
|
|
65
|
-
const z_coord = parseFloat(parts[3]);
|
|
66
|
-
if (!Number.isFinite(x_coord) ||
|
|
67
|
-
!Number.isFinite(y_coord) ||
|
|
68
|
-
!Number.isFinite(z_coord)) {
|
|
69
|
-
console.warn(`Skipping XYZ atom with invalid coordinates in frame ${frames.length} at line ${line_idx + 1}`);
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
const raw_symbol = parts[0];
|
|
73
|
-
const element_symbol = coerce_element_symbol(raw_symbol);
|
|
74
|
-
if (!element_symbol) {
|
|
75
|
-
console.warn(`Skipping XYZ atom with unknown element symbol "${raw_symbol}" in frame ${frames.length}`);
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
elements.push(element_symbol);
|
|
79
|
-
positions.push([x_coord, y_coord, z_coord]);
|
|
80
|
-
if (has_forces && parts.length >= 7) {
|
|
81
|
-
const force_x = parseFloat(parts[4]);
|
|
82
|
-
const force_y = parseFloat(parts[5]);
|
|
83
|
-
const force_z = parseFloat(parts[6]);
|
|
84
|
-
if (Number.isFinite(force_x) &&
|
|
85
|
-
Number.isFinite(force_y) &&
|
|
86
|
-
Number.isFinite(force_z)) {
|
|
87
|
-
forces.push([force_x, force_y, force_z]);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
if (forces.length > 0) {
|
|
93
|
-
metadata.forces = forces;
|
|
94
|
-
const magnitudes = forces.map((force) => Math.hypot(...force));
|
|
95
|
-
metadata.force_max = Math.max(...magnitudes);
|
|
96
|
-
// Calculate RMS (root mean square) of force magnitudes
|
|
97
|
-
metadata.force_norm = Math.sqrt(magnitudes.reduce((sum, mag) => sum + mag ** 2, 0) / magnitudes.length);
|
|
98
|
-
}
|
|
99
|
-
frames.push(create_trajectory_frame(positions, elements, lattice_matrix, lattice_matrix ? [true, true, true] : undefined, step, metadata));
|
|
100
|
-
line_idx++;
|
|
104
|
+
const comment = lines[line_idx + 1] || ``;
|
|
105
|
+
const { step, properties } = parse_xyz_comment_metadata(comment);
|
|
106
|
+
const metadata = { ...properties };
|
|
107
|
+
const lattice_matrix = parse_extxyz_lattice(comment);
|
|
108
|
+
if (lattice_matrix)
|
|
109
|
+
metadata.volume = math.calc_lattice_params(lattice_matrix).volume;
|
|
110
|
+
const { elements, positions, force_stats } = parse_xyz_atom_lines(lines, line_idx + 2, num_atoms, comment, `frame ${frames.length}`);
|
|
111
|
+
Object.assign(metadata, force_stats);
|
|
112
|
+
frames.push(create_trajectory_frame(positions, elements, lattice_matrix, lattice_matrix ? [true, true, true] : undefined, step ?? frames.length, metadata));
|
|
113
|
+
line_idx += num_atoms + 2;
|
|
101
114
|
}
|
|
102
115
|
return {
|
|
103
116
|
frames,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DataSeries } from '../plot/types';
|
|
1
|
+
import type { DataSeries } from '../plot/core/types';
|
|
2
2
|
import type { TrajectoryDataExtractor, TrajectoryMetadata, TrajectoryType } from './index';
|
|
3
3
|
export interface PlotSeriesOptions {
|
|
4
4
|
property_config?: Record<string, {
|