matterviz 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/EmptyState.svelte +10 -2
- package/dist/FilePicker.svelte +154 -96
- package/dist/Icon.svelte +20 -14
- package/dist/MillerIndexInput.svelte +27 -21
- package/dist/api/optimade.js +6 -6
- package/dist/app.css +216 -178
- package/dist/brillouin/BrillouinZone.svelte +299 -198
- package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
- package/dist/brillouin/BrillouinZoneExportPane.svelte +74 -55
- package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
- package/dist/brillouin/BrillouinZoneScene.svelte +277 -165
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneTooltip.svelte +17 -7
- package/dist/brillouin/compute.js +11 -6
- package/dist/chempot-diagram/ChemPotDiagram.svelte +327 -0
- package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +847 -0
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +3194 -0
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
- package/dist/chempot-diagram/ChemPotScene3D.svelte +11 -0
- package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
- package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
- package/dist/chempot-diagram/async-compute.svelte.js +77 -0
- package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
- package/dist/chempot-diagram/chempot-worker.js +11 -0
- package/dist/chempot-diagram/color.d.ts +10 -0
- package/dist/chempot-diagram/color.js +32 -0
- package/dist/chempot-diagram/compute.d.ts +48 -0
- package/dist/chempot-diagram/compute.js +812 -0
- package/dist/chempot-diagram/index.d.ts +6 -0
- package/dist/chempot-diagram/index.js +6 -0
- package/dist/chempot-diagram/pointer.d.ts +16 -0
- package/dist/chempot-diagram/pointer.js +40 -0
- package/dist/chempot-diagram/temperature.d.ts +15 -0
- package/dist/chempot-diagram/temperature.js +36 -0
- package/dist/chempot-diagram/types.d.ts +86 -0
- package/dist/chempot-diagram/types.js +28 -0
- package/dist/colors/index.d.ts +3 -1
- package/dist/colors/index.js +9 -3
- package/dist/composition/BarChart.svelte +141 -77
- package/dist/composition/BubbleChart.svelte +107 -52
- package/dist/composition/Composition.svelte +100 -79
- package/dist/composition/Formula.svelte +108 -62
- package/dist/composition/FormulaFilter.svelte +973 -353
- package/dist/composition/FormulaFilter.svelte.d.ts +35 -1
- package/dist/composition/PieChart.svelte +199 -99
- package/dist/composition/PieChart.svelte.d.ts +1 -1
- package/dist/composition/format.d.ts +5 -0
- package/dist/composition/format.js +20 -3
- package/dist/composition/parse.js +14 -9
- package/dist/convex-hull/ConvexHull.svelte +93 -38
- package/dist/convex-hull/ConvexHull2D.svelte +551 -393
- package/dist/convex-hull/ConvexHull3D.svelte +1303 -825
- package/dist/convex-hull/ConvexHull4D.svelte +1012 -686
- package/dist/convex-hull/ConvexHullControls.svelte +115 -28
- package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
- package/dist/convex-hull/ConvexHullStats.svelte +821 -249
- package/dist/convex-hull/ConvexHullStats.svelte.d.ts +6 -1
- package/dist/convex-hull/ConvexHullTooltip.svelte +41 -16
- package/dist/convex-hull/GasPressureControls.svelte +104 -61
- package/dist/convex-hull/StructurePopup.svelte +25 -4
- package/dist/convex-hull/TemperatureSlider.svelte +45 -25
- package/dist/convex-hull/barycentric-coords.js +13 -7
- package/dist/convex-hull/demo-temperature.d.ts +6 -0
- package/dist/convex-hull/demo-temperature.js +40 -0
- package/dist/convex-hull/gas-thermodynamics.js +17 -12
- package/dist/convex-hull/helpers.d.ts +10 -1
- package/dist/convex-hull/helpers.js +79 -38
- package/dist/convex-hull/index.d.ts +1 -0
- package/dist/convex-hull/index.js +1 -0
- package/dist/convex-hull/thermodynamics.d.ts +8 -21
- package/dist/convex-hull/thermodynamics.js +163 -69
- package/dist/convex-hull/types.d.ts +12 -12
- package/dist/convex-hull/types.js +0 -12
- package/dist/coordination/CoordinationBarPlot.svelte +232 -176
- package/dist/element/BohrAtom.svelte +56 -13
- package/dist/element/ElementHeading.svelte +7 -2
- package/dist/element/ElementPhoto.svelte +15 -9
- package/dist/element/ElementStats.svelte +10 -4
- package/dist/element/ElementTile.svelte +137 -73
- package/dist/element/Nucleus.svelte +39 -11
- package/dist/element/data.js +2 -14
- package/dist/element/data.json.gz +0 -0
- package/dist/element/types.d.ts +1 -0
- package/dist/feedback/ClickFeedback.svelte +16 -5
- package/dist/feedback/DragOverlay.svelte +10 -2
- package/dist/feedback/Spinner.svelte +4 -2
- package/dist/feedback/StatusMessage.svelte +8 -2
- package/dist/fermi-surface/FermiSlice.svelte +118 -88
- package/dist/fermi-surface/FermiSurface.svelte +336 -239
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
- package/dist/fermi-surface/FermiSurfaceScene.svelte +536 -343
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
- package/dist/fermi-surface/compute.js +16 -20
- package/dist/fermi-surface/parse.js +37 -33
- package/dist/fermi-surface/symmetry.js +2 -7
- package/dist/fermi-surface/types.d.ts +3 -5
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +1527 -0
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +225 -0
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +30 -0
- package/dist/heatmap-matrix/index.d.ts +53 -0
- package/dist/heatmap-matrix/index.js +100 -0
- package/dist/heatmap-matrix/shared.d.ts +2 -0
- package/dist/heatmap-matrix/shared.js +4 -0
- package/dist/icons.d.ts +111 -0
- package/dist/icons.js +158 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +5 -2
- package/dist/io/decompress.js +1 -1
- package/dist/io/export.d.ts +3 -0
- package/dist/io/export.js +138 -140
- package/dist/io/file-drop.d.ts +7 -0
- package/dist/io/file-drop.js +43 -0
- package/dist/io/index.d.ts +2 -2
- package/dist/io/index.js +2 -112
- package/dist/io/is-binary.js +2 -3
- package/dist/io/types.d.ts +1 -0
- package/dist/io/url-drop.d.ts +2 -0
- package/dist/io/url-drop.js +117 -0
- package/dist/isosurface/Isosurface.svelte +220 -110
- package/dist/isosurface/IsosurfaceControls.svelte +65 -28
- package/dist/isosurface/parse.js +104 -56
- package/dist/isosurface/slice.d.ts +2 -1
- package/dist/isosurface/slice.js +8 -13
- package/dist/isosurface/types.d.ts +14 -1
- package/dist/isosurface/types.js +152 -5
- package/dist/labels.d.ts +2 -1
- package/dist/labels.js +12 -8
- package/dist/layout/FullscreenToggle.svelte +11 -2
- package/dist/layout/InfoCard.svelte +38 -6
- package/dist/layout/InfoTag.svelte +125 -94
- package/dist/layout/PropertyFilter.svelte +82 -37
- package/dist/layout/SettingsSection.svelte +85 -55
- package/dist/layout/SubpageGrid.svelte +82 -0
- package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
- package/dist/layout/index.d.ts +1 -0
- package/dist/layout/index.js +1 -0
- package/dist/layout/json-tree/JsonNode.svelte +266 -223
- package/dist/layout/json-tree/JsonTree.svelte +516 -429
- package/dist/layout/json-tree/JsonTree.svelte.d.ts +1 -1
- package/dist/layout/json-tree/JsonValue.svelte +281 -173
- package/dist/layout/json-tree/types.d.ts +10 -2
- package/dist/layout/json-tree/utils.d.ts +2 -0
- package/dist/layout/json-tree/utils.js +37 -2
- package/dist/marching-cubes.js +25 -2
- package/dist/math.d.ts +20 -17
- package/dist/math.js +474 -57
- package/dist/overlays/ContextMenu.svelte +66 -40
- package/dist/overlays/DraggablePane.svelte +331 -154
- package/dist/overlays/DraggablePane.svelte.d.ts +2 -0
- package/dist/periodic-table/PeriodicTable.svelte +278 -145
- package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
- package/dist/periodic-table/PropertySelect.svelte +25 -7
- package/dist/periodic-table/TableInset.svelte +8 -3
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +559 -267
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -2
- package/dist/phase-diagram/PhaseDiagramControls.svelte +131 -51
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +3 -2
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +126 -0
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +160 -110
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +8 -1
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +217 -86
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +6 -3
- package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
- package/dist/phase-diagram/build-diagram.js +9 -9
- package/dist/phase-diagram/colors.js +1 -3
- package/dist/phase-diagram/index.d.ts +2 -0
- package/dist/phase-diagram/index.js +2 -0
- package/dist/phase-diagram/parse.js +10 -9
- package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
- package/dist/phase-diagram/svg-to-diagram.js +869 -0
- package/dist/phase-diagram/types.d.ts +10 -0
- package/dist/phase-diagram/utils.d.ts +8 -4
- package/dist/phase-diagram/utils.js +219 -74
- package/dist/plot/AxisLabel.svelte +51 -0
- package/dist/plot/AxisLabel.svelte.d.ts +16 -0
- package/dist/plot/BarPlot.svelte +1461 -768
- package/dist/plot/BarPlot.svelte.d.ts +3 -3
- package/dist/plot/BarPlotControls.svelte +33 -6
- package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ColorBar.svelte +533 -383
- package/dist/plot/ColorBar.svelte.d.ts +1 -1
- package/dist/plot/ColorScaleSelect.svelte +28 -7
- package/dist/plot/ElementScatter.svelte +38 -16
- package/dist/plot/FillArea.svelte +152 -92
- package/dist/plot/Histogram.svelte +1162 -709
- package/dist/plot/Histogram.svelte.d.ts +1 -1
- package/dist/plot/HistogramControls.svelte +81 -18
- package/dist/plot/HistogramControls.svelte.d.ts +6 -2
- package/dist/plot/InteractiveAxisLabel.svelte +34 -11
- package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
- package/dist/plot/Line.svelte +63 -28
- package/dist/plot/PlotControls.svelte +221 -96
- package/dist/plot/PlotControls.svelte.d.ts +1 -1
- package/dist/plot/PlotLegend.svelte +174 -91
- package/dist/plot/PlotTooltip.svelte +45 -6
- package/dist/plot/PortalSelect.svelte +175 -146
- package/dist/plot/ReferenceLine.svelte +77 -22
- package/dist/plot/ReferenceLine.svelte.d.ts +1 -0
- package/dist/plot/ReferenceLine3D.svelte +132 -107
- package/dist/plot/ReferencePlane.svelte +146 -123
- package/dist/plot/ScatterPlot.svelte +1880 -1156
- package/dist/plot/ScatterPlot.svelte.d.ts +3 -3
- package/dist/plot/ScatterPlot3D.svelte +256 -131
- package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3DControls.svelte +300 -297
- package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
- package/dist/plot/ScatterPlot3DScene.svelte +608 -406
- package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlotControls.svelte +150 -70
- package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ScatterPoint.svelte +98 -26
- package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
- package/dist/plot/SpacegroupBarPlot.svelte +142 -85
- package/dist/plot/Surface3D.svelte +159 -108
- package/dist/plot/ZeroLines.svelte +96 -0
- package/dist/plot/ZeroLines.svelte.d.ts +32 -0
- package/dist/plot/ZoomRect.svelte +23 -0
- package/dist/plot/ZoomRect.svelte.d.ts +8 -0
- package/dist/plot/axis-utils.d.ts +1 -1
- package/dist/plot/axis-utils.js +1 -3
- package/dist/plot/data-cleaning.js +12 -28
- package/dist/plot/data-transform.js +2 -1
- package/dist/plot/fill-utils.js +2 -0
- package/dist/plot/index.d.ts +6 -2
- package/dist/plot/index.js +6 -2
- package/dist/plot/interactions.d.ts +8 -10
- package/dist/plot/interactions.js +2 -3
- package/dist/plot/layout.d.ts +11 -2
- package/dist/plot/layout.js +44 -17
- package/dist/plot/reference-line.d.ts +5 -22
- package/dist/plot/reference-line.js +12 -84
- package/dist/plot/scales.js +24 -36
- package/dist/plot/types.d.ts +53 -40
- package/dist/plot/types.js +12 -7
- package/dist/plot/utils/label-placement.d.ts +32 -15
- package/dist/plot/utils/label-placement.js +227 -63
- package/dist/plot/utils/series-visibility.js +2 -3
- package/dist/plot/utils.d.ts +1 -0
- package/dist/plot/utils.js +14 -0
- package/dist/rdf/RdfPlot.svelte +173 -132
- package/dist/rdf/calc-rdf.js +4 -5
- package/dist/sanitize.d.ts +4 -0
- package/dist/sanitize.js +107 -0
- package/dist/settings.d.ts +21 -6
- package/dist/settings.js +63 -19
- package/dist/spectral/Bands.svelte +963 -412
- package/dist/spectral/Bands.svelte.d.ts +22 -2
- package/dist/spectral/BandsAndDos.svelte +90 -49
- package/dist/spectral/BrillouinBandsDos.svelte +151 -93
- package/dist/spectral/Dos.svelte +389 -258
- package/dist/spectral/helpers.d.ts +23 -1
- package/dist/spectral/helpers.js +119 -51
- package/dist/spectral/types.d.ts +2 -0
- package/dist/state.svelte.d.ts +1 -1
- package/dist/state.svelte.js +3 -2
- package/dist/structure/Arrow.svelte +59 -20
- package/dist/structure/AtomLegend.svelte +231 -129
- package/dist/structure/AtomLegend.svelte.d.ts +1 -1
- package/dist/structure/Bond.svelte +73 -47
- package/dist/structure/CanvasTooltip.svelte +10 -2
- package/dist/structure/CellSelect.svelte +148 -51
- package/dist/structure/Cylinder.svelte +33 -17
- package/dist/structure/Lattice.svelte +88 -33
- package/dist/structure/Structure.svelte +1077 -821
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureControls.svelte +373 -139
- package/dist/structure/StructureControls.svelte.d.ts +1 -1
- package/dist/structure/StructureExportPane.svelte +124 -89
- package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +304 -231
- package/dist/structure/StructureScene.svelte +919 -445
- package/dist/structure/StructureScene.svelte.d.ts +16 -7
- package/dist/structure/atom-properties.d.ts +6 -2
- package/dist/structure/atom-properties.js +42 -29
- package/dist/structure/bonding.js +6 -7
- package/dist/structure/export.js +22 -34
- package/dist/structure/ferrox-wasm-types.d.ts +3 -2
- package/dist/structure/ferrox-wasm-types.js +0 -3
- package/dist/structure/ferrox-wasm.d.ts +3 -2
- package/dist/structure/ferrox-wasm.js +2 -3
- package/dist/structure/index.d.ts +16 -0
- package/dist/structure/index.js +88 -6
- package/dist/structure/measure.d.ts +2 -2
- package/dist/structure/measure.js +4 -44
- package/dist/structure/parse.js +130 -155
- package/dist/structure/partial-occupancy.d.ts +25 -0
- package/dist/structure/partial-occupancy.js +99 -0
- package/dist/structure/pbc.d.ts +1 -0
- package/dist/structure/pbc.js +16 -6
- package/dist/structure/supercell.d.ts +2 -2
- package/dist/structure/supercell.js +12 -22
- package/dist/structure/validation.js +5 -3
- package/dist/symmetry/SymmetryStats.svelte +94 -37
- package/dist/symmetry/WyckoffTable.svelte +42 -14
- package/dist/symmetry/cell-transform.js +5 -3
- package/dist/symmetry/index.d.ts +7 -4
- package/dist/symmetry/index.js +87 -21
- package/dist/symmetry/spacegroups.js +148 -148
- package/dist/table/HeatmapTable.svelte +1112 -516
- package/dist/table/HeatmapTable.svelte.d.ts +12 -1
- package/dist/table/ToggleMenu.svelte +125 -90
- package/dist/table/index.d.ts +2 -0
- package/dist/table/index.js +2 -4
- package/dist/theme/ThemeControl.svelte +21 -12
- package/dist/time.js +4 -1
- package/dist/tooltip/TooltipContent.svelte +33 -8
- package/dist/trajectory/Trajectory.svelte +889 -687
- package/dist/trajectory/TrajectoryError.svelte +14 -3
- package/dist/trajectory/TrajectoryExportPane.svelte +148 -90
- package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +1 -1
- package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
- package/dist/trajectory/constants.d.ts +6 -0
- package/dist/trajectory/constants.js +7 -0
- package/dist/trajectory/extract.js +13 -31
- package/dist/trajectory/format-detect.d.ts +9 -0
- package/dist/trajectory/format-detect.js +76 -0
- package/dist/trajectory/frame-reader.d.ts +17 -0
- package/dist/trajectory/frame-reader.js +332 -0
- package/dist/trajectory/helpers.d.ts +14 -0
- package/dist/trajectory/helpers.js +172 -0
- package/dist/trajectory/index.d.ts +1 -0
- package/dist/trajectory/index.js +23 -14
- package/dist/trajectory/parse/ase.d.ts +2 -0
- package/dist/trajectory/parse/ase.js +77 -0
- package/dist/trajectory/parse/hdf5.d.ts +2 -0
- package/dist/trajectory/parse/hdf5.js +129 -0
- package/dist/trajectory/parse/index.d.ts +12 -0
- package/dist/trajectory/parse/index.js +299 -0
- package/dist/trajectory/parse/lammps.d.ts +5 -0
- package/dist/trajectory/parse/lammps.js +179 -0
- package/dist/trajectory/parse/vasp.d.ts +2 -0
- package/dist/trajectory/parse/vasp.js +68 -0
- package/dist/trajectory/parse/xyz.d.ts +2 -0
- package/dist/trajectory/parse/xyz.js +110 -0
- package/dist/trajectory/plotting.js +13 -8
- package/dist/trajectory/types.d.ts +11 -0
- package/dist/trajectory/types.js +1 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +17 -0
- package/dist/xrd/XrdPlot.svelte +337 -245
- package/dist/xrd/broadening.js +14 -9
- package/dist/xrd/calc-xrd.js +12 -19
- package/dist/xrd/parse.d.ts +1 -1
- package/dist/xrd/parse.js +17 -17
- package/package.json +103 -101
- package/readme.md +4 -4
- package/dist/trajectory/parse.d.ts +0 -42
- package/dist/trajectory/parse.js +0 -1267
- /package/dist/element/{data.json.d.ts → data.json.gz.d.ts} +0 -0
- /package/dist/theme/{themes.js → themes.mjs} +0 -0
package/dist/structure/parse.js
CHANGED
|
@@ -2,9 +2,8 @@ import { COMPRESSION_EXTENSIONS_REGEX, CONFIG_DIRS_REGEX, STRUCT_KEYWORDS_REGEX,
|
|
|
2
2
|
import { ELEM_SYMBOLS } from '../labels';
|
|
3
3
|
import * as math from '../math';
|
|
4
4
|
import { wrap_to_unit_cell } from './pbc';
|
|
5
|
+
import { normalize_scientific_notation } from '../utils';
|
|
5
6
|
import { load as yaml_load } from 'js-yaml';
|
|
6
|
-
// Normalize scientific notation in coordinate strings (handles eEdD and *^ notation variants)
|
|
7
|
-
const normalize_scientific_notation = (str) => str.toLowerCase().replace(/d/g, `e`).replace(/\*\^/g, `e`);
|
|
8
7
|
// Parse a coordinate value that might be in various scientific notation formats
|
|
9
8
|
function parse_coordinate(str) {
|
|
10
9
|
const normalized = normalize_scientific_notation(str.trim());
|
|
@@ -44,6 +43,19 @@ function validate_element_symbol(symbol, index) {
|
|
|
44
43
|
console.warn(`Invalid element symbol '${symbol}', using fallback '${fallback}'`);
|
|
45
44
|
return fallback;
|
|
46
45
|
}
|
|
46
|
+
const try_create_cart_to_frac = (lattice_matrix) => {
|
|
47
|
+
try {
|
|
48
|
+
return math.create_cart_to_frac(lattice_matrix);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const approximate_cart_to_frac = (xyz, axis_lengths) => [
|
|
55
|
+
Math.abs(axis_lengths[0]) > math.EPS ? xyz[0] / axis_lengths[0] : 0,
|
|
56
|
+
Math.abs(axis_lengths[1]) > math.EPS ? xyz[1] / axis_lengths[1] : 0,
|
|
57
|
+
Math.abs(axis_lengths[2]) > math.EPS ? xyz[2] / axis_lengths[2] : 0,
|
|
58
|
+
];
|
|
47
59
|
// Parse VASP POSCAR file format
|
|
48
60
|
export function parse_poscar(content) {
|
|
49
61
|
try {
|
|
@@ -98,9 +110,7 @@ export function parse_poscar(content) {
|
|
|
98
110
|
for (let lookahead_idx = 1; lookahead_idx < 10; lookahead_idx++) {
|
|
99
111
|
if (line_index + lookahead_idx >= lines.length)
|
|
100
112
|
break;
|
|
101
|
-
const next_line_first_token = lines[line_index + lookahead_idx]
|
|
102
|
-
.trim()
|
|
103
|
-
.split(/\s+/)[0];
|
|
113
|
+
const next_line_first_token = lines[line_index + lookahead_idx].trim().split(/\s+/)[0];
|
|
104
114
|
const next_token_as_number = parseInt(next_line_first_token);
|
|
105
115
|
if (!isNaN(next_token_as_number)) {
|
|
106
116
|
symbol_lines = lookahead_idx;
|
|
@@ -152,13 +162,18 @@ export function parse_poscar(content) {
|
|
|
152
162
|
}
|
|
153
163
|
// Determine coordinate mode
|
|
154
164
|
const is_direct = coordinate_mode.startsWith(`D`);
|
|
155
|
-
const is_cartesian = coordinate_mode.startsWith(`C`) ||
|
|
156
|
-
coordinate_mode.startsWith(`K`);
|
|
165
|
+
const is_cartesian = coordinate_mode.startsWith(`C`) || coordinate_mode.startsWith(`K`);
|
|
157
166
|
if (!is_direct && !is_cartesian) {
|
|
158
167
|
console.error(`Unknown coordinate mode in POSCAR: ${coordinate_mode}`);
|
|
159
168
|
return null;
|
|
160
169
|
}
|
|
161
170
|
// Parse atomic positions
|
|
171
|
+
const poscar_axis_lengths = scaled_lattice.map((lattice_vec) => Math.hypot(...lattice_vec));
|
|
172
|
+
const poscar_frac_to_cart = math.create_frac_to_cart(scaled_lattice);
|
|
173
|
+
const poscar_cart_to_frac = try_create_cart_to_frac(scaled_lattice);
|
|
174
|
+
if (!is_direct && !poscar_cart_to_frac) {
|
|
175
|
+
console.warn(`POSCAR: singular lattice, using axis-length fallback for cart→frac`);
|
|
176
|
+
}
|
|
162
177
|
const sites = [];
|
|
163
178
|
let atom_index = 0;
|
|
164
179
|
for (let elem_idx = 0; elem_idx < element_symbols.length; elem_idx++) {
|
|
@@ -176,11 +191,7 @@ export function parse_poscar(content) {
|
|
|
176
191
|
if (has_selective_dynamics) {
|
|
177
192
|
const tokens = lines[coord_line_idx].trim().split(/\s+/);
|
|
178
193
|
if (tokens.length >= 6) {
|
|
179
|
-
selective_dynamics = [
|
|
180
|
-
tokens[3] === `T`,
|
|
181
|
-
tokens[4] === `T`,
|
|
182
|
-
tokens[5] === `T`,
|
|
183
|
-
];
|
|
194
|
+
selective_dynamics = [tokens[3] === `T`, tokens[4] === `T`, tokens[5] === `T`];
|
|
184
195
|
}
|
|
185
196
|
}
|
|
186
197
|
let xyz;
|
|
@@ -188,45 +199,24 @@ export function parse_poscar(content) {
|
|
|
188
199
|
const [x, y, z] = coords;
|
|
189
200
|
if (is_direct) {
|
|
190
201
|
// Store fractional coordinates, wrapping to [0, 1) range
|
|
191
|
-
abc = [x
|
|
192
|
-
|
|
193
|
-
const lattice_transposed = math.transpose_3x3_matrix(scaled_lattice);
|
|
194
|
-
xyz = math.mat3x3_vec3_multiply(lattice_transposed, abc);
|
|
202
|
+
abc = wrap_to_unit_cell([x, y, z]);
|
|
203
|
+
xyz = poscar_frac_to_cart(abc);
|
|
195
204
|
}
|
|
196
|
-
else {
|
|
205
|
+
else {
|
|
206
|
+
// Already Cartesian, scale if needed
|
|
197
207
|
xyz = math.scale([x, y, z], scale_factor);
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
let raw_abc;
|
|
202
|
-
try {
|
|
203
|
-
const lattice_transposed = math.transpose_3x3_matrix(scaled_lattice);
|
|
204
|
-
const lattice_inv = math.matrix_inverse_3x3(lattice_transposed);
|
|
205
|
-
raw_abc = math.mat3x3_vec3_multiply(lattice_inv, xyz);
|
|
206
|
-
}
|
|
207
|
-
catch {
|
|
208
|
-
// Fallback to simplified method if matrix is singular
|
|
209
|
-
raw_abc = [
|
|
210
|
-
xyz[0] / scaled_lattice[0][0],
|
|
211
|
-
xyz[1] / scaled_lattice[1][1],
|
|
212
|
-
xyz[2] / scaled_lattice[2][2],
|
|
213
|
-
];
|
|
214
|
-
}
|
|
208
|
+
const raw_abc = poscar_cart_to_frac
|
|
209
|
+
? poscar_cart_to_frac(xyz)
|
|
210
|
+
: approximate_cart_to_frac(xyz, poscar_axis_lengths);
|
|
215
211
|
// Wrap fractional coordinates to [0, 1) range
|
|
216
|
-
abc =
|
|
217
|
-
raw_abc[0] - Math.floor(raw_abc[0]),
|
|
218
|
-
raw_abc[1] - Math.floor(raw_abc[1]),
|
|
219
|
-
raw_abc[2] - Math.floor(raw_abc[2]),
|
|
220
|
-
];
|
|
212
|
+
abc = wrap_to_unit_cell(raw_abc);
|
|
221
213
|
}
|
|
222
214
|
const site = {
|
|
223
215
|
species: [{ element, occu: 1, oxidation_state: 0 }],
|
|
224
216
|
abc,
|
|
225
217
|
xyz,
|
|
226
218
|
label: `${element}${atom_index + atom_count_idx + 1}`,
|
|
227
|
-
properties: selective_dynamics
|
|
228
|
-
? { selective_dynamics: selective_dynamics }
|
|
229
|
-
: {},
|
|
219
|
+
properties: selective_dynamics ? { selective_dynamics: selective_dynamics } : {},
|
|
230
220
|
};
|
|
231
221
|
sites.push(site);
|
|
232
222
|
}
|
|
@@ -263,9 +253,7 @@ export function parse_xyz(content) {
|
|
|
263
253
|
let line_idx = 0;
|
|
264
254
|
while (line_idx < all_lines.length) {
|
|
265
255
|
const numAtoms = parseInt(all_lines[line_idx].trim(), 10);
|
|
266
|
-
if (!isNaN(numAtoms) &&
|
|
267
|
-
numAtoms > 0 &&
|
|
268
|
-
line_idx + numAtoms + 1 < all_lines.length) {
|
|
256
|
+
if (!isNaN(numAtoms) && numAtoms > 0 && line_idx + numAtoms + 1 < all_lines.length) {
|
|
269
257
|
const frameLines = all_lines.slice(line_idx, line_idx + numAtoms + 2);
|
|
270
258
|
frames.push(frameLines.join(`\n`));
|
|
271
259
|
line_idx += numAtoms + 2;
|
|
@@ -293,7 +281,7 @@ export function parse_xyz(content) {
|
|
|
293
281
|
const comment_line = lines[1];
|
|
294
282
|
let lattice;
|
|
295
283
|
// Check for extended XYZ lattice information in comment line
|
|
296
|
-
const lattice_match =
|
|
284
|
+
const lattice_match = /Lattice="([^"]+)"/.exec(comment_line);
|
|
297
285
|
if (lattice_match) {
|
|
298
286
|
const lattice_values = lattice_match[1].split(/\s+/).map(parse_coordinate);
|
|
299
287
|
if (lattice_values.length === 9) {
|
|
@@ -307,6 +295,13 @@ export function parse_xyz(content) {
|
|
|
307
295
|
}
|
|
308
296
|
}
|
|
309
297
|
// Parse atomic coordinates (starting from line 3)
|
|
298
|
+
const xyz_axis_lengths = lattice ? [lattice.a, lattice.b, lattice.c] : null;
|
|
299
|
+
let xyz_frac_to_cart = null;
|
|
300
|
+
let xyz_cart_to_frac = null;
|
|
301
|
+
if (lattice) {
|
|
302
|
+
xyz_frac_to_cart = math.create_frac_to_cart(lattice.matrix);
|
|
303
|
+
xyz_cart_to_frac = try_create_cart_to_frac(lattice.matrix);
|
|
304
|
+
}
|
|
310
305
|
const sites = [];
|
|
311
306
|
for (let atom_idx = 0; atom_idx < num_atoms; atom_idx++) {
|
|
312
307
|
const line_idx = atom_idx + 2;
|
|
@@ -329,29 +324,14 @@ export function parse_xyz(content) {
|
|
|
329
324
|
const xyz = [coords[0], coords[1], coords[2]];
|
|
330
325
|
// Calculate fractional coordinates if lattice is available
|
|
331
326
|
let abc = [0, 0, 0];
|
|
332
|
-
if (lattice) {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
try {
|
|
337
|
-
const lattice_transposed = math.transpose_3x3_matrix(lattice.matrix);
|
|
338
|
-
const lattice_inv = math.matrix_inverse_3x3(lattice_transposed);
|
|
339
|
-
abc = math.mat3x3_vec3_multiply(lattice_inv, xyz);
|
|
340
|
-
}
|
|
341
|
-
catch {
|
|
342
|
-
// Fallback to simplified method if matrix is singular
|
|
343
|
-
abc = [xyz[0] / lattice.a, xyz[1] / lattice.b, xyz[2] / lattice.c];
|
|
344
|
-
}
|
|
327
|
+
if (lattice && xyz_frac_to_cart && xyz_axis_lengths) {
|
|
328
|
+
abc = xyz_cart_to_frac
|
|
329
|
+
? xyz_cart_to_frac(xyz)
|
|
330
|
+
: approximate_cart_to_frac(xyz, xyz_axis_lengths);
|
|
345
331
|
// Ensure fractional coordinates are wrapped into [0, 1) for consistency
|
|
346
|
-
abc =
|
|
347
|
-
abc[0] - Math.floor(abc[0]),
|
|
348
|
-
abc[1] - Math.floor(abc[1]),
|
|
349
|
-
abc[2] - Math.floor(abc[2]),
|
|
350
|
-
];
|
|
332
|
+
abc = wrap_to_unit_cell(abc);
|
|
351
333
|
// Keep rendered atoms inside primary unit cell by recomputing xyz
|
|
352
|
-
|
|
353
|
-
const lattice_transposed = math.transpose_3x3_matrix(lattice.matrix);
|
|
354
|
-
const wrapped_xyz = math.mat3x3_vec3_multiply(lattice_transposed, abc);
|
|
334
|
+
const wrapped_xyz = xyz_frac_to_cart(abc);
|
|
355
335
|
xyz[0] = wrapped_xyz[0];
|
|
356
336
|
xyz[1] = wrapped_xyz[1];
|
|
357
337
|
xyz[2] = wrapped_xyz[2];
|
|
@@ -396,7 +376,7 @@ const parse_symmetry_expression = (expr_input) => {
|
|
|
396
376
|
tokens.push(current_token);
|
|
397
377
|
for (const token of tokens) {
|
|
398
378
|
// Check if this token is a variable term (x, y, or z with optional sign)
|
|
399
|
-
const var_match =
|
|
379
|
+
const var_match = /^([+-]?)([xyz])$/.exec(token);
|
|
400
380
|
if (var_match) {
|
|
401
381
|
const sign = var_match[1] === `-` ? -1 : 1;
|
|
402
382
|
const var_char = var_match[2];
|
|
@@ -445,9 +425,7 @@ const apply_symmetry_ops = (atom, symmetry_ops, wrap_fractional_coords) => {
|
|
|
445
425
|
return [atom];
|
|
446
426
|
const equivalent_atoms = [];
|
|
447
427
|
const seen = new Set();
|
|
448
|
-
const wrap = (coords) => (
|
|
449
|
-
? coords.map((val) => val - Math.floor(val))
|
|
450
|
-
: coords);
|
|
428
|
+
const wrap = (coords) => wrap_fractional_coords ? wrap_to_unit_cell(coords) : coords;
|
|
451
429
|
// Use 6 decimal places for deduplication to handle floating point imprecision
|
|
452
430
|
// from compound symmetry operations like x-y, -x+y which can produce small errors
|
|
453
431
|
const key = (coords) => `${coords[0].toFixed(6)},${coords[1].toFixed(6)},${coords[2].toFixed(6)}`;
|
|
@@ -456,7 +434,7 @@ const apply_symmetry_ops = (atom, symmetry_ops, wrap_fractional_coords) => {
|
|
|
456
434
|
seen.add(key(base_coords));
|
|
457
435
|
equivalent_atoms.push({ ...atom, coords: base_coords });
|
|
458
436
|
for (const operation of symmetry_ops) {
|
|
459
|
-
const operation_match =
|
|
437
|
+
const operation_match = /['"]([^'"]+)['"]/.exec(operation);
|
|
460
438
|
const expr_str = operation_match ? operation_match[1] : operation.trim();
|
|
461
439
|
const parts = expr_str.split(`,`).map((part) => part.trim());
|
|
462
440
|
if (parts.length !== 3)
|
|
@@ -465,17 +443,18 @@ const apply_symmetry_ops = (atom, symmetry_ops, wrap_fractional_coords) => {
|
|
|
465
443
|
for (let dim = 0; dim < 3; dim++) {
|
|
466
444
|
const { coefficients, translation } = parse_symmetry_expression(parts[dim]);
|
|
467
445
|
// Apply: new_coord = coeff_x * x + coeff_y * y + coeff_z * z + translation
|
|
468
|
-
new_coords[dim] =
|
|
469
|
-
coefficients[
|
|
470
|
-
|
|
471
|
-
|
|
446
|
+
new_coords[dim] =
|
|
447
|
+
coefficients[0] * atom.coords[0] +
|
|
448
|
+
coefficients[1] * atom.coords[1] +
|
|
449
|
+
coefficients[2] * atom.coords[2] +
|
|
450
|
+
translation;
|
|
472
451
|
}
|
|
473
452
|
// Wrap and deduplicate transformed coordinates
|
|
474
453
|
const wrapped = wrap(new_coords);
|
|
475
|
-
const
|
|
476
|
-
if (seen.has(
|
|
454
|
+
const cache_key = key(wrapped);
|
|
455
|
+
if (seen.has(cache_key))
|
|
477
456
|
continue;
|
|
478
|
-
seen.add(
|
|
457
|
+
seen.add(cache_key);
|
|
479
458
|
equivalent_atoms.push({
|
|
480
459
|
...atom,
|
|
481
460
|
coords: wrapped,
|
|
@@ -549,7 +528,7 @@ const parse_cif_atom_data = (raw_data, indices, coords_type) => {
|
|
|
549
528
|
const occu = occupancy >= 0 && raw_data[occupancy]
|
|
550
529
|
? parseFloat(raw_data[occupancy].split(`(`)[0]) || 1.0
|
|
551
530
|
: 1.0;
|
|
552
|
-
const element_symbol = (symbol >= 0 &&
|
|
531
|
+
const element_symbol = (symbol >= 0 && /^([A-Z][a-z]*)/.exec(raw_data[symbol])?.[1]) ||
|
|
553
532
|
raw_data[label]?.match(/([A-Z][a-z]*)/g)?.[0] ||
|
|
554
533
|
(() => {
|
|
555
534
|
throw new Error(`Could not extract element symbol from: ${raw_data.join(` `)}`);
|
|
@@ -605,9 +584,11 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
605
584
|
continue;
|
|
606
585
|
// Check if this loop contains coordinate headers
|
|
607
586
|
const indices_preview = build_cif_atom_site_header_indices(headers);
|
|
608
|
-
const has_coords = (indices_preview.x !== undefined &&
|
|
587
|
+
const has_coords = (indices_preview.x !== undefined &&
|
|
588
|
+
indices_preview.y !== undefined &&
|
|
609
589
|
indices_preview.z !== undefined) ||
|
|
610
|
-
(indices_preview.cart_x !== undefined &&
|
|
590
|
+
(indices_preview.cart_x !== undefined &&
|
|
591
|
+
indices_preview.cart_y !== undefined &&
|
|
611
592
|
indices_preview.cart_z !== undefined);
|
|
612
593
|
if (!has_coords) {
|
|
613
594
|
ii = jj - 1;
|
|
@@ -645,10 +626,12 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
645
626
|
// Parse atom data with error handling
|
|
646
627
|
const header_indices = build_cif_atom_site_header_indices(atom_headers);
|
|
647
628
|
// Determine available coordinate type
|
|
648
|
-
const coords_type = header_indices.x !== undefined &&
|
|
629
|
+
const coords_type = header_indices.x !== undefined &&
|
|
630
|
+
header_indices.y !== undefined &&
|
|
649
631
|
header_indices.z !== undefined
|
|
650
632
|
? `fract`
|
|
651
|
-
: header_indices.cart_x !== undefined &&
|
|
633
|
+
: header_indices.cart_x !== undefined &&
|
|
634
|
+
header_indices.cart_y !== undefined &&
|
|
652
635
|
header_indices.cart_z !== undefined
|
|
653
636
|
? `cart`
|
|
654
637
|
: null;
|
|
@@ -699,21 +682,15 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
699
682
|
const [alpha, beta, gamma] = angles;
|
|
700
683
|
const lattice_matrix = math.cell_to_lattice_matrix(a, b, c, alpha, beta, gamma);
|
|
701
684
|
const lattice_params = math.calc_lattice_params(lattice_matrix);
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
try {
|
|
705
|
-
lattice_inv_transposed = math.matrix_inverse_3x3(lattice_transposed);
|
|
706
|
-
}
|
|
707
|
-
catch {
|
|
708
|
-
lattice_inv_transposed = null;
|
|
709
|
-
}
|
|
685
|
+
const frac_to_cart = math.create_frac_to_cart(lattice_matrix);
|
|
686
|
+
const cart_to_frac = try_create_cart_to_frac(lattice_matrix);
|
|
710
687
|
// Create sites with coordinate conversion and symmetry operations
|
|
711
|
-
const wrap_vec3 = (v) => wrap_fractional_coords ? v
|
|
688
|
+
const wrap_vec3 = (v) => (wrap_fractional_coords ? wrap_to_unit_cell(v) : v);
|
|
712
689
|
// Apply symmetry operations to generate all equivalent positions
|
|
713
690
|
const all_sites = [];
|
|
714
691
|
// Normalize symmetry operations (trim/strip quotes) but preserve duplicates; we deduplicate positions later
|
|
715
692
|
const normalized_ops = symmetry_ops
|
|
716
|
-
.map((op) =>
|
|
693
|
+
.map((op) => /['"]([^'"]+)['"]/.exec(op)?.[1] || op.trim())
|
|
717
694
|
.map((op) => op.replace(/\s+/g, ``));
|
|
718
695
|
// Rely on symmetry operations list for all centering/translations to avoid double-counting
|
|
719
696
|
// TODO: Support conventional cells with centering by discovering centering from space group metadata
|
|
@@ -747,7 +724,7 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
747
724
|
const toks = (line.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || []).map((tok) => tok.replace(/['"]/g, ``));
|
|
748
725
|
if (toks.length > Math.max(sym_idx, num_idx)) {
|
|
749
726
|
// Normalize type symbol to bare element (e.g. 'Sn2+' -> 'Sn')
|
|
750
|
-
const match =
|
|
727
|
+
const match = /^([A-Z][a-z]*)/.exec(toks[sym_idx]);
|
|
751
728
|
const sym = match ? match[1] : toks[sym_idx];
|
|
752
729
|
const num = parseInt(toks[num_idx]);
|
|
753
730
|
if (sym && !Number.isNaN(num))
|
|
@@ -785,13 +762,9 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
785
762
|
}
|
|
786
763
|
else {
|
|
787
764
|
const xyz_base = [atom.coords[0], atom.coords[1], atom.coords[2]];
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
atom_abc = wrap_vec3(raw);
|
|
792
|
-
}
|
|
793
|
-
else
|
|
794
|
-
atom_abc = wrap_vec3([xyz_base[0] / a, xyz_base[1] / b, xyz_base[2] / c]);
|
|
765
|
+
const atom_abc = wrap_vec3(cart_to_frac
|
|
766
|
+
? cart_to_frac(xyz_base)
|
|
767
|
+
: approximate_cart_to_frac(xyz_base, [a, b, c]));
|
|
795
768
|
fractional_atom = { ...atom, coords: atom_abc, coords_type: `fract` };
|
|
796
769
|
}
|
|
797
770
|
// First apply symmetry operations in fractional space
|
|
@@ -808,7 +781,7 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
808
781
|
if (seen_site_keys.has(key))
|
|
809
782
|
continue;
|
|
810
783
|
seen_site_keys.add(key);
|
|
811
|
-
const xyz =
|
|
784
|
+
const xyz = frac_to_cart(abc);
|
|
812
785
|
all_sites.push({
|
|
813
786
|
species: [{ element, occu: equiv_atom.occupancy, oxidation_state: 0 }],
|
|
814
787
|
abc,
|
|
@@ -833,15 +806,11 @@ function convert_phonopy_cell(cell) {
|
|
|
833
806
|
// Phonopy stores lattice vectors as rows, use them directly
|
|
834
807
|
const lattice_matrix = cell.lattice;
|
|
835
808
|
// Process each atomic site
|
|
809
|
+
const phonopy_frac_to_cart = math.create_frac_to_cart(lattice_matrix);
|
|
836
810
|
for (const point of cell.points) {
|
|
837
811
|
const element = validate_element_symbol(point.symbol, sites.length);
|
|
838
|
-
const abc = [
|
|
839
|
-
|
|
840
|
-
point.coordinates[1],
|
|
841
|
-
point.coordinates[2],
|
|
842
|
-
];
|
|
843
|
-
// Convert fractional to Cartesian coordinates
|
|
844
|
-
const xyz = math.mat3x3_vec3_multiply(math.transpose_3x3_matrix(lattice_matrix), abc);
|
|
812
|
+
const abc = [point.coordinates[0], point.coordinates[1], point.coordinates[2]];
|
|
813
|
+
const xyz = phonopy_frac_to_cart(abc);
|
|
845
814
|
const properties = {
|
|
846
815
|
mass: point.mass,
|
|
847
816
|
...(point.reduced_to !== undefined && { reduced_to: point.reduced_to }),
|
|
@@ -869,7 +838,7 @@ export function parse_phonopy_yaml(content, cell_type) {
|
|
|
869
838
|
}
|
|
870
839
|
// Check if we're still in the phonon_displacements section
|
|
871
840
|
if (skip_displacements) {
|
|
872
|
-
if (
|
|
841
|
+
if (/^[a-zA-Z_]/.exec(line)) {
|
|
873
842
|
// New top-level key, stop skipping
|
|
874
843
|
skip_displacements = false;
|
|
875
844
|
}
|
|
@@ -922,7 +891,7 @@ export function parse_phonopy_yaml(content, cell_type) {
|
|
|
922
891
|
// Recursively search for a valid structure object in nested JSON
|
|
923
892
|
function find_structure_in_json(obj, visited = new WeakSet()) {
|
|
924
893
|
// Check if current object is null or undefined
|
|
925
|
-
if (obj
|
|
894
|
+
if (obj == null)
|
|
926
895
|
return null;
|
|
927
896
|
if (typeof obj !== `object`)
|
|
928
897
|
return null; // If it's not an object, skip it
|
|
@@ -939,11 +908,10 @@ function find_structure_in_json(obj, visited = new WeakSet()) {
|
|
|
939
908
|
return null;
|
|
940
909
|
}
|
|
941
910
|
// Check if this object looks like a valid structure
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
return potential_structure;
|
|
911
|
+
if (is_parsed_structure(obj))
|
|
912
|
+
return obj;
|
|
945
913
|
// Otherwise, recursively search through all properties
|
|
946
|
-
for (const value of Object.values(
|
|
914
|
+
for (const value of Object.values(obj)) {
|
|
947
915
|
const result = find_structure_in_json(value, visited);
|
|
948
916
|
if (result)
|
|
949
917
|
return result;
|
|
@@ -954,17 +922,19 @@ function find_structure_in_json(obj, visited = new WeakSet()) {
|
|
|
954
922
|
function is_parsed_structure(obj) {
|
|
955
923
|
if (!obj || typeof obj !== `object`)
|
|
956
924
|
return false;
|
|
957
|
-
const
|
|
958
|
-
|
|
959
|
-
if (!Array.isArray(
|
|
925
|
+
const parsed_obj = obj;
|
|
926
|
+
const sites = parsed_obj.sites;
|
|
927
|
+
if (!Array.isArray(sites) || sites.length === 0)
|
|
960
928
|
return false;
|
|
961
|
-
|
|
962
|
-
const first_site = record.sites[0];
|
|
929
|
+
const first_site = sites[0];
|
|
963
930
|
if (!first_site || typeof first_site !== `object`)
|
|
964
931
|
return false;
|
|
965
|
-
|
|
966
|
-
const
|
|
967
|
-
const
|
|
932
|
+
const first_site_obj = first_site;
|
|
933
|
+
const species = first_site_obj.species;
|
|
934
|
+
const abc = first_site_obj.abc;
|
|
935
|
+
const xyz = first_site_obj.xyz;
|
|
936
|
+
const has_species = Array.isArray(species) && species.length > 0;
|
|
937
|
+
const has_coords = Array.isArray(abc) || Array.isArray(xyz);
|
|
968
938
|
return has_species && has_coords;
|
|
969
939
|
}
|
|
970
940
|
// Normalize structure coordinates: wrap fractional coords to [0,1) and recompute Cartesian
|
|
@@ -1073,8 +1043,7 @@ export function parse_structure_file(content, filename) {
|
|
|
1073
1043
|
const coords = parts.slice(1, 4);
|
|
1074
1044
|
// Check if first token looks like an element symbol (not a number)
|
|
1075
1045
|
// and the next 3 tokens look like coordinates (numbers)
|
|
1076
|
-
const is_element_symbol = isNaN(parseInt(first_token)) &&
|
|
1077
|
-
first_token.length <= 3;
|
|
1046
|
+
const is_element_symbol = isNaN(parseInt(first_token)) && first_token.length <= 3;
|
|
1078
1047
|
const are_coordinates = coords.every((coord) => !isNaN(parseFloat(coord)));
|
|
1079
1048
|
if (is_element_symbol && are_coordinates) {
|
|
1080
1049
|
// First token is likely an element symbol, likely XYZ
|
|
@@ -1188,7 +1157,24 @@ export function parse_optimade_from_raw(raw) {
|
|
|
1188
1157
|
const species = species_raw;
|
|
1189
1158
|
// Optimade stores lattice vectors as rows, so use as is
|
|
1190
1159
|
const lattice_matrix = attrs.lattice_vectors;
|
|
1160
|
+
const optimade_lattice_params = lattice_matrix
|
|
1161
|
+
? math.calc_lattice_params(lattice_matrix)
|
|
1162
|
+
: null;
|
|
1191
1163
|
// Parse atomic sites
|
|
1164
|
+
const optimade_exact_cart_to_frac = lattice_matrix
|
|
1165
|
+
? try_create_cart_to_frac(lattice_matrix)
|
|
1166
|
+
: null;
|
|
1167
|
+
const optimade_cart_to_frac = lattice_matrix && optimade_lattice_params
|
|
1168
|
+
? (optimade_exact_cart_to_frac ??
|
|
1169
|
+
((xyz) => approximate_cart_to_frac(xyz, [
|
|
1170
|
+
optimade_lattice_params.a,
|
|
1171
|
+
optimade_lattice_params.b,
|
|
1172
|
+
optimade_lattice_params.c,
|
|
1173
|
+
])))
|
|
1174
|
+
: null;
|
|
1175
|
+
if (lattice_matrix && !optimade_exact_cart_to_frac) {
|
|
1176
|
+
console.warn(`Failed to create exact coordinate converter for OPTIMADE structure`);
|
|
1177
|
+
}
|
|
1192
1178
|
const sites = [];
|
|
1193
1179
|
for (let idx = 0; idx < positions.length; idx++) {
|
|
1194
1180
|
const pos = positions[idx];
|
|
@@ -1200,18 +1186,7 @@ export function parse_optimade_from_raw(raw) {
|
|
|
1200
1186
|
const element = validate_element_symbol(element_symbol, idx);
|
|
1201
1187
|
const xyz = [pos[0], pos[1], pos[2]];
|
|
1202
1188
|
// Calculate fractional coordinates if lattice is available
|
|
1203
|
-
|
|
1204
|
-
if (lattice_matrix) {
|
|
1205
|
-
try {
|
|
1206
|
-
const lattice_transposed = math.transpose_3x3_matrix(lattice_matrix);
|
|
1207
|
-
const lattice_inv = math.matrix_inverse_3x3(lattice_transposed);
|
|
1208
|
-
abc = math.mat3x3_vec3_multiply(lattice_inv, xyz);
|
|
1209
|
-
}
|
|
1210
|
-
catch {
|
|
1211
|
-
// Fallback if matrix inversion fails
|
|
1212
|
-
console.warn(`Failed to calculate fractional coordinates for OPTIMADE structure`);
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1189
|
+
const abc = optimade_cart_to_frac ? optimade_cart_to_frac(xyz) : [0, 0, 0];
|
|
1215
1190
|
const site = {
|
|
1216
1191
|
species: [{ element, occu: 1, oxidation_state: 0 }],
|
|
1217
1192
|
abc,
|
|
@@ -1227,9 +1202,8 @@ export function parse_optimade_from_raw(raw) {
|
|
|
1227
1202
|
}
|
|
1228
1203
|
// Create structure object
|
|
1229
1204
|
let lattice;
|
|
1230
|
-
if (lattice_matrix) {
|
|
1231
|
-
|
|
1232
|
-
lattice = { matrix: lattice_matrix, ...lattice_params };
|
|
1205
|
+
if (lattice_matrix && optimade_lattice_params) {
|
|
1206
|
+
lattice = { matrix: lattice_matrix, ...optimade_lattice_params };
|
|
1233
1207
|
}
|
|
1234
1208
|
const structure_result = {
|
|
1235
1209
|
sites,
|
|
@@ -1260,7 +1234,7 @@ function extract_optimade_structure_from_raw(raw) {
|
|
|
1260
1234
|
const candidate = Array.isArray(payload) ? payload[0] : payload;
|
|
1261
1235
|
return is_optimade_structure_object(candidate) ? candidate : null;
|
|
1262
1236
|
}
|
|
1263
|
-
const unwrap_data = (value) =>
|
|
1237
|
+
const unwrap_data = (value) => value && typeof value === `object` && `data` in value
|
|
1264
1238
|
? value.data
|
|
1265
1239
|
: value;
|
|
1266
1240
|
// Type guard: verify minimal OPTIMADE structure shape
|
|
@@ -1268,8 +1242,13 @@ function is_optimade_structure_object(value) {
|
|
|
1268
1242
|
if (!value || typeof value !== `object`)
|
|
1269
1243
|
return false;
|
|
1270
1244
|
const obj = value;
|
|
1271
|
-
|
|
1272
|
-
|
|
1245
|
+
const type = obj.type;
|
|
1246
|
+
const id = obj.id;
|
|
1247
|
+
const attributes = obj.attributes;
|
|
1248
|
+
return (type === `structures` &&
|
|
1249
|
+
typeof id === `string` &&
|
|
1250
|
+
typeof attributes === `object` &&
|
|
1251
|
+
attributes !== null);
|
|
1273
1252
|
}
|
|
1274
1253
|
// Convert OPTIMADE structure to Crystal format
|
|
1275
1254
|
export function optimade_to_crystal(optimade_structure) {
|
|
@@ -1287,21 +1266,15 @@ export function optimade_to_crystal(optimade_structure) {
|
|
|
1287
1266
|
const lattice_params = math.calc_lattice_params(lattice_matrix);
|
|
1288
1267
|
// Build species lookup for site properties (mass, concentration, etc.)
|
|
1289
1268
|
const species_map = new Map(species?.map((spec) => [spec.name, spec]));
|
|
1269
|
+
const crystal_cart_to_frac = try_create_cart_to_frac(lattice_matrix) ??
|
|
1270
|
+
((xyz) => approximate_cart_to_frac(xyz, [lattice_params.a, lattice_params.b, lattice_params.c]));
|
|
1290
1271
|
const sites = cartesian_site_positions.map((pos, idx) => {
|
|
1291
1272
|
const element_symbol = species_at_sites[idx];
|
|
1292
1273
|
if (!element_symbol)
|
|
1293
1274
|
throw new Error(`Missing species for site ${idx}`);
|
|
1294
1275
|
const element = validate_element_symbol(element_symbol, idx);
|
|
1295
1276
|
const xyz = [pos[0], pos[1], pos[2]];
|
|
1296
|
-
|
|
1297
|
-
try {
|
|
1298
|
-
const lattice_transposed = math.transpose_3x3_matrix(lattice_matrix);
|
|
1299
|
-
const inv_matrix = math.matrix_inverse_3x3(lattice_transposed);
|
|
1300
|
-
abc = math.mat3x3_vec3_multiply(inv_matrix, xyz);
|
|
1301
|
-
}
|
|
1302
|
-
catch {
|
|
1303
|
-
abc = [0, 0, 0];
|
|
1304
|
-
}
|
|
1277
|
+
const abc = crystal_cart_to_frac ? crystal_cart_to_frac(xyz) : [0, 0, 0];
|
|
1305
1278
|
// Extract mass/concentration from species data
|
|
1306
1279
|
const spec = species_map.get(element_symbol);
|
|
1307
1280
|
const site_props = {};
|
|
@@ -1348,8 +1321,10 @@ export function is_structure_file(filename) {
|
|
|
1348
1321
|
if (/\.(yaml|yml|xml)$/i.test(name) && STRUCT_KEYWORDS_REGEX.test(name))
|
|
1349
1322
|
return true;
|
|
1350
1323
|
// More restrictive keyword detection for JSON files
|
|
1351
|
-
if (/\.json$/i.test(name) &&
|
|
1352
|
-
|
|
1324
|
+
if (/\.json$/i.test(name) &&
|
|
1325
|
+
STRUCT_KEYWORDS_STRICT_REGEX.test(name) &&
|
|
1326
|
+
!TRAJ_KEYWORDS_REGEX.test(name) &&
|
|
1327
|
+
!CONFIG_DIRS_REGEX.test(name))
|
|
1353
1328
|
return true;
|
|
1354
1329
|
// Compressed files - check base filename recursively
|
|
1355
1330
|
if (COMPRESSION_EXTENSIONS_REGEX.test(name)) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Site } from './';
|
|
2
|
+
export declare const PARTIAL_OCCUPANCY_SLICE_GAP_RAD = 0.001;
|
|
3
|
+
export type RenderSite = {
|
|
4
|
+
site_idx: number;
|
|
5
|
+
site: Site;
|
|
6
|
+
is_image_atom: boolean;
|
|
7
|
+
source_site_indices: number[];
|
|
8
|
+
};
|
|
9
|
+
export type SliceGeometry = {
|
|
10
|
+
element: string;
|
|
11
|
+
occupancy: number;
|
|
12
|
+
start_phi: number;
|
|
13
|
+
end_phi: number;
|
|
14
|
+
phi_length: number;
|
|
15
|
+
render_start_cap: boolean;
|
|
16
|
+
render_end_cap: boolean;
|
|
17
|
+
};
|
|
18
|
+
export type CapArcConfig = {
|
|
19
|
+
start_cap_arc_start: number;
|
|
20
|
+
end_cap_arc_start: number;
|
|
21
|
+
arc_length: number;
|
|
22
|
+
};
|
|
23
|
+
export declare const PARTIAL_OCCUPANCY_CAP_ARC: CapArcConfig;
|
|
24
|
+
export declare const merge_split_partial_sites: (sites: Site[], hidden_elements?: ReadonlySet<string>) => RenderSite[];
|
|
25
|
+
export declare const compute_slice_geometry: (visible_species: Site[`species`], slice_gap_rad?: number) => SliceGeometry[];
|