matterviz 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/FilePicker.svelte +37 -20
- package/dist/Icon.svelte +2 -2
- package/dist/MillerIndexInput.svelte +60 -0
- package/dist/MillerIndexInput.svelte.d.ts +7 -0
- package/dist/app.css +38 -2
- package/dist/brillouin/BrillouinZone.svelte +20 -62
- package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneExportPane.svelte +12 -20
- package/dist/brillouin/BrillouinZoneScene.svelte +2 -2
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
- package/dist/chempot-diagram/ChemPotDiagram.svelte +192 -0
- package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +677 -0
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2688 -0
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
- package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -0
- package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
- package/dist/chempot-diagram/color.d.ts +10 -0
- package/dist/chempot-diagram/color.js +33 -0
- package/dist/chempot-diagram/compute.d.ts +38 -0
- package/dist/chempot-diagram/compute.js +650 -0
- package/dist/chempot-diagram/index.d.ts +5 -0
- package/dist/chempot-diagram/index.js +5 -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 +37 -0
- package/dist/chempot-diagram/types.d.ts +83 -0
- package/dist/chempot-diagram/types.js +27 -0
- package/dist/colors/index.d.ts +3 -1
- package/dist/colors/index.js +4 -0
- package/dist/composition/BarChart.svelte +13 -22
- package/dist/composition/BubbleChart.svelte +5 -3
- package/dist/composition/FormulaFilter.svelte +770 -90
- package/dist/composition/FormulaFilter.svelte.d.ts +37 -1
- package/dist/composition/PieChart.svelte +43 -18
- package/dist/composition/PieChart.svelte.d.ts +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -0
- package/dist/convex-hull/ConvexHull.svelte +14 -1
- package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull2D.svelte +14 -45
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +396 -134
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +93 -42
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullControls.svelte +94 -31
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +4 -2
- package/dist/convex-hull/ConvexHullStats.svelte +697 -128
- package/dist/convex-hull/ConvexHullStats.svelte.d.ts +6 -1
- package/dist/convex-hull/ConvexHullTooltip.svelte +1 -0
- package/dist/convex-hull/GasPressureControls.svelte +72 -38
- package/dist/convex-hull/GasPressureControls.svelte.d.ts +2 -1
- package/dist/convex-hull/TemperatureSlider.svelte +46 -19
- package/dist/convex-hull/TemperatureSlider.svelte.d.ts +2 -1
- package/dist/convex-hull/demo-temperature.d.ts +6 -0
- package/dist/convex-hull/demo-temperature.js +36 -0
- package/dist/convex-hull/gas-thermodynamics.js +16 -5
- package/dist/convex-hull/helpers.d.ts +7 -1
- package/dist/convex-hull/helpers.js +45 -15
- package/dist/convex-hull/index.d.ts +15 -1
- package/dist/convex-hull/index.js +1 -0
- package/dist/convex-hull/thermodynamics.d.ts +8 -21
- package/dist/convex-hull/thermodynamics.js +106 -17
- package/dist/convex-hull/types.d.ts +7 -0
- package/dist/convex-hull/types.js +11 -0
- package/dist/coordination/CoordinationBarPlot.svelte +29 -46
- package/dist/element/BohrAtom.svelte +1 -1
- package/dist/element/data.js +2 -14
- package/dist/element/data.json.gz +0 -0
- package/dist/element/index.d.ts +1 -1
- package/dist/element/index.js +1 -0
- package/dist/element/types.d.ts +1 -0
- package/dist/fermi-surface/FermiSurface.svelte +21 -65
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
- package/dist/fermi-surface/compute.js +1 -21
- package/dist/fermi-surface/marching-cubes.d.ts +2 -13
- package/dist/fermi-surface/marching-cubes.js +2 -519
- package/dist/fermi-surface/parse.js +17 -23
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +1273 -0
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +171 -0
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +31 -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 +119 -0
- package/dist/icons.js +119 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +6 -1
- package/dist/io/export.js +15 -3
- 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/types.d.ts +1 -0
- package/dist/io/url-drop.d.ts +2 -0
- package/dist/io/url-drop.js +118 -0
- package/dist/isosurface/Isosurface.svelte +231 -0
- package/dist/isosurface/Isosurface.svelte.d.ts +8 -0
- package/dist/isosurface/IsosurfaceControls.svelte +273 -0
- package/dist/isosurface/IsosurfaceControls.svelte.d.ts +9 -0
- package/dist/isosurface/index.d.ts +5 -0
- package/dist/isosurface/index.js +6 -0
- package/dist/isosurface/parse.d.ts +6 -0
- package/dist/isosurface/parse.js +548 -0
- package/dist/isosurface/slice.d.ts +11 -0
- package/dist/isosurface/slice.js +145 -0
- package/dist/isosurface/types.d.ts +55 -0
- package/dist/isosurface/types.js +178 -0
- package/dist/labels.d.ts +2 -1
- package/dist/labels.js +1 -0
- package/dist/layout/InfoTag.svelte +62 -62
- package/dist/layout/SubpageGrid.svelte +74 -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 +226 -53
- package/dist/layout/json-tree/JsonTree.svelte +425 -51
- package/dist/layout/json-tree/JsonTree.svelte.d.ts +1 -1
- package/dist/layout/json-tree/JsonValue.svelte +218 -97
- package/dist/layout/json-tree/types.d.ts +27 -2
- package/dist/layout/json-tree/utils.d.ts +14 -1
- package/dist/layout/json-tree/utils.js +254 -0
- package/dist/marching-cubes.d.ts +14 -0
- package/dist/marching-cubes.js +519 -0
- package/dist/math.d.ts +8 -0
- package/dist/math.js +374 -7
- package/dist/overlays/ContextMenu.svelte +3 -2
- package/dist/overlays/DraggablePane.svelte +163 -58
- package/dist/overlays/DraggablePane.svelte.d.ts +2 -0
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +232 -77
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -2
- package/dist/phase-diagram/PhaseDiagramControls.svelte +32 -11
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +3 -2
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +103 -0
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +102 -95
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +7 -0
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +100 -26
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +6 -3
- package/dist/phase-diagram/index.d.ts +2 -0
- package/dist/phase-diagram/index.js +2 -0
- package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
- package/dist/phase-diagram/svg-to-diagram.js +865 -0
- package/dist/phase-diagram/types.d.ts +10 -0
- package/dist/phase-diagram/utils.d.ts +7 -4
- package/dist/phase-diagram/utils.js +149 -59
- package/dist/plot/AxisLabel.svelte +26 -0
- package/dist/plot/AxisLabel.svelte.d.ts +16 -0
- package/dist/plot/BarPlot.svelte +473 -228
- package/dist/plot/BarPlot.svelte.d.ts +3 -3
- package/dist/plot/BarPlotControls.svelte +3 -2
- package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ColorBar.svelte +54 -54
- package/dist/plot/ColorBar.svelte.d.ts +1 -1
- package/dist/plot/ElementScatter.svelte +4 -3
- package/dist/plot/FillArea.svelte +4 -1
- package/dist/plot/Histogram.svelte +320 -230
- package/dist/plot/Histogram.svelte.d.ts +2 -2
- package/dist/plot/HistogramControls.svelte +29 -10
- package/dist/plot/HistogramControls.svelte.d.ts +6 -2
- package/dist/plot/InteractiveAxisLabel.svelte.d.ts +2 -2
- package/dist/plot/PlotControls.svelte +109 -27
- package/dist/plot/PlotControls.svelte.d.ts +1 -1
- package/dist/plot/PlotLegend.svelte +1 -1
- package/dist/plot/PortalSelect.svelte +2 -1
- package/dist/plot/ReferenceLine.svelte +2 -1
- package/dist/plot/ReferenceLine.svelte.d.ts +1 -0
- package/dist/plot/ReferencePlane.svelte +1 -3
- package/dist/plot/ScatterPlot.svelte +343 -209
- package/dist/plot/ScatterPlot.svelte.d.ts +3 -3
- package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3DControls.svelte +203 -250
- package/dist/plot/ScatterPlot3DScene.svelte +4 -7
- package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlotControls.svelte +95 -55
- package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ZeroLines.svelte +44 -0
- package/dist/plot/ZeroLines.svelte.d.ts +32 -0
- package/dist/plot/ZoomRect.svelte +21 -0
- package/dist/plot/ZoomRect.svelte.d.ts +8 -0
- package/dist/plot/axis-utils.d.ts +1 -1
- package/dist/plot/data-cleaning.js +1 -5
- 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 +10 -19
- package/dist/plot/layout.d.ts +7 -1
- package/dist/plot/layout.js +12 -4
- package/dist/plot/reference-line.d.ts +4 -21
- package/dist/plot/reference-line.js +7 -81
- package/dist/plot/types.d.ts +42 -17
- package/dist/plot/types.js +10 -0
- package/dist/plot/utils/label-placement.js +14 -11
- package/dist/plot/utils.d.ts +1 -0
- package/dist/plot/utils.js +14 -0
- package/dist/rdf/RdfPlot.svelte +55 -66
- package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
- package/dist/rdf/index.d.ts +1 -1
- package/dist/rdf/index.js +1 -1
- package/dist/settings.d.ts +5 -0
- package/dist/settings.js +37 -3
- package/dist/spectral/Bands.svelte +515 -143
- package/dist/spectral/Bands.svelte.d.ts +22 -2
- package/dist/spectral/helpers.d.ts +23 -1
- package/dist/spectral/helpers.js +65 -9
- package/dist/spectral/types.d.ts +2 -0
- package/dist/structure/AtomLegend.svelte +31 -10
- package/dist/structure/AtomLegend.svelte.d.ts +1 -1
- package/dist/structure/CellSelect.svelte +92 -22
- package/dist/structure/Lattice.svelte +2 -0
- package/dist/structure/Structure.svelte +716 -173
- package/dist/structure/Structure.svelte.d.ts +7 -2
- package/dist/structure/StructureControls.svelte +26 -14
- package/dist/structure/StructureControls.svelte.d.ts +5 -1
- package/dist/structure/StructureInfoPane.svelte +7 -1
- package/dist/structure/StructureScene.svelte +386 -95
- package/dist/structure/StructureScene.svelte.d.ts +15 -4
- package/dist/structure/atom-properties.d.ts +6 -2
- package/dist/structure/atom-properties.js +38 -25
- package/dist/structure/export.js +10 -7
- 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 +1 -2
- package/dist/structure/index.d.ts +7 -0
- package/dist/structure/index.js +22 -0
- package/dist/structure/parse.js +19 -16
- package/dist/structure/partial-occupancy.d.ts +25 -0
- package/dist/structure/partial-occupancy.js +102 -0
- package/dist/structure/validation.js +6 -3
- package/dist/symmetry/SymmetryStats.svelte +18 -4
- package/dist/symmetry/WyckoffTable.svelte +18 -10
- package/dist/symmetry/index.d.ts +7 -4
- package/dist/symmetry/index.js +83 -18
- package/dist/table/HeatmapTable.svelte +468 -69
- package/dist/table/HeatmapTable.svelte.d.ts +13 -1
- package/dist/table/ToggleMenu.svelte +291 -44
- package/dist/table/ToggleMenu.svelte.d.ts +4 -1
- package/dist/table/index.d.ts +3 -0
- package/dist/tooltip/index.d.ts +1 -1
- package/dist/tooltip/index.js +1 -0
- package/dist/trajectory/Trajectory.svelte +147 -145
- package/dist/trajectory/TrajectoryExportPane.svelte +13 -9
- package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +1 -1
- package/dist/trajectory/constants.d.ts +6 -0
- package/dist/trajectory/constants.js +7 -0
- package/dist/trajectory/extract.js +3 -5
- 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 +339 -0
- package/dist/trajectory/helpers.d.ts +15 -0
- package/dist/trajectory/helpers.js +187 -0
- package/dist/trajectory/index.d.ts +1 -0
- package/dist/trajectory/index.js +11 -4
- package/dist/trajectory/parse/ase.d.ts +2 -0
- package/dist/trajectory/parse/ase.js +76 -0
- package/dist/trajectory/parse/hdf5.d.ts +2 -0
- package/dist/trajectory/parse/hdf5.js +121 -0
- package/dist/trajectory/parse/index.d.ts +12 -0
- package/dist/trajectory/parse/index.js +304 -0
- package/dist/trajectory/parse/lammps.d.ts +5 -0
- package/dist/trajectory/parse/lammps.js +169 -0
- package/dist/trajectory/parse/vasp.d.ts +2 -0
- package/dist/trajectory/parse/vasp.js +65 -0
- package/dist/trajectory/parse/xyz.d.ts +2 -0
- package/dist/trajectory/parse/xyz.js +109 -0
- package/dist/trajectory/types.d.ts +11 -0
- package/dist/trajectory/types.js +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +4 -0
- package/dist/xrd/XrdPlot.svelte +6 -4
- package/dist/xrd/calc-xrd.js +0 -1
- package/package.json +33 -23
- 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
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
// Parsers for volumetric data file formats (VASP CHGCAR, Gaussian .cube)
|
|
2
|
+
import { VASP_VOLUMETRIC_REGEX } from '../constants';
|
|
3
|
+
import { ELEM_SYMBOLS } from '../labels';
|
|
4
|
+
import * as math from '../math';
|
|
5
|
+
// Bohr radius in Angstroms (for Gaussian .cube unit conversion)
|
|
6
|
+
const BOHR_TO_ANGSTROM = 0.529177249;
|
|
7
|
+
// Wrap a value to [0, 1) range for fractional coordinates
|
|
8
|
+
const wrap_frac = (val) => val - Math.floor(val);
|
|
9
|
+
// === Fast number parsing utilities ===
|
|
10
|
+
// Parse whitespace-separated numbers directly from a string, starting at `pos`.
|
|
11
|
+
// Writes into a pre-allocated Float64Array and returns { count, end_pos }.
|
|
12
|
+
// Stops at `max_count` numbers, end of string, or when encountering a line
|
|
13
|
+
// starting with a letter (e.g. "augmentation" in CHGCAR).
|
|
14
|
+
function parse_float_block(text, pos, max_count, data, data_offset = 0) {
|
|
15
|
+
let idx = data_offset;
|
|
16
|
+
const target = data_offset + max_count;
|
|
17
|
+
const len = text.length;
|
|
18
|
+
while (idx < target && pos < len) {
|
|
19
|
+
// Skip whitespace
|
|
20
|
+
let char_code = text.charCodeAt(pos);
|
|
21
|
+
while (pos < len && char_code <= 32) {
|
|
22
|
+
// After a newline, check if the next non-space char is a letter (section break)
|
|
23
|
+
if (char_code === 10 || char_code === 13) {
|
|
24
|
+
let peek = pos + 1;
|
|
25
|
+
// Skip \r\n combo
|
|
26
|
+
if (char_code === 13 && peek < len && text.charCodeAt(peek) === 10)
|
|
27
|
+
peek++;
|
|
28
|
+
// Skip leading spaces on the new line
|
|
29
|
+
while (peek < len && text.charCodeAt(peek) === 32)
|
|
30
|
+
peek++;
|
|
31
|
+
if (peek < len) {
|
|
32
|
+
const next_char = text.charCodeAt(peek);
|
|
33
|
+
// Letter a-z or A-Z signals a non-numeric line (e.g. "augmentation")
|
|
34
|
+
if ((next_char >= 65 && next_char <= 90) || (next_char >= 97 && next_char <= 122)) {
|
|
35
|
+
return { count: idx - data_offset, end_pos: pos };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
char_code = text.charCodeAt(++pos);
|
|
40
|
+
}
|
|
41
|
+
if (pos >= len)
|
|
42
|
+
break;
|
|
43
|
+
// Find end of token
|
|
44
|
+
const start = pos;
|
|
45
|
+
while (pos < len && text.charCodeAt(pos) > 32)
|
|
46
|
+
pos++;
|
|
47
|
+
// Parse number using unary + (handles scientific notation)
|
|
48
|
+
const num = +text.substring(start, pos);
|
|
49
|
+
if (!Number.isNaN(num)) {
|
|
50
|
+
data[idx++] = num;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { count: idx - data_offset, end_pos: pos };
|
|
54
|
+
}
|
|
55
|
+
// Find the character offset for line N in a string (0-indexed).
|
|
56
|
+
// Much faster than splitting the entire string into lines.
|
|
57
|
+
function find_line_offset(text, target_line) {
|
|
58
|
+
let line = 0;
|
|
59
|
+
let pos = 0;
|
|
60
|
+
while (line < target_line && pos < text.length) {
|
|
61
|
+
if (text.charCodeAt(pos) === 10)
|
|
62
|
+
line++;
|
|
63
|
+
pos++;
|
|
64
|
+
}
|
|
65
|
+
return pos;
|
|
66
|
+
}
|
|
67
|
+
// Read a single line from text at the given offset, returning the line and next offset
|
|
68
|
+
function read_line(text, pos) {
|
|
69
|
+
let end = pos;
|
|
70
|
+
while (end < text.length && text.charCodeAt(end) !== 10 && text.charCodeAt(end) !== 13)
|
|
71
|
+
end++;
|
|
72
|
+
const line = text.substring(pos, end);
|
|
73
|
+
let next = end;
|
|
74
|
+
if (next < text.length && text.charCodeAt(next) === 13)
|
|
75
|
+
next++; // skip \r
|
|
76
|
+
if (next < text.length && text.charCodeAt(next) === 10)
|
|
77
|
+
next++; // skip \n
|
|
78
|
+
return { line, next };
|
|
79
|
+
}
|
|
80
|
+
// Read N lines starting from pos, returning array of trimmed lines and final offset
|
|
81
|
+
function read_lines(text, pos, count) {
|
|
82
|
+
const result = [];
|
|
83
|
+
for (let idx = 0; idx < count; idx++) {
|
|
84
|
+
const { line, next } = read_line(text, pos);
|
|
85
|
+
result.push(line.trim());
|
|
86
|
+
pos = next;
|
|
87
|
+
}
|
|
88
|
+
return { lines: result, next: pos };
|
|
89
|
+
}
|
|
90
|
+
function build_grid({ data, nx, ny, nz, divisor = 1, data_order = `z_fastest` }) {
|
|
91
|
+
const grid = new Array(nx);
|
|
92
|
+
let min_val = Infinity;
|
|
93
|
+
let max_val = -Infinity;
|
|
94
|
+
let sum = 0;
|
|
95
|
+
const total = nx * ny * nz;
|
|
96
|
+
const data_len = Math.min(data.length, total);
|
|
97
|
+
if (data_len === 0) {
|
|
98
|
+
// Empty data: return zeroed grid with neutral data_range
|
|
99
|
+
for (let ix = 0; ix < nx; ix++) {
|
|
100
|
+
const plane = new Array(ny);
|
|
101
|
+
for (let iy = 0; iy < ny; iy++)
|
|
102
|
+
plane[iy] = new Array(nz).fill(0);
|
|
103
|
+
grid[ix] = plane;
|
|
104
|
+
}
|
|
105
|
+
return { grid, data_range: { min: 0, max: 0, abs_max: 0, mean: 0 } };
|
|
106
|
+
}
|
|
107
|
+
if (data_order === `z_fastest`) {
|
|
108
|
+
// .cube convention: z varies fastest, then y, then x.
|
|
109
|
+
const ny_nz = ny * nz;
|
|
110
|
+
for (let ix = 0; ix < nx; ix++) {
|
|
111
|
+
const plane = new Array(ny);
|
|
112
|
+
for (let iy = 0; iy < ny; iy++) {
|
|
113
|
+
const row = new Array(nz).fill(0);
|
|
114
|
+
const base = ix * ny_nz + iy * nz;
|
|
115
|
+
const row_end = Math.min(base + nz, data_len);
|
|
116
|
+
for (let flat_idx = base; flat_idx < row_end; flat_idx++) {
|
|
117
|
+
const val = data[flat_idx] / divisor;
|
|
118
|
+
row[flat_idx - base] = val;
|
|
119
|
+
if (val < min_val)
|
|
120
|
+
min_val = val;
|
|
121
|
+
if (val > max_val)
|
|
122
|
+
max_val = val;
|
|
123
|
+
sum += val;
|
|
124
|
+
}
|
|
125
|
+
plane[iy] = row;
|
|
126
|
+
}
|
|
127
|
+
grid[ix] = plane;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// VASP CHGCAR/ELFCAR/LOCPOT convention: x varies fastest, then y, then z.
|
|
132
|
+
for (let ix = 0; ix < nx; ix++) {
|
|
133
|
+
const plane = new Array(ny);
|
|
134
|
+
for (let iy = 0; iy < ny; iy++)
|
|
135
|
+
plane[iy] = new Array(nz).fill(0);
|
|
136
|
+
grid[ix] = plane;
|
|
137
|
+
}
|
|
138
|
+
let flat_idx = 0;
|
|
139
|
+
let data_exhausted = false;
|
|
140
|
+
for (let iz = 0; iz < nz; iz++) {
|
|
141
|
+
for (let iy = 0; iy < ny; iy++) {
|
|
142
|
+
for (let ix = 0; ix < nx; ix++) {
|
|
143
|
+
if (flat_idx >= data_len) {
|
|
144
|
+
data_exhausted = true;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
const val = data[flat_idx] / divisor;
|
|
148
|
+
grid[ix][iy][iz] = val;
|
|
149
|
+
if (val < min_val)
|
|
150
|
+
min_val = val;
|
|
151
|
+
if (val > max_val)
|
|
152
|
+
max_val = val;
|
|
153
|
+
sum += val;
|
|
154
|
+
flat_idx++;
|
|
155
|
+
}
|
|
156
|
+
if (data_exhausted)
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
if (data_exhausted)
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const abs_max = Math.max(Math.abs(min_val), Math.abs(max_val));
|
|
164
|
+
const data_range = { min: min_val, max: max_val, abs_max, mean: sum / data_len };
|
|
165
|
+
return { grid, data_range };
|
|
166
|
+
}
|
|
167
|
+
// === CHGCAR Parser ===
|
|
168
|
+
// Parse VASP CHGCAR/AECCAR/ELFCAR/LOCPOT file format.
|
|
169
|
+
// CHGCAR consists of a POSCAR header followed by volumetric data on a 3D grid.
|
|
170
|
+
// Spin-polarized files contain two data blocks (total charge + magnetization).
|
|
171
|
+
export function parse_chgcar(content) {
|
|
172
|
+
// Strip leading whitespace
|
|
173
|
+
let pos = 0;
|
|
174
|
+
while (pos < content.length && content.charCodeAt(pos) <= 32)
|
|
175
|
+
pos++;
|
|
176
|
+
// Parse header line by line (only the first ~20 lines, not the whole file)
|
|
177
|
+
// Line 0: comment
|
|
178
|
+
let cur = read_line(content, pos);
|
|
179
|
+
pos = cur.next;
|
|
180
|
+
// Line 1: scale factor
|
|
181
|
+
cur = read_line(content, pos);
|
|
182
|
+
const scale_factor = parseFloat(cur.line);
|
|
183
|
+
if (isNaN(scale_factor)) {
|
|
184
|
+
console.error(`Invalid scaling factor in CHGCAR`);
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
pos = cur.next;
|
|
188
|
+
// Lines 2-4: lattice vectors
|
|
189
|
+
const parse_vector = (line) => math.scale(line.trim().split(/\s+/).slice(0, 3).map(Number), scale_factor);
|
|
190
|
+
const lat_lines = read_lines(content, pos, 3);
|
|
191
|
+
const lattice = [
|
|
192
|
+
parse_vector(lat_lines.lines[0]),
|
|
193
|
+
parse_vector(lat_lines.lines[1]),
|
|
194
|
+
parse_vector(lat_lines.lines[2]),
|
|
195
|
+
];
|
|
196
|
+
pos = lat_lines.next;
|
|
197
|
+
// Lines 5+: element symbols and atom counts
|
|
198
|
+
let element_symbols = [];
|
|
199
|
+
let atom_counts = [];
|
|
200
|
+
cur = read_line(content, pos);
|
|
201
|
+
if (pos >= content.length) {
|
|
202
|
+
console.error(`CHGCAR: file ends before element/count lines`);
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
// Detect VASP 5+ format (has element symbols before counts)
|
|
206
|
+
const first_token = cur.line.trim().split(/\s+/)[0];
|
|
207
|
+
const has_element_symbols = isNaN(parseInt(first_token));
|
|
208
|
+
if (has_element_symbols) {
|
|
209
|
+
element_symbols = cur.line.trim().split(/\s+/);
|
|
210
|
+
pos = cur.next;
|
|
211
|
+
cur = read_line(content, pos);
|
|
212
|
+
if (pos >= content.length) {
|
|
213
|
+
console.error(`CHGCAR: file ends before atom counts line`);
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
atom_counts = cur.line.trim().split(/\s+/).map(Number);
|
|
217
|
+
pos = cur.next;
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
atom_counts = cur.line.trim().split(/\s+/).map(Number);
|
|
221
|
+
const fallback_elements = [`H`, `He`, `Li`, `Be`, `B`, `C`, `N`, `O`, `F`, `Ne`];
|
|
222
|
+
element_symbols = atom_counts.map((_count, idx) => fallback_elements[idx % fallback_elements.length]);
|
|
223
|
+
pos = cur.next;
|
|
224
|
+
}
|
|
225
|
+
if (pos >= content.length) {
|
|
226
|
+
console.error(`CHGCAR: file ends before coordinate mode line`);
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
// Check for selective dynamics line
|
|
230
|
+
cur = read_line(content, pos);
|
|
231
|
+
if (cur.line.trim().toUpperCase().startsWith(`S`)) {
|
|
232
|
+
pos = cur.next; // skip selective dynamics line
|
|
233
|
+
cur = read_line(content, pos);
|
|
234
|
+
}
|
|
235
|
+
if (pos >= content.length) {
|
|
236
|
+
console.error(`CHGCAR: file ends before coordinate mode line`);
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
// Coordinate mode line
|
|
240
|
+
const is_direct = cur.line.trim().toUpperCase().startsWith(`D`);
|
|
241
|
+
pos = cur.next;
|
|
242
|
+
// Parse atomic positions
|
|
243
|
+
const lattice_transposed = math.transpose_3x3_matrix(lattice);
|
|
244
|
+
const lattice_inv = math.matrix_inverse_3x3(lattice_transposed);
|
|
245
|
+
const sites = [];
|
|
246
|
+
let atom_idx = 0;
|
|
247
|
+
for (let elem_idx = 0; elem_idx < element_symbols.length; elem_idx++) {
|
|
248
|
+
const symbol = element_symbols[elem_idx].split(/[_/]/)[0];
|
|
249
|
+
const element = (ELEM_SYMBOLS.includes(symbol) ? symbol : `H`);
|
|
250
|
+
const count = atom_counts[elem_idx];
|
|
251
|
+
for (let count_idx = 0; count_idx < count; count_idx++) {
|
|
252
|
+
if (pos >= content.length) {
|
|
253
|
+
console.error(`CHGCAR: file ends before all atom coordinates are read`);
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
cur = read_line(content, pos);
|
|
257
|
+
const coords = cur.line.trim().split(/\s+/).slice(0, 3).map(Number);
|
|
258
|
+
pos = cur.next;
|
|
259
|
+
let abc;
|
|
260
|
+
let xyz;
|
|
261
|
+
if (is_direct) {
|
|
262
|
+
abc = [wrap_frac(coords[0]), wrap_frac(coords[1]), wrap_frac(coords[2])];
|
|
263
|
+
xyz = math.mat3x3_vec3_multiply(lattice_transposed, abc);
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
xyz = math.scale(coords, scale_factor);
|
|
267
|
+
const raw = math.mat3x3_vec3_multiply(lattice_inv, xyz);
|
|
268
|
+
abc = [wrap_frac(raw[0]), wrap_frac(raw[1]), wrap_frac(raw[2])];
|
|
269
|
+
}
|
|
270
|
+
sites.push({
|
|
271
|
+
species: [{ element, occu: 1, oxidation_state: 0 }],
|
|
272
|
+
abc,
|
|
273
|
+
xyz,
|
|
274
|
+
label: `${element}${atom_idx + count_idx + 1}`,
|
|
275
|
+
properties: {},
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
atom_idx += count;
|
|
279
|
+
}
|
|
280
|
+
// Build the structure (volumetric files are always periodic)
|
|
281
|
+
const lattice_params = math.calc_lattice_params(lattice);
|
|
282
|
+
const structure = {
|
|
283
|
+
sites,
|
|
284
|
+
lattice: { matrix: lattice, pbc: [true, true, true], ...lattice_params },
|
|
285
|
+
};
|
|
286
|
+
// Parse volumetric data blocks
|
|
287
|
+
const volumes = [];
|
|
288
|
+
const volume_labels = [`charge density`, `magnetization density`];
|
|
289
|
+
for (let vol_idx = 0; vol_idx < 2; vol_idx++) {
|
|
290
|
+
// Skip blank lines
|
|
291
|
+
while (pos < content.length) {
|
|
292
|
+
cur = read_line(content, pos);
|
|
293
|
+
if (cur.line.trim() !== ``)
|
|
294
|
+
break;
|
|
295
|
+
pos = cur.next;
|
|
296
|
+
}
|
|
297
|
+
if (pos >= content.length)
|
|
298
|
+
break;
|
|
299
|
+
// Parse grid dimensions: NGX NGY NGZ
|
|
300
|
+
cur = read_line(content, pos);
|
|
301
|
+
const grid_tokens = cur.line.trim().split(/\s+/).map(Number);
|
|
302
|
+
if (grid_tokens.length < 3 || grid_tokens.some(isNaN))
|
|
303
|
+
break;
|
|
304
|
+
const [ngx, ngy, ngz] = grid_tokens;
|
|
305
|
+
pos = cur.next;
|
|
306
|
+
// Fast-parse volumetric data directly from the string
|
|
307
|
+
const total_points = ngx * ngy * ngz;
|
|
308
|
+
const data = new Float64Array(total_points);
|
|
309
|
+
const { count: parsed_count, end_pos } = parse_float_block(content, pos, total_points, data);
|
|
310
|
+
pos = end_pos;
|
|
311
|
+
if (parsed_count < total_points) {
|
|
312
|
+
console.warn(`CHGCAR volume ${vol_idx}: expected ${total_points} values, got ${parsed_count}`);
|
|
313
|
+
if (parsed_count === 0)
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
// CHGCAR stores rho * V_cell, so normalize by dividing by cell volume.
|
|
317
|
+
// Use Math.abs to guard against negative determinant (left-handed lattice).
|
|
318
|
+
const cell_volume = Math.abs(lattice_params.volume);
|
|
319
|
+
const divisor = cell_volume > 1e-30 ? cell_volume : 1;
|
|
320
|
+
const { grid, data_range } = build_grid({
|
|
321
|
+
data: data.subarray(0, parsed_count),
|
|
322
|
+
nx: ngx,
|
|
323
|
+
ny: ngy,
|
|
324
|
+
nz: ngz,
|
|
325
|
+
divisor,
|
|
326
|
+
data_order: `x_fastest`,
|
|
327
|
+
});
|
|
328
|
+
volumes.push({
|
|
329
|
+
grid,
|
|
330
|
+
grid_dims: [ngx, ngy, ngz],
|
|
331
|
+
lattice,
|
|
332
|
+
origin: [0, 0, 0],
|
|
333
|
+
data_range,
|
|
334
|
+
data_order: `x_fastest`,
|
|
335
|
+
periodic: true, // VASP grids span [0,1) with N points, wrapping at boundaries
|
|
336
|
+
label: volume_labels[vol_idx],
|
|
337
|
+
});
|
|
338
|
+
// Skip augmentation occupancies and any remaining non-numeric lines
|
|
339
|
+
while (pos < content.length) {
|
|
340
|
+
cur = read_line(content, pos);
|
|
341
|
+
const trimmed = cur.line.trim();
|
|
342
|
+
if (trimmed === `` || /^\d+\s+\d+\s+\d+$/.test(trimmed))
|
|
343
|
+
break;
|
|
344
|
+
pos = cur.next;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (volumes.length === 0) {
|
|
348
|
+
console.error(`No volumetric data found in CHGCAR`);
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
return { structure, volumes };
|
|
352
|
+
}
|
|
353
|
+
// === Gaussian .cube Parser ===
|
|
354
|
+
// Parse Gaussian .cube file format.
|
|
355
|
+
// Contains atomic structure and volumetric data in a single file.
|
|
356
|
+
// Units: if grid dimensions are positive, coordinates are in Bohr; if negative, in Angstrom.
|
|
357
|
+
export function parse_cube(content, options = {}) {
|
|
358
|
+
// Quick line count check: need at least 7 lines (2 title + 1 header + 3 voxel + 1 atom)
|
|
359
|
+
let line_count = 0;
|
|
360
|
+
for (let idx = 0; idx < content.length && line_count < 7; idx++) {
|
|
361
|
+
if (content.charCodeAt(idx) === 10)
|
|
362
|
+
line_count++;
|
|
363
|
+
}
|
|
364
|
+
if (line_count < 6) {
|
|
365
|
+
console.error(`.cube file too short`);
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
// Parse header (first 6 lines + atom lines)
|
|
369
|
+
let pos = 0;
|
|
370
|
+
const header = read_lines(content, pos, 6);
|
|
371
|
+
pos = header.next;
|
|
372
|
+
// Line 2: n_atoms, origin_x, origin_y, origin_z
|
|
373
|
+
// (negative n_atoms indicates orbital data with extra header line)
|
|
374
|
+
const line2 = header.lines[2].split(/\s+/).map(Number);
|
|
375
|
+
if (line2.length < 4 || line2.some(isNaN)) {
|
|
376
|
+
console.error(`.cube header line 3 malformed: expected 4 numbers`);
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
const n_atoms = Math.abs(line2[0]);
|
|
380
|
+
const has_orbital_header = line2[0] < 0;
|
|
381
|
+
const raw_origin = [line2[1], line2[2], line2[3]];
|
|
382
|
+
// Lines 3-5: grid dimensions and voxel vectors
|
|
383
|
+
// Positive N means coordinates in Bohr, negative N means Angstrom
|
|
384
|
+
const voxel_lines = [
|
|
385
|
+
header.lines[3].split(/\s+/).map(Number),
|
|
386
|
+
header.lines[4].split(/\s+/).map(Number),
|
|
387
|
+
header.lines[5].split(/\s+/).map(Number),
|
|
388
|
+
];
|
|
389
|
+
if (voxel_lines.some((line) => line.length < 4 || line.some(isNaN))) {
|
|
390
|
+
console.error(`.cube voxel lines malformed: expected 4 numbers per line`);
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
const n_grid = [
|
|
394
|
+
Math.abs(voxel_lines[0][0]),
|
|
395
|
+
Math.abs(voxel_lines[1][0]),
|
|
396
|
+
Math.abs(voxel_lines[2][0]),
|
|
397
|
+
];
|
|
398
|
+
// Per Gaussian .cube convention, the sign of the first axis N determines units
|
|
399
|
+
const is_bohr = voxel_lines[0][0] > 0;
|
|
400
|
+
const unit_scale = is_bohr ? BOHR_TO_ANGSTROM : 1.0;
|
|
401
|
+
// Voxel vectors (convert to Angstrom if needed)
|
|
402
|
+
const [voxel_a, voxel_b, voxel_c] = voxel_lines.map((line) => math.scale(line.slice(1, 4), unit_scale));
|
|
403
|
+
// Lattice vectors = grid_dim * voxel_vector
|
|
404
|
+
const lattice = [
|
|
405
|
+
math.scale(voxel_a, n_grid[0]),
|
|
406
|
+
math.scale(voxel_b, n_grid[1]),
|
|
407
|
+
math.scale(voxel_c, n_grid[2]),
|
|
408
|
+
];
|
|
409
|
+
const origin = math.scale(raw_origin, unit_scale);
|
|
410
|
+
// Periodicity: use explicit override if provided, else heuristic based on origin.
|
|
411
|
+
// Molecular .cube files have a non-zero origin (bounding box offset); periodic
|
|
412
|
+
// systems (QE, CP2K) have origin at (0,0,0). Pass { periodic: true/false } to
|
|
413
|
+
// override when the heuristic is wrong (e.g. molecule centered at origin).
|
|
414
|
+
const is_periodic = options.periodic ?? Math.hypot(...origin) < 1e-6;
|
|
415
|
+
// Parse atomic positions
|
|
416
|
+
const sites = [];
|
|
417
|
+
const lattice_transposed = math.transpose_3x3_matrix(lattice);
|
|
418
|
+
let lattice_inv;
|
|
419
|
+
try {
|
|
420
|
+
lattice_inv = math.matrix_inverse_3x3(lattice_transposed);
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
// Non-periodic system (molecule), use identity
|
|
424
|
+
lattice_inv = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
|
|
425
|
+
}
|
|
426
|
+
for (let atom_idx = 0; atom_idx < n_atoms; atom_idx++) {
|
|
427
|
+
const cur = read_line(content, pos);
|
|
428
|
+
const atom_line = cur.line.trim().split(/\s+/).map(Number);
|
|
429
|
+
pos = cur.next;
|
|
430
|
+
// Validate: need atomic_number, charge, x, y, z (5 tokens, indices 2-4 finite)
|
|
431
|
+
if (atom_line.length < 5 || !isFinite(atom_line[2]) || !isFinite(atom_line[3]) ||
|
|
432
|
+
!isFinite(atom_line[4])) {
|
|
433
|
+
console.warn(`.cube atom ${atom_idx}: malformed line "${cur.line.trim()}", skipping`);
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
// atom_line[1] is the charge (often 0)
|
|
437
|
+
const raw_xyz = math.scale([atom_line[2], atom_line[3], atom_line[4]], unit_scale);
|
|
438
|
+
// Convert Cartesian to fractional, accounting for origin offset.
|
|
439
|
+
// Store lattice-frame xyz (shifted) so abc and xyz stay consistent.
|
|
440
|
+
const xyz = math.subtract(raw_xyz, origin);
|
|
441
|
+
const abc = math.mat3x3_vec3_multiply(lattice_inv, xyz);
|
|
442
|
+
const element = atomic_number_to_symbol(atom_line[0]);
|
|
443
|
+
sites.push({
|
|
444
|
+
species: [{ element, occu: 1, oxidation_state: 0 }],
|
|
445
|
+
abc,
|
|
446
|
+
xyz,
|
|
447
|
+
label: `${element}${atom_idx + 1}`,
|
|
448
|
+
properties: {},
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
// Build structure
|
|
452
|
+
const lattice_params = math.calc_lattice_params(lattice);
|
|
453
|
+
const structure = {
|
|
454
|
+
sites,
|
|
455
|
+
lattice: {
|
|
456
|
+
matrix: lattice,
|
|
457
|
+
pbc: [is_periodic, is_periodic, is_periodic],
|
|
458
|
+
...lattice_params,
|
|
459
|
+
},
|
|
460
|
+
};
|
|
461
|
+
// Skip orbital header line if present
|
|
462
|
+
if (has_orbital_header && pos < content.length) {
|
|
463
|
+
const cur = read_line(content, pos);
|
|
464
|
+
pos = cur.next;
|
|
465
|
+
}
|
|
466
|
+
// Fast-parse volumetric data directly from the string
|
|
467
|
+
const total_points = n_grid[0] * n_grid[1] * n_grid[2];
|
|
468
|
+
const data = new Float64Array(total_points);
|
|
469
|
+
const { count: parsed_count } = parse_float_block(content, pos, total_points, data);
|
|
470
|
+
if (parsed_count < total_points) {
|
|
471
|
+
console.warn(`.cube: expected ${total_points} data values, got ${parsed_count}`);
|
|
472
|
+
if (parsed_count === 0) {
|
|
473
|
+
console.error(`No volumetric data found in .cube file`);
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const { grid, data_range } = build_grid({
|
|
478
|
+
data: data.subarray(0, parsed_count),
|
|
479
|
+
nx: n_grid[0],
|
|
480
|
+
ny: n_grid[1],
|
|
481
|
+
nz: n_grid[2],
|
|
482
|
+
data_order: `z_fastest`,
|
|
483
|
+
});
|
|
484
|
+
const volumes = [{
|
|
485
|
+
grid,
|
|
486
|
+
grid_dims: n_grid,
|
|
487
|
+
lattice,
|
|
488
|
+
origin,
|
|
489
|
+
data_range,
|
|
490
|
+
data_order: `z_fastest`,
|
|
491
|
+
periodic: is_periodic, // periodic systems wrap; molecular .cube files include both endpoints
|
|
492
|
+
label: `volumetric data`,
|
|
493
|
+
}];
|
|
494
|
+
return { structure, volumes };
|
|
495
|
+
}
|
|
496
|
+
// Convert atomic number to element symbol using ELEM_SYMBOLS (1-indexed: H=1, He=2, ...)
|
|
497
|
+
function atomic_number_to_symbol(atomic_number) {
|
|
498
|
+
// ELEM_SYMBOLS is 0-indexed (H at index 0), atomic numbers are 1-indexed
|
|
499
|
+
const idx = atomic_number - 1;
|
|
500
|
+
return (idx >= 0 && idx < ELEM_SYMBOLS.length
|
|
501
|
+
? ELEM_SYMBOLS[idx]
|
|
502
|
+
: `H`);
|
|
503
|
+
}
|
|
504
|
+
// Auto-detect and parse volumetric file format based on filename and content
|
|
505
|
+
export function parse_volumetric_file(content, filename) {
|
|
506
|
+
// Strip compression suffixes so "CHGCAR.gz" and "molecule.cube.bz2" match correctly
|
|
507
|
+
const lower_name = (filename ?? ``).toLowerCase().replace(/\.(gz|bz2|xz|zst)$/, ``);
|
|
508
|
+
// Extension-based detection
|
|
509
|
+
if (lower_name.endsWith(`.cube`))
|
|
510
|
+
return parse_cube(content);
|
|
511
|
+
// VASP volumetric file detection by filename
|
|
512
|
+
if (VASP_VOLUMETRIC_REGEX.test(lower_name))
|
|
513
|
+
return parse_chgcar(content);
|
|
514
|
+
// Content-based detection (only parse first few lines, not the whole file)
|
|
515
|
+
// Find enough lines for detection without splitting the entire string
|
|
516
|
+
const detection_end = find_line_offset(content, 10);
|
|
517
|
+
const detection_text = content.substring(0, detection_end);
|
|
518
|
+
const lines = detection_text.split(/\r?\n/);
|
|
519
|
+
// .cube detection: line 3 has 4 numbers (n_atoms + origin), line 4 has 4 numbers (grid dim + voxel)
|
|
520
|
+
if (lines.length > 4) {
|
|
521
|
+
const line2_tokens = lines[2].trim().split(/\s+/);
|
|
522
|
+
const line3_tokens = lines[3].trim().split(/\s+/);
|
|
523
|
+
if (line2_tokens.length === 4 &&
|
|
524
|
+
line3_tokens.length === 4 &&
|
|
525
|
+
line2_tokens.every((tok) => !isNaN(Number(tok))) &&
|
|
526
|
+
line3_tokens.every((tok) => !isNaN(Number(tok)))) {
|
|
527
|
+
return parse_cube(content);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
// CHGCAR detection: requires POSCAR-like header (scale factor on line 2) AND
|
|
531
|
+
// a grid dimensions line (3 integers) somewhere after the header. This distinguishes
|
|
532
|
+
// CHGCAR from plain POSCAR/CONTCAR files which share the same header format.
|
|
533
|
+
if (lines.length > 2 && !isNaN(parseFloat(lines[1].trim()))) {
|
|
534
|
+
// Scan for grid dimensions line (3 integers) starting from ~line 7
|
|
535
|
+
let scan_pos = find_line_offset(content, 7);
|
|
536
|
+
// Only scan a limited window, not the entire file
|
|
537
|
+
// Scan enough to cover large atom blocks (~100 chars/atom × ~200 atoms max)
|
|
538
|
+
const scan_end = Math.min(content.length, scan_pos + 25000);
|
|
539
|
+
while (scan_pos < scan_end) {
|
|
540
|
+
const { line, next } = read_line(content, scan_pos);
|
|
541
|
+
if (/^\s*\d+\s+\d+\s+\d+\s*$/.test(line)) {
|
|
542
|
+
return parse_chgcar(content);
|
|
543
|
+
}
|
|
544
|
+
scan_pos = next;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Vec3 } from '../math';
|
|
2
|
+
import type { VolumetricData } from './types';
|
|
3
|
+
export interface SliceResult {
|
|
4
|
+
data: Float64Array;
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
min: number;
|
|
8
|
+
max: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function trilinear_interpolate(grid: number[][][], fx: number, fy: number, fz: number, periodic: boolean): number;
|
|
11
|
+
export declare function sample_hkl_slice(volume: VolumetricData, miller_indices: Vec3, distance: number, n_points?: number): SliceResult | null;
|