matterviz 0.4.2 → 0.4.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/FilePicker.svelte +40 -50
- package/dist/api/optimade.js +10 -7
- package/dist/app.css +15 -0
- package/dist/brillouin/BrillouinZone.svelte +18 -9
- package/dist/brillouin/BrillouinZoneScene.svelte +20 -29
- package/dist/brillouin/compute.js +10 -16
- package/dist/brillouin/geometry.js +2 -2
- package/dist/chempot-diagram/ChemPotDiagram.svelte +18 -15
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +17 -113
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +104 -264
- package/dist/chempot-diagram/async-compute.svelte.js +12 -13
- package/dist/chempot-diagram/color.d.ts +21 -1
- package/dist/chempot-diagram/color.js +70 -1
- package/dist/chempot-diagram/compute.d.ts +7 -0
- package/dist/chempot-diagram/compute.js +85 -26
- package/dist/chempot-diagram/export.js +1 -6
- package/dist/chempot-diagram/temperature.js +9 -10
- package/dist/composition/BarChart.svelte +7 -9
- package/dist/composition/Composition.svelte +3 -8
- package/dist/composition/Formula.svelte +2 -2
- package/dist/composition/FormulaFilter.svelte +27 -93
- package/dist/composition/format.js +6 -8
- package/dist/composition/parse.js +50 -105
- package/dist/constants.d.ts +0 -1
- package/dist/constants.js +6 -17
- package/dist/convex-hull/ConvexHull2D.svelte +0 -2
- package/dist/convex-hull/ConvexHull3D.svelte +19 -47
- package/dist/convex-hull/ConvexHull4D.svelte +65 -95
- package/dist/convex-hull/StructurePopup.svelte +1 -1
- package/dist/convex-hull/gas-thermodynamics.js +21 -53
- package/dist/convex-hull/helpers.js +1 -7
- package/dist/convex-hull/thermodynamics.js +19 -27
- package/dist/coordination/CoordinationBarPlot.svelte +12 -21
- package/dist/element/BohrAtom.svelte +0 -1
- package/dist/element/ElementTile.svelte +26 -44
- package/dist/element/data.d.ts +2 -0
- package/dist/element/data.js +5 -1
- package/dist/element/index.d.ts +1 -1
- package/dist/element/index.js +1 -1
- package/dist/fermi-surface/FermiSurface.svelte +22 -23
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +2 -6
- package/dist/fermi-surface/FermiSurfaceScene.svelte +32 -77
- package/dist/fermi-surface/compute.js +22 -55
- package/dist/fermi-surface/parse.js +38 -70
- package/dist/file-viewer/JsonBrowser.svelte +1043 -0
- package/dist/file-viewer/JsonBrowser.svelte.d.ts +9 -0
- package/dist/file-viewer/PlotPanel.svelte +350 -0
- package/dist/file-viewer/PlotPanel.svelte.d.ts +9 -0
- package/dist/file-viewer/TrajectoryWithDos.svelte +30 -0
- package/dist/file-viewer/TrajectoryWithDos.svelte.d.ts +10 -0
- package/dist/file-viewer/detect.d.ts +11 -0
- package/dist/file-viewer/detect.js +381 -0
- package/dist/file-viewer/eligibility.d.ts +4 -0
- package/dist/file-viewer/eligibility.js +53 -0
- package/dist/file-viewer/host-protocol.d.ts +50 -0
- package/dist/file-viewer/host-transfer.d.ts +25 -0
- package/dist/file-viewer/host-transfer.js +42 -0
- package/dist/file-viewer/index.d.ts +5 -0
- package/dist/file-viewer/index.js +5 -0
- package/dist/file-viewer/main.d.ts +44 -0
- package/dist/file-viewer/main.js +535 -0
- package/dist/file-viewer/parse.d.ts +9 -0
- package/dist/file-viewer/parse.js +170 -0
- package/dist/file-viewer/plot-utils.d.ts +24 -0
- package/dist/file-viewer/plot-utils.js +191 -0
- package/dist/file-viewer/types.d.ts +4 -0
- package/dist/file-viewer/types.js +5 -0
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +57 -84
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/io/ExportPane.svelte +1 -1
- package/dist/io/decompress.d.ts +2 -0
- package/dist/io/decompress.js +3 -2
- package/dist/io/export.d.ts +7 -4
- package/dist/io/export.js +70 -18
- package/dist/io/file-drop.js +60 -35
- package/dist/io/url-drop.d.ts +2 -0
- package/dist/io/url-drop.js +28 -16
- package/dist/isosurface/Isosurface.svelte +687 -196
- package/dist/isosurface/Isosurface.svelte.d.ts +7 -1
- package/dist/isosurface/IsosurfaceControls.svelte +564 -85
- package/dist/isosurface/IsosurfaceControls.svelte.d.ts +1 -1
- package/dist/isosurface/VolumeSlice.svelte +224 -0
- package/dist/isosurface/VolumeSlice.svelte.d.ts +28 -0
- package/dist/isosurface/coloring.d.ts +20 -0
- package/dist/isosurface/coloring.js +118 -0
- package/dist/isosurface/geometry-worker-types.d.ts +38 -0
- package/dist/isosurface/geometry.worker.d.ts +1 -0
- package/dist/isosurface/geometry.worker.js +59 -0
- package/dist/isosurface/grid.d.ts +10 -0
- package/dist/isosurface/grid.js +31 -0
- package/dist/isosurface/index.d.ts +4 -0
- package/dist/isosurface/index.js +4 -0
- package/dist/isosurface/profile.d.ts +12 -0
- package/dist/isosurface/profile.js +19 -0
- package/dist/isosurface/sampling.d.ts +34 -0
- package/dist/isosurface/sampling.js +408 -0
- package/dist/isosurface/slice-rendering.d.ts +13 -0
- package/dist/isosurface/slice-rendering.js +79 -0
- package/dist/isosurface/slice.d.ts +22 -2
- package/dist/isosurface/slice.js +200 -116
- package/dist/isosurface/types.d.ts +24 -1
- package/dist/isosurface/types.js +120 -8
- package/dist/json-path.d.ts +4 -0
- package/dist/json-path.js +89 -0
- package/dist/labels.js +1 -1
- package/dist/layout/InfoCard.svelte +1 -3
- package/dist/layout/SettingsSection.svelte +52 -61
- package/dist/layout/SettingsSection.svelte.d.ts +6 -7
- package/dist/layout/ViewerChrome.svelte +7 -1
- package/dist/layout/json-tree/JsonNode.svelte +14 -48
- package/dist/layout/json-tree/JsonTree.svelte +48 -95
- package/dist/layout/json-tree/JsonValue.svelte +3 -18
- package/dist/layout/json-tree/utils.d.ts +0 -3
- package/dist/layout/json-tree/utils.js +50 -179
- package/dist/marching-cubes.d.ts +8 -1
- package/dist/marching-cubes.js +115 -48
- package/dist/math.d.ts +3 -2
- package/dist/math.js +29 -38
- package/dist/overlays/ContextMenu.svelte +15 -32
- package/dist/overlays/DraggablePane.svelte +39 -58
- package/dist/overlays/DraggablePane.svelte.d.ts +1 -1
- package/dist/overlays/GlassChip.svelte +3 -1
- package/dist/periodic-table/PeriodicTableControls.svelte +26 -52
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +18 -34
- package/dist/phase-diagram/build-diagram.js +1 -6
- package/dist/phase-diagram/svg-to-diagram.js +9 -14
- package/dist/plot/bar/BarPlot.svelte +38 -42
- package/dist/plot/bar/SpacegroupBarPlot.svelte +18 -42
- package/dist/plot/box/BoxPlot.svelte +27 -23
- package/dist/plot/core/auto-place.d.ts +1 -1
- package/dist/plot/core/auto-place.js +1 -1
- package/dist/plot/core/data-transform.d.ts +1 -2
- package/dist/plot/core/data-transform.js +0 -10
- package/dist/plot/core/layout.d.ts +12 -5
- package/dist/plot/core/layout.js +37 -27
- package/dist/plot/core/pan-zoom.svelte.d.ts +2 -4
- package/dist/plot/core/pan-zoom.svelte.js +34 -17
- package/dist/plot/core/placed-tween.svelte.d.ts +3 -0
- package/dist/plot/core/placed-tween.svelte.js +68 -21
- package/dist/plot/core/svg.js +0 -1
- package/dist/plot/core/utils/hierarchy-chart.d.ts +1 -0
- package/dist/plot/core/utils/hierarchy-chart.js +12 -0
- package/dist/plot/core/utils/hierarchy-labels.js +3 -2
- package/dist/plot/histogram/Histogram.svelte +21 -26
- package/dist/plot/sankey/sankey.js +13 -9
- package/dist/plot/scatter/BinnedScatterPlot.svelte +59 -43
- package/dist/plot/scatter/ScatterPlot.svelte +67 -52
- package/dist/plot/scatter/adaptive-density.d.ts +1 -1
- package/dist/plot/scatter/adaptive-density.js +36 -20
- package/dist/plot/sunburst/Sunburst.svelte +20 -20
- package/dist/plot/treemap/Treemap.svelte +175 -60
- package/dist/plot/treemap/Treemap.svelte.d.ts +6 -0
- package/dist/plot/treemap/index.d.ts +1 -0
- package/dist/plot/treemap/labels.d.ts +38 -0
- package/dist/plot/treemap/labels.js +80 -0
- package/dist/plot/treemap/treemap.d.ts +4 -2
- package/dist/plot/treemap/treemap.js +5 -2
- package/dist/rdf/RdfPlot.svelte +17 -35
- package/dist/rdf/calc-rdf.js +59 -44
- package/dist/rdf/index.d.ts +0 -1
- package/dist/sanitize.js +84 -26
- package/dist/scene/SceneCamera.svelte +1 -1
- package/dist/scene/SceneCamera.svelte.d.ts +1 -1
- package/dist/scene/index.d.ts +1 -2
- package/dist/scene/index.js +1 -1
- package/dist/scene/{props.js → props.svelte.js} +15 -1
- package/dist/settings.js +159 -482
- package/dist/spectral/Bands.svelte +151 -158
- package/dist/spectral/BrillouinBandsDos.svelte +4 -19
- package/dist/spectral/Dos.svelte +26 -24
- package/dist/spectral/helpers.js +40 -105
- package/dist/structure/AtomLegend.svelte +8 -13
- package/dist/structure/AtomLegend.svelte.d.ts +2 -3
- package/dist/structure/CellSelect.svelte +35 -10
- package/dist/structure/Structure.svelte +177 -99
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureCarousel.svelte +10 -3
- package/dist/structure/StructureControls.svelte +8 -26
- package/dist/structure/StructureControls.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +65 -76
- package/dist/structure/StructureScene.svelte +75 -109
- package/dist/structure/StructureScene.svelte.d.ts +3 -1
- package/dist/structure/StructureViewport.svelte +120 -55
- package/dist/structure/StructureViewport.svelte.d.ts +3 -1
- package/dist/structure/atom-properties.d.ts +1 -1
- package/dist/structure/atom-properties.js +4 -3
- package/dist/structure/bond-order-perception.js +3 -8
- package/dist/structure/bonding.d.ts +0 -1
- package/dist/structure/bonding.js +16 -23
- package/dist/structure/export.js +50 -74
- package/dist/structure/format-detect.js +5 -7
- package/dist/structure/index.js +6 -12
- package/dist/structure/parse.js +38 -100
- package/dist/structure/partial-occupancy.js +7 -4
- package/dist/structure/pbc.js +8 -10
- package/dist/structure/polyhedra.js +5 -4
- package/dist/structure/prop-groups.d.ts +85 -0
- package/dist/structure/prop-groups.js +18 -0
- package/dist/symmetry/SymmetryStats.svelte +0 -4
- package/dist/symmetry/WyckoffTable.svelte +5 -4
- package/dist/symmetry/cell-transform.js +5 -14
- package/dist/symmetry/index.js +6 -9
- package/dist/table/HeatmapTable.svelte +61 -103
- package/dist/table/ToggleMenu.svelte +166 -44
- package/dist/table/ToggleMenu.svelte.d.ts +1 -1
- package/dist/theme/embedded.d.ts +18 -0
- package/dist/theme/embedded.js +206 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.js +1 -2
- package/dist/theme/themes.mjs +31 -60
- package/dist/tooltip/TooltipContent.svelte +1 -1
- package/dist/tooltip/TooltipContent.svelte.d.ts +1 -1
- package/dist/tooltip/index.d.ts +8 -1
- package/dist/trajectory/Trajectory.svelte +140 -120
- package/dist/trajectory/TrajectoryError.svelte +1 -66
- package/dist/trajectory/TrajectoryExportPane.svelte +0 -1
- package/dist/trajectory/TrajectoryInfoPane.svelte +55 -80
- package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +2 -1
- package/dist/trajectory/extract.js +19 -29
- package/dist/trajectory/format-detect.d.ts +2 -0
- package/dist/trajectory/format-detect.js +7 -5
- package/dist/trajectory/frame-reader.js +3 -4
- package/dist/trajectory/helpers.js +16 -49
- package/dist/trajectory/index.js +2 -8
- package/dist/trajectory/parse/ase.js +1 -1
- package/dist/trajectory/parse/index.d.ts +4 -3
- package/dist/trajectory/parse/index.js +12 -17
- package/dist/trajectory/parse/vaspout-h5.d.ts +0 -1
- package/dist/trajectory/parse/vaspout-h5.js +1 -4
- package/dist/trajectory/parse/xyz.js +48 -2
- package/dist/trajectory/plotting.js +14 -37
- package/dist/xrd/XrdPlot.svelte +43 -68
- package/dist/xrd/calc-xrd.js +22 -58
- package/dist/xrd/parse.js +13 -22
- package/package.json +40 -8
- package/readme.md +6 -5
- package/dist/scene/types.d.ts +0 -26
- package/dist/tooltip/types.d.ts +0 -8
- package/dist/trajectory/constants.d.ts +0 -6
- package/dist/trajectory/constants.js +0 -11
- /package/dist/{scene/types.js → file-viewer/host-protocol.js} +0 -0
- /package/dist/{tooltip/types.js → isosurface/geometry-worker-types.js} +0 -0
|
@@ -1,22 +1,20 @@
|
|
|
1
|
-
import { CONFIG_DIRS_REGEX, STRUCT_KEYWORDS_REGEX, STRUCT_KEYWORDS_STRICT_REGEX, STRUCTURE_EXTENSIONS_REGEX, TRAJ_KEYWORDS_REGEX, VASP_FILES_REGEX, } from '../constants';
|
|
1
|
+
import { CONFIG_DIRS_REGEX, STRUCT_KEYWORDS_REGEX, STRUCT_KEYWORDS_STRICT_REGEX, STRUCTURE_EXTENSIONS_REGEX, TRAJ_KEYWORDS_REGEX, VASP_FILES_REGEX, XDATCAR_REGEX, XYZ_EXTXYZ_REGEX, } from '../constants';
|
|
2
2
|
import { strip_compression_extensions } from '../io/decompress';
|
|
3
3
|
// Filename-only detection lives apart from the parsers so lightweight callers
|
|
4
4
|
// (desktop recents, file pickers) do not load YAML, structure math, and element
|
|
5
5
|
// data just to choose an icon.
|
|
6
6
|
export function is_structure_file(filename) {
|
|
7
7
|
const name = strip_compression_extensions(filename);
|
|
8
|
-
if (/\.(?:traj|xtc|h5|hdf5)$/i.test(name) ||
|
|
8
|
+
if (/\.(?:traj|xtc|h5|hdf5)$/i.test(name) || XDATCAR_REGEX.test(name))
|
|
9
9
|
return false;
|
|
10
10
|
if (STRUCTURE_EXTENSIONS_REGEX.test(name) || VASP_FILES_REGEX.test(name))
|
|
11
11
|
return true;
|
|
12
|
-
if (
|
|
12
|
+
if (XYZ_EXTXYZ_REGEX.test(name))
|
|
13
13
|
return !TRAJ_KEYWORDS_REGEX.test(name);
|
|
14
14
|
if (/\.(?:yaml|yml|xml)$/i.test(name) && STRUCT_KEYWORDS_REGEX.test(name))
|
|
15
15
|
return true;
|
|
16
|
-
|
|
16
|
+
return (/\.json$/i.test(name) &&
|
|
17
17
|
STRUCT_KEYWORDS_STRICT_REGEX.test(name) &&
|
|
18
18
|
!TRAJ_KEYWORDS_REGEX.test(name) &&
|
|
19
|
-
!CONFIG_DIRS_REGEX.test(name))
|
|
20
|
-
return true;
|
|
21
|
-
return false;
|
|
19
|
+
!CONFIG_DIRS_REGEX.test(name));
|
|
22
20
|
}
|
package/dist/structure/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ATOMIC_WEIGHTS } from '../composition/parse';
|
|
2
|
-
import { element_data } from '../element';
|
|
2
|
+
import { element_by_symbol, element_data } from '../element';
|
|
3
3
|
import * as math from '../math';
|
|
4
4
|
export { default as Arrow } from './Arrow.svelte';
|
|
5
5
|
export * from './atom-properties';
|
|
@@ -42,23 +42,17 @@ export function get_element_counts(structure) {
|
|
|
42
42
|
export function format_chemical_formula(structure, sort_fn) {
|
|
43
43
|
// concatenate elements in a structure followed by their amount
|
|
44
44
|
const elements = get_element_counts(structure);
|
|
45
|
-
const formula =
|
|
46
|
-
for (const element of sort_fn(Object.keys(elements))) {
|
|
45
|
+
const formula = sort_fn(Object.keys(elements)).map((element) => {
|
|
47
46
|
const amount = elements[element] ?? 0;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
else
|
|
51
|
-
formula.push(`${element}<sub>${amount}</sub>`);
|
|
52
|
-
}
|
|
47
|
+
return amount === 1 ? element : `${element}<sub>${amount}</sub>`;
|
|
48
|
+
});
|
|
53
49
|
return formula.join(` `);
|
|
54
50
|
}
|
|
55
|
-
// Electronegativity by element symbol (0 when missing), for O(1) sort comparisons
|
|
56
|
-
const electronegativity_by_symbol = new Map(element_data.map((el) => [el.symbol, el.electronegativity ?? 0]));
|
|
57
51
|
export function format_formula_by_electronegativity(structure) {
|
|
58
52
|
// concatenate elements in a structure followed by their amount sorted by electronegativity
|
|
59
53
|
return format_chemical_formula(structure, (symbols) => symbols.sort((el1, el2) => {
|
|
60
|
-
const elec_neg1 =
|
|
61
|
-
const elec_neg2 =
|
|
54
|
+
const elec_neg1 = element_by_symbol.get(el1)?.electronegativity ?? 0;
|
|
55
|
+
const elec_neg2 = element_by_symbol.get(el2)?.electronegativity ?? 0;
|
|
62
56
|
// Sort by electronegativity (ascending), then alphabetically for ties
|
|
63
57
|
if (elec_neg1 !== elec_neg2)
|
|
64
58
|
return elec_neg1 - elec_neg2;
|
package/dist/structure/parse.js
CHANGED
|
@@ -37,7 +37,6 @@ const aggregate_parse_error = (filename) => {
|
|
|
37
37
|
return new Error(`Failed to parse structure${filename ? ` from '${filename}'` : ``}${detail}`);
|
|
38
38
|
};
|
|
39
39
|
const cif_coords_key = (coords) => `${coords[0].toFixed(6)},${coords[1].toFixed(6)},${coords[2].toFixed(6)}`;
|
|
40
|
-
const cif_site_key = (element, abc, label) => `${element}|${label}|${cif_coords_key(abc)}`;
|
|
41
40
|
// Bravais lattice centering translations (excluding the identity) keyed by the
|
|
42
41
|
// leading letter of a space-group Hermann-Mauguin symbol. R is the obverse
|
|
43
42
|
// hexagonal setting.
|
|
@@ -72,15 +71,9 @@ const vec3_from_values = (values, context) => {
|
|
|
72
71
|
throw new Error(`Invalid ${context}: expected 3 coordinates, got ${values?.length ?? 0}`);
|
|
73
72
|
}
|
|
74
73
|
const coords = math.finite_vec3_from_values(values);
|
|
75
|
-
if (coords)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const value = values[idx];
|
|
79
|
-
if (typeof value !== `number` || !Number.isFinite(value)) {
|
|
80
|
-
throw new TypeError(`Invalid ${context}: coordinate ${idx} must be finite, got ${String(value)}`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
throw new Error(`Invalid ${context}: expected 3 finite coordinates`);
|
|
74
|
+
if (!coords)
|
|
75
|
+
throw new Error(`Invalid ${context}: expected 3 finite coordinates, got [${values.map(String).join(`, `)}]`);
|
|
76
|
+
return coords;
|
|
84
77
|
};
|
|
85
78
|
// Parse a coordinate value that might be in various scientific notation formats
|
|
86
79
|
function parse_coordinate(str) {
|
|
@@ -219,9 +212,9 @@ export function parse_poscar(content) {
|
|
|
219
212
|
// Try to parse the first token as a number - if it succeeds, it's VASP 4 format
|
|
220
213
|
const has_element_symbols = isNaN(parse_leading_num(lines[line_index]));
|
|
221
214
|
if (has_element_symbols) {
|
|
222
|
-
// VASP 5+ format -
|
|
215
|
+
// VASP 5+ format - element symbols (possibly spanning multiple lines),
|
|
216
|
+
// followed by as many atom-count lines. Look ahead to find where numbers start.
|
|
223
217
|
let symbol_lines = 1;
|
|
224
|
-
// Look ahead to find where numbers start (atom counts)
|
|
225
218
|
for (let lookahead_idx = 1; lookahead_idx < 10; lookahead_idx++) {
|
|
226
219
|
if (line_index + lookahead_idx >= lines.length)
|
|
227
220
|
break;
|
|
@@ -230,21 +223,11 @@ export function parse_poscar(content) {
|
|
|
230
223
|
break;
|
|
231
224
|
}
|
|
232
225
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
// Parse atom counts (may span multiple lines)
|
|
240
|
-
for (let count_line_idx = 0; count_line_idx < symbol_lines; count_line_idx++) {
|
|
241
|
-
if (line_index + symbol_lines + count_line_idx < lines.length) {
|
|
242
|
-
const counts = lines[line_index + symbol_lines + count_line_idx]
|
|
243
|
-
.trim()
|
|
244
|
-
.split(/\s+/)
|
|
245
|
-
.map(Number);
|
|
246
|
-
atom_counts.push(...counts);
|
|
247
|
-
}
|
|
226
|
+
for (let offset = 0; offset < symbol_lines; offset++) {
|
|
227
|
+
const symbol_tokens = lines[line_index + offset]?.trim().split(/\s+/) ?? [];
|
|
228
|
+
element_symbols.push(...symbol_tokens);
|
|
229
|
+
const count_tokens = lines[line_index + symbol_lines + offset]?.trim().split(/\s+/);
|
|
230
|
+
atom_counts.push(...(count_tokens?.map(Number) ?? []));
|
|
248
231
|
}
|
|
249
232
|
line_index += 2 * symbol_lines;
|
|
250
233
|
}
|
|
@@ -393,17 +376,14 @@ export function parse_xyz(content) {
|
|
|
393
376
|
return null;
|
|
394
377
|
}
|
|
395
378
|
const element = validate_element_symbol(parts[0], atom_idx);
|
|
396
|
-
|
|
379
|
+
let xyz = vec3_from_values(parts.slice(1, 4).map(parse_coordinate), `XYZ atom position ${atom_idx + 1}`);
|
|
397
380
|
// Calculate fractional coordinates if lattice is available
|
|
398
381
|
let abc = [0, 0, 0];
|
|
399
382
|
if (lattice && xyz_frac_to_cart && xyz_cart_to_frac) {
|
|
400
|
-
//
|
|
383
|
+
// Wrap fractional coordinates into [0, 1) and recompute xyz from them so
|
|
384
|
+
// rendered atoms stay inside the primary unit cell
|
|
401
385
|
abc = wrap_to_unit_cell(xyz_cart_to_frac(xyz));
|
|
402
|
-
|
|
403
|
-
const wrapped_xyz = xyz_frac_to_cart(abc);
|
|
404
|
-
xyz[0] = wrapped_xyz[0];
|
|
405
|
-
xyz[1] = wrapped_xyz[1];
|
|
406
|
-
xyz[2] = wrapped_xyz[2];
|
|
386
|
+
xyz = xyz_frac_to_cart(abc);
|
|
407
387
|
}
|
|
408
388
|
sites.push(make_site(element, abc, xyz, `${element}${atom_idx + 1}`));
|
|
409
389
|
}
|
|
@@ -420,67 +400,31 @@ export function parse_xyz(content) {
|
|
|
420
400
|
const parse_symmetry_expression = (expr_input) => {
|
|
421
401
|
const coefficients = [0, 0, 0];
|
|
422
402
|
let translation = 0;
|
|
423
|
-
//
|
|
403
|
+
// Strip whitespace, then split into signed terms: "x-y+1/3" → ["x", "-y", "+1/3"].
|
|
404
|
+
// Dangling operators (e.g. "x+") produce no token and are silently ignored.
|
|
424
405
|
const expr = expr_input.replaceAll(/\s+/g, ``);
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
for (const char of expr) {
|
|
432
|
-
if ((char === `+` || char === `-`) && current_token.length > 0) {
|
|
433
|
-
tokens.push(current_token);
|
|
434
|
-
current_token = char;
|
|
435
|
-
}
|
|
436
|
-
else {
|
|
437
|
-
current_token += char;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
if (current_token)
|
|
441
|
-
tokens.push(current_token);
|
|
442
|
-
for (const token of tokens) {
|
|
443
|
-
// Check if this token is a variable term (x, y, or z with optional sign)
|
|
444
|
-
const var_match = /^(?<sign>[+-]?)(?<axis>[xyz])$/.exec(token);
|
|
445
|
-
if (var_match) {
|
|
446
|
-
const sign = var_match[1] === `-` ? -1 : 1;
|
|
447
|
-
const var_char = var_match[2];
|
|
448
|
-
const var_idx = var_char === `x` ? 0 : var_char === `y` ? 1 : 2;
|
|
406
|
+
for (const token of expr.match(/[+-]?[^+-]+/g) ?? []) {
|
|
407
|
+
const sign = token.startsWith(`-`) ? -1 : 1;
|
|
408
|
+
const term = token.replace(/^[+-]/, ``);
|
|
409
|
+
// Variable term (x, y, or z)
|
|
410
|
+
const var_idx = [`x`, `y`, `z`].indexOf(term);
|
|
411
|
+
if (var_idx !== -1) {
|
|
449
412
|
coefficients[var_idx] += sign;
|
|
450
413
|
continue;
|
|
451
414
|
}
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
num_str = num_str.slice(1);
|
|
458
|
-
}
|
|
459
|
-
else if (num_str.startsWith(`-`)) {
|
|
460
|
-
sign = -1;
|
|
461
|
-
num_str = num_str.slice(1);
|
|
462
|
-
}
|
|
463
|
-
// Skip empty tokens (from dangling operators like "x+")
|
|
464
|
-
if (!num_str || num_str === `+` || num_str === `-`)
|
|
415
|
+
// Numeric term: integer, decimal, or fraction like "1/3"
|
|
416
|
+
const parts = term.split(`/`);
|
|
417
|
+
// skip malformed terms like "1/2/3"
|
|
418
|
+
if (parts.length > 2) {
|
|
419
|
+
diag_warn(`Skipping malformed symmetry term '${term}'`);
|
|
465
420
|
continue;
|
|
466
|
-
if (num_str.includes(`/`)) {
|
|
467
|
-
// Fraction
|
|
468
|
-
const parts = num_str.split(`/`);
|
|
469
|
-
if (parts.length === 2) {
|
|
470
|
-
const numerator = Number(parts[0]);
|
|
471
|
-
const denominator = Number(parts[1]);
|
|
472
|
-
if (!isNaN(numerator) && !isNaN(denominator) && denominator !== 0) {
|
|
473
|
-
translation += sign * (numerator / denominator);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
else {
|
|
478
|
-
// Integer or decimal
|
|
479
|
-
const val = Number(num_str);
|
|
480
|
-
if (!isNaN(val)) {
|
|
481
|
-
translation += sign * val;
|
|
482
|
-
}
|
|
483
421
|
}
|
|
422
|
+
const [numerator, denominator = `1`] = parts;
|
|
423
|
+
const value = Number(numerator) / Number(denominator);
|
|
424
|
+
if (Number.isFinite(value))
|
|
425
|
+
translation += sign * value;
|
|
426
|
+
else
|
|
427
|
+
diag_warn(`Skipping non-finite symmetry term '${term}'`);
|
|
484
428
|
}
|
|
485
429
|
return { coefficients, translation };
|
|
486
430
|
};
|
|
@@ -606,12 +550,9 @@ const split_cif_tokens = (line) => (line.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)
|
|
|
606
550
|
// Parse atom data from CIF with robust error handling
|
|
607
551
|
const parse_cif_atom_data = (raw_data, indices, coords_type) => {
|
|
608
552
|
const { label = 0, symbol = -1, occupancy = -1 } = indices;
|
|
553
|
+
// cif_coords_type already guaranteed all three indices for coords_type exist
|
|
609
554
|
const coord_indices = cif_coord_indices(indices, coords_type);
|
|
610
|
-
if (coord_indices.some((idx) => idx === undefined)) {
|
|
611
|
-
throw new Error(`Missing coordinate indices`);
|
|
612
|
-
}
|
|
613
555
|
const coords_triplet = vec3_from_values(coord_indices.map((idx) => {
|
|
614
|
-
// idx cannot be undefined: the `.some` guard above already threw
|
|
615
556
|
const coord_str = raw_data[idx];
|
|
616
557
|
if (!coord_str)
|
|
617
558
|
throw new Error(`Missing coordinate at index ${idx}`);
|
|
@@ -623,8 +564,8 @@ const parse_cif_atom_data = (raw_data, indices, coords_type) => {
|
|
|
623
564
|
const raw_occu = occupancy >= 0 && raw_data[occupancy]
|
|
624
565
|
? parse_cif_uncertain_number(raw_data[occupancy])
|
|
625
566
|
: null;
|
|
626
|
-
//
|
|
627
|
-
const occu = raw_occu
|
|
567
|
+
// Explicit 0 is kept; missing / `.` / `?` (null) default to fully occupied.
|
|
568
|
+
const occu = raw_occu ?? 1.0;
|
|
628
569
|
const from_symbol = symbol >= 0 ? /^(?<element>[A-Z][a-z]*)/.exec(raw_data[symbol])?.[1] : undefined;
|
|
629
570
|
const element_symbol = from_symbol ?? raw_data[label]?.match(/(?:[A-Z][a-z]*)/g)?.[0];
|
|
630
571
|
if (!element_symbol) {
|
|
@@ -816,7 +757,7 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
816
757
|
const equiv_atoms = apply_symmetry_ops(fractional_atom, ops_to_use, wrap_fractional_coords, extra_centering);
|
|
817
758
|
for (const equiv_atom of equiv_atoms) {
|
|
818
759
|
const abc = wrap_vec3(equiv_atom.coords);
|
|
819
|
-
const key =
|
|
760
|
+
const key = `${element}|${equiv_atom.id}|${cif_coords_key(abc)}`;
|
|
820
761
|
if (seen_site_keys.has(key))
|
|
821
762
|
continue;
|
|
822
763
|
seen_site_keys.add(key);
|
|
@@ -1321,10 +1262,7 @@ const unwrap_data = (value) => value && typeof value === `object` && `data` in v
|
|
|
1321
1262
|
function is_optimade_structure_object(value) {
|
|
1322
1263
|
if (!value || typeof value !== `object`)
|
|
1323
1264
|
return false;
|
|
1324
|
-
const
|
|
1325
|
-
const type = obj.type;
|
|
1326
|
-
const id = obj.id;
|
|
1327
|
-
const attributes = obj.attributes;
|
|
1265
|
+
const { type, id, attributes } = value;
|
|
1328
1266
|
return (type === `structures` &&
|
|
1329
1267
|
typeof id === `string` &&
|
|
1330
1268
|
typeof attributes === `object` &&
|
|
@@ -10,7 +10,6 @@ const make_render_site = (sites, site_idx, source_site_indices, site_override) =
|
|
|
10
10
|
is_image_atom: source_site_indices.some((source_site_idx) => is_image_atom(sites[source_site_idx])),
|
|
11
11
|
source_site_indices,
|
|
12
12
|
});
|
|
13
|
-
const sq_dist = (xyz_1, xyz_2) => (xyz_1[0] - xyz_2[0]) ** 2 + (xyz_1[1] - xyz_2[1]) ** 2 + (xyz_1[2] - xyz_2[2]) ** 2;
|
|
14
13
|
const is_split_partial_site = (site, hidden_elements) => {
|
|
15
14
|
const visible_species = site.species.filter(({ element }) => !hidden_elements.has(element));
|
|
16
15
|
const total_visible_occupancy = visible_species.reduce((occupancy_sum, { occu }) => occupancy_sum + occu, 0);
|
|
@@ -25,7 +24,12 @@ const group_split_partial_indices = (sites, hidden_elements) => {
|
|
|
25
24
|
non_grouped_site_indices.push(site_idx);
|
|
26
25
|
continue;
|
|
27
26
|
}
|
|
28
|
-
const matched_group_idx = grouped_centers.findIndex((center_xyz) =>
|
|
27
|
+
const matched_group_idx = grouped_centers.findIndex((center_xyz) => {
|
|
28
|
+
const dx = center_xyz[0] - site.xyz[0];
|
|
29
|
+
const dy = center_xyz[1] - site.xyz[1];
|
|
30
|
+
const dz = center_xyz[2] - site.xyz[2];
|
|
31
|
+
return dx * dx + dy * dy + dz * dz <= MERGE_DISTANCE_TOLERANCE ** 2;
|
|
32
|
+
});
|
|
29
33
|
if (matched_group_idx === -1) {
|
|
30
34
|
grouped_centers.push(site.xyz);
|
|
31
35
|
grouped_site_indices.push([site_idx]);
|
|
@@ -59,8 +63,7 @@ export const PARTIAL_OCCUPANCY_CAP_ARC = {
|
|
|
59
63
|
arc_length: Math.PI,
|
|
60
64
|
};
|
|
61
65
|
export const merge_split_partial_sites = (sites, hidden_elements = new Set()) => {
|
|
62
|
-
const
|
|
63
|
-
const { non_grouped_site_indices, grouped_site_indices } = grouped_indices;
|
|
66
|
+
const { non_grouped_site_indices, grouped_site_indices } = group_split_partial_indices(sites, hidden_elements);
|
|
64
67
|
return build_render_sites(sites, non_grouped_site_indices, grouped_site_indices);
|
|
65
68
|
};
|
|
66
69
|
export const compute_slice_geometry = (visible_species, slice_gap_rad = PARTIAL_OCCUPANCY_SLICE_GAP_RAD) => {
|
package/dist/structure/pbc.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
// Periodic boundary conditions utilities
|
|
2
|
+
import { element_by_symbol } from '../element/data';
|
|
1
3
|
import * as math from '../math';
|
|
2
|
-
import {
|
|
4
|
+
import { get_majority_element, has_framework_potential, is_spectator_center, pack_cell_key, } from './bonding';
|
|
3
5
|
// Distance slack added to the covalent-radii sum when deciding whether a
|
|
4
6
|
// candidate image atom bonds to a base atom (VESTA-like bond search criterion)
|
|
5
7
|
const BOND_SLACK = 0.4; // Å
|
|
@@ -107,7 +109,7 @@ export function find_image_atoms(structure, { tolerance } = {}) {
|
|
|
107
109
|
let max_radius = 0;
|
|
108
110
|
for (const site of structure.sites) {
|
|
109
111
|
const elem = get_majority_element(site);
|
|
110
|
-
const data = elem === null ? undefined :
|
|
112
|
+
const data = elem === null ? undefined : element_by_symbol.get(elem);
|
|
111
113
|
const radius = data?.covalent_radius ?? null;
|
|
112
114
|
site_elements.push(elem);
|
|
113
115
|
if (elem !== null)
|
|
@@ -250,17 +252,13 @@ export function find_image_atoms(structure, { tolerance } = {}) {
|
|
|
250
252
|
}
|
|
251
253
|
return image_sites;
|
|
252
254
|
}
|
|
253
|
-
// Return structure with image atoms added
|
|
255
|
+
// Return structure with image atoms added (unchanged when none are generated,
|
|
256
|
+
// e.g. for scattered trajectory-like data)
|
|
254
257
|
export function get_pbc_image_sites(...args) {
|
|
255
258
|
const structure = args[0];
|
|
256
|
-
if (!structure || !structure.sites || structure.sites.length === 0) {
|
|
257
|
-
return structure;
|
|
258
|
-
}
|
|
259
|
-
// Return trajectory data unchanged
|
|
260
|
-
if (is_scattered_trajectory(structure.sites))
|
|
261
|
-
return structure;
|
|
262
|
-
// Add image atoms to regular crystal structures
|
|
263
259
|
const image_sites = find_image_atoms(...args);
|
|
260
|
+
if (image_sites.length === 0)
|
|
261
|
+
return structure;
|
|
264
262
|
const imaged_struct = { ...structure, sites: [...structure.sites] };
|
|
265
263
|
for (const [site_idx, img_xyz, img_abc, is_completion] of image_sites) {
|
|
266
264
|
const orig_site = structure.sites[site_idx];
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
// so the whole scene renders in 1-2 draw calls regardless of supercell size.
|
|
5
5
|
// Hot paths use scalar math and per-element caches to scale to large structures.
|
|
6
6
|
import { rgb as parse_rgb } from 'd3-color';
|
|
7
|
+
import { element_by_symbol } from '../element/data';
|
|
7
8
|
import { DEFAULTS } from '../settings';
|
|
8
9
|
import { get_orig_site_idx } from './atom-properties';
|
|
9
|
-
import {
|
|
10
|
+
import { get_majority_element, has_framework_potential, is_spectator_center } from './bonding';
|
|
10
11
|
const face_of = (points, vert_a, vert_b, vert_c) => {
|
|
11
12
|
const [ax, ay, az] = points[vert_a];
|
|
12
13
|
const abx = points[vert_b][0] - ax;
|
|
@@ -301,7 +302,7 @@ export function build_adjacency(bonds) {
|
|
|
301
302
|
function is_anion_vertex(center_en, center_is_metal, neighbor_element, electronegativity_margin) {
|
|
302
303
|
if (!neighbor_element)
|
|
303
304
|
return false;
|
|
304
|
-
const n_data =
|
|
305
|
+
const n_data = element_by_symbol.get(neighbor_element);
|
|
305
306
|
if (n_data?.metal)
|
|
306
307
|
return false;
|
|
307
308
|
const n_en = n_data?.electronegativity ?? null;
|
|
@@ -341,12 +342,12 @@ export function compute_polyhedra(structure, bonds, options = {}) {
|
|
|
341
342
|
const center_info = (element) => {
|
|
342
343
|
let info = center_info_cache.get(element);
|
|
343
344
|
if (!info) {
|
|
344
|
-
const data =
|
|
345
|
+
const data = element_by_symbol.get(element);
|
|
345
346
|
const center_en = data?.electronegativity ?? null;
|
|
346
347
|
const r_center = data?.covalent_radius ?? null;
|
|
347
348
|
const accepts = new Set(unique_elements.filter((n_elem) => is_anion_vertex(center_en, data?.metal === true, n_elem, electronegativity_margin)));
|
|
348
349
|
const radii_sums = new Map(unique_elements.map((n_elem) => {
|
|
349
|
-
const r_n =
|
|
350
|
+
const r_n = element_by_symbol.get(n_elem)?.covalent_radius ?? null;
|
|
350
351
|
return [n_elem, r_center !== null && r_n !== null ? r_center + r_n : null];
|
|
351
352
|
}));
|
|
352
353
|
info = { accepts, radii_sums };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { DefaultSettings } from '../settings';
|
|
2
|
+
export declare const build_structure_props_from_settings: (defaults: DefaultSettings) => {
|
|
3
|
+
scene_props: {
|
|
4
|
+
gizmo: boolean;
|
|
5
|
+
atom_radius: number;
|
|
6
|
+
same_size_atoms: boolean;
|
|
7
|
+
show_atoms: boolean;
|
|
8
|
+
show_image_atoms: boolean;
|
|
9
|
+
sphere_segments: number;
|
|
10
|
+
bond_thickness: number;
|
|
11
|
+
auto_bond_order: boolean;
|
|
12
|
+
aromatic_display: "aromatic" | "kekule";
|
|
13
|
+
show_bonds: "always" | "never" | "crystals" | "molecules";
|
|
14
|
+
bond_color: string;
|
|
15
|
+
bonding_strategy: "electroneg_ratio" | "solid_angle";
|
|
16
|
+
show_polyhedra: "always" | "never" | "crystals" | "molecules";
|
|
17
|
+
polyhedra_opacity: number;
|
|
18
|
+
polyhedra_show_edges: boolean;
|
|
19
|
+
polyhedra_edge_color: string;
|
|
20
|
+
polyhedra_color_mode: import("./polyhedra").PolyhedraColorMode;
|
|
21
|
+
polyhedra_color: string;
|
|
22
|
+
polyhedra_hide_center_atoms: boolean;
|
|
23
|
+
polyhedra_min_neighbors: number;
|
|
24
|
+
polyhedra_max_neighbors: number;
|
|
25
|
+
polyhedra_excluded_elements: readonly string[];
|
|
26
|
+
polyhedra_included_elements: readonly string[];
|
|
27
|
+
atom_color_mode: "element" | "coordination" | "wyckoff" | "custom";
|
|
28
|
+
atom_color_scale: import("..").D3InterpolateName;
|
|
29
|
+
atom_color_scale_type: "continuous" | "categorical";
|
|
30
|
+
show_gizmo: boolean;
|
|
31
|
+
camera_position: import("..").Vec3;
|
|
32
|
+
camera_projection: import("../settings").CameraProjection;
|
|
33
|
+
initial_zoom: number;
|
|
34
|
+
fov: number;
|
|
35
|
+
rotation_damping: number;
|
|
36
|
+
rotate_speed: number;
|
|
37
|
+
zoom_speed: number;
|
|
38
|
+
pan_speed: number;
|
|
39
|
+
zoom_to_cursor: boolean;
|
|
40
|
+
max_zoom: number | undefined;
|
|
41
|
+
min_zoom: number | undefined;
|
|
42
|
+
auto_rotate: number;
|
|
43
|
+
rotation: import("..").Vec3;
|
|
44
|
+
show_site_labels: boolean;
|
|
45
|
+
show_site_indices: boolean;
|
|
46
|
+
site_label_size: number;
|
|
47
|
+
site_label_color: string;
|
|
48
|
+
site_label_bg_color: string;
|
|
49
|
+
site_label_padding: number;
|
|
50
|
+
site_label_offset: import("..").Vec3;
|
|
51
|
+
ambient_light: number;
|
|
52
|
+
directional_light: number;
|
|
53
|
+
vector_configs: Record<string, import("../settings").VectorLayerConfig>;
|
|
54
|
+
vector_scale: number;
|
|
55
|
+
vector_color: string;
|
|
56
|
+
vector_color_mode: "auto" | "element" | "uniform" | "spin_direction" | "magnitude";
|
|
57
|
+
vector_color_scale: import("..").D3InterpolateName;
|
|
58
|
+
vector_normalize: boolean;
|
|
59
|
+
vector_uniform_thickness: boolean;
|
|
60
|
+
vector_origin_gap: number;
|
|
61
|
+
vector_shaft_radius: number;
|
|
62
|
+
vector_arrow_head_radius: number;
|
|
63
|
+
vector_arrow_head_length: number;
|
|
64
|
+
show_cell: boolean;
|
|
65
|
+
show_cell_vectors: boolean;
|
|
66
|
+
cell_edge_opacity: number;
|
|
67
|
+
cell_surface_opacity: number;
|
|
68
|
+
cell_edge_color: string;
|
|
69
|
+
cell_surface_color: string;
|
|
70
|
+
cell_edge_width: number;
|
|
71
|
+
fullscreen_toggle: boolean;
|
|
72
|
+
};
|
|
73
|
+
lattice_props: {
|
|
74
|
+
cell_edge_opacity: number;
|
|
75
|
+
cell_surface_opacity: number;
|
|
76
|
+
cell_edge_color: string;
|
|
77
|
+
cell_surface_color: string;
|
|
78
|
+
cell_edge_width: number;
|
|
79
|
+
show_cell_vectors: boolean;
|
|
80
|
+
};
|
|
81
|
+
color_scheme: string;
|
|
82
|
+
background_color: string;
|
|
83
|
+
background_opacity: number;
|
|
84
|
+
show_image_atoms: boolean;
|
|
85
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const build_structure_props_from_settings = (defaults) => {
|
|
2
|
+
const { structure } = defaults;
|
|
3
|
+
return {
|
|
4
|
+
scene_props: { ...structure, gizmo: structure.show_gizmo },
|
|
5
|
+
lattice_props: {
|
|
6
|
+
cell_edge_opacity: structure.cell_edge_opacity,
|
|
7
|
+
cell_surface_opacity: structure.cell_surface_opacity,
|
|
8
|
+
cell_edge_color: structure.cell_edge_color,
|
|
9
|
+
cell_surface_color: structure.cell_surface_color,
|
|
10
|
+
cell_edge_width: structure.cell_edge_width,
|
|
11
|
+
show_cell_vectors: structure.show_cell_vectors,
|
|
12
|
+
},
|
|
13
|
+
color_scheme: defaults.color_scheme,
|
|
14
|
+
background_color: defaults.background_color,
|
|
15
|
+
background_opacity: defaults.background_opacity,
|
|
16
|
+
show_image_atoms: structure.show_image_atoms,
|
|
17
|
+
};
|
|
18
|
+
};
|
|
@@ -95,14 +95,10 @@
|
|
|
95
95
|
hermann_mauguin: `Hermann-Mauguin symbol describes symmetry operations. Format: Lattice type + Point group symmetry. Example: P4/mmm = Primitive + 4-fold rotation + mirror planes`,
|
|
96
96
|
hall_number: `Hall number: alternative numbering system for space groups. Useful for crystallographic software compatibility.`,
|
|
97
97
|
pearson_symbol: `Pearson symbol. Format: Crystal system + Number of atoms per unit cell. Example: tP2 = tetragonal primitive with 2 atoms`,
|
|
98
|
-
symmetry_operations: `Total symmetry operations that map the crystal structure onto itself. Includes rotations, translations, and combinations.`,
|
|
99
98
|
distinct_orbits: `Number of unique Wyckoff positions (symmetry-equivalent atomic sites) in the crystal structure.`,
|
|
100
99
|
wyckoff_sequence: `Wyckoff sequence: letters of all occupied Wyckoff positions in descending alphabetical order, with superscript counts for letters occupied by multiple orbits. A standard structure-type fingerprint (complements the Pearson symbol).`,
|
|
101
100
|
free_params: `Internal degrees of freedom: number of free fractional-coordinate parameters (x, y, z in the ITA representative coordinates) summed over occupied Wyckoff orbits. 0 means all atomic positions are fully fixed by symmetry.`,
|
|
102
101
|
settings: `All settings of this space group in the International Tables (origin choices, unique axes, cell choices, hexagonal vs rhombohedral axes). The setting detected for this structure is highlighted.`,
|
|
103
|
-
translations: `Number of translations in the crystal structure.`,
|
|
104
|
-
rotations: `Number of rotations in the crystal structure.`,
|
|
105
|
-
roto_translations: `Number of roto-translations in the crystal structure.`,
|
|
106
102
|
}
|
|
107
103
|
const tooltips: Record<string, string> = $derived(show_tooltips ? titles : {})
|
|
108
104
|
|
|
@@ -102,13 +102,13 @@
|
|
|
102
102
|
<td>
|
|
103
103
|
<span
|
|
104
104
|
style:background-color={colors.element[elem]}
|
|
105
|
-
style="display: inline-block; padding: 0
|
|
105
|
+
style="display: inline-block; padding: 0 4pt; border-radius: 3pt; line-height: 1.25"
|
|
106
106
|
{@attach contrast_color()}
|
|
107
107
|
>
|
|
108
108
|
{elem}
|
|
109
109
|
</span>
|
|
110
110
|
</td>
|
|
111
|
-
<td>({abc?.map(format_fractional).join(
|
|
111
|
+
<td>({abc?.map(format_fractional).join(`, `) ?? `N/A`})</td>
|
|
112
112
|
{#if has_ita_coords}
|
|
113
113
|
<td>{wyckoff_pos.coordinates ? `(${wyckoff_pos.coordinates})` : ``}</td>
|
|
114
114
|
{/if}
|
|
@@ -136,10 +136,11 @@
|
|
|
136
136
|
|
|
137
137
|
<style>
|
|
138
138
|
.wyckoff-table {
|
|
139
|
-
margin-top:
|
|
139
|
+
margin-top: 0.5em;
|
|
140
|
+
border-collapse: collapse;
|
|
140
141
|
}
|
|
141
142
|
.wyckoff-table :is(th, td) {
|
|
142
|
-
padding:
|
|
143
|
+
padding: 1px 3px;
|
|
143
144
|
text-align: center;
|
|
144
145
|
vertical-align: middle;
|
|
145
146
|
}
|
|
@@ -19,10 +19,8 @@ original_structure) {
|
|
|
19
19
|
// serialization for both input and output (see to_cell_json: column-major(B) ==
|
|
20
20
|
// row-major(RB)), so the flat array is row-major with each row a lattice vector.
|
|
21
21
|
const lattice_matrix = math.vec9_to_mat3x3([...cell.lattice.basis]);
|
|
22
|
-
// Calculate lattice parameters from matrix
|
|
23
22
|
const lattice_params = math.calc_lattice_params(lattice_matrix);
|
|
24
23
|
const frac_to_cart = math.create_frac_to_cart(lattice_matrix);
|
|
25
|
-
// Build sites from positions and atomic numbers
|
|
26
24
|
const sites = cell.positions.map((abc, idx) => {
|
|
27
25
|
const atomic_number = cell.numbers[idx];
|
|
28
26
|
const element = ATOMIC_NUMBER_TO_SYMBOL[atomic_number];
|
|
@@ -31,11 +29,10 @@ original_structure) {
|
|
|
31
29
|
}
|
|
32
30
|
// Wrap fractional coordinates to [0, 1) range (moyo-wasm may return outside)
|
|
33
31
|
const wrapped_abc = wrap_to_unit_cell(abc);
|
|
34
|
-
const xyz = frac_to_cart(wrapped_abc);
|
|
35
32
|
// Oxidation state is set to 0 (unknown) because moyo-wasm only provides atomic numbers.
|
|
36
33
|
// transformed cell may have different/reordered sites, making it non-trivial to
|
|
37
34
|
// map oxidation states from original structure.
|
|
38
|
-
return make_site(element, wrapped_abc,
|
|
35
|
+
return make_site(element, wrapped_abc, frac_to_cart(wrapped_abc), element);
|
|
39
36
|
});
|
|
40
37
|
const lattice = {
|
|
41
38
|
matrix: lattice_matrix,
|
|
@@ -55,14 +52,12 @@ original_structure) {
|
|
|
55
52
|
// The conventional cell is the standard crystallographic setting for the space group.
|
|
56
53
|
export function get_conventional_cell(original_structure, // The original input structure
|
|
57
54
|
sym_data) {
|
|
58
|
-
// The conventional cell as a Crystal
|
|
59
55
|
return moyo_cell_to_structure(sym_data.std_cell, original_structure);
|
|
60
56
|
}
|
|
61
57
|
// Get the primitive cell from symmetry analysis data.
|
|
62
58
|
// The primitive cell is the smallest unit cell with one lattice point.
|
|
63
59
|
export function get_primitive_cell(original_structure, // The original input structure
|
|
64
60
|
sym_data) {
|
|
65
|
-
// The primitive cell as a Crystal
|
|
66
61
|
return moyo_cell_to_structure(sym_data.prim_std_cell, original_structure);
|
|
67
62
|
}
|
|
68
63
|
// Transform a structure based on the selected cell type.
|
|
@@ -70,16 +65,12 @@ sym_data) {
|
|
|
70
65
|
export function transform_cell(structure, // The original structure
|
|
71
66
|
cell_type, // The desired cell type ('original', 'conventional', or 'primitive')
|
|
72
67
|
sym_data) {
|
|
73
|
-
|
|
74
|
-
if (cell_type === `original` || !sym_data) {
|
|
68
|
+
if (!sym_data)
|
|
75
69
|
return structure;
|
|
76
|
-
|
|
77
|
-
if (cell_type === `conventional`) {
|
|
70
|
+
if (cell_type === `conventional`)
|
|
78
71
|
return get_conventional_cell(structure, sym_data);
|
|
79
|
-
|
|
80
|
-
if (cell_type === `primitive`) {
|
|
72
|
+
if (cell_type === `primitive`)
|
|
81
73
|
return get_primitive_cell(structure, sym_data);
|
|
82
|
-
|
|
83
|
-
// Unknown cell_type at runtime (e.g. from stale persisted settings): leave unchanged
|
|
74
|
+
// 'original' or unknown runtime cell_type (e.g. stale persisted settings): unchanged
|
|
84
75
|
return structure;
|
|
85
76
|
}
|
package/dist/symmetry/index.js
CHANGED
|
@@ -89,10 +89,9 @@ function get_site_atomic_number(site, site_idx) {
|
|
|
89
89
|
best_occupancy = occupancy;
|
|
90
90
|
}
|
|
91
91
|
});
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const atomic_number = SYMBOL_TO_ATOMIC_NUMBER[selected_element];
|
|
92
|
+
const atomic_number = selected_element
|
|
93
|
+
? SYMBOL_TO_ATOMIC_NUMBER[selected_element]
|
|
94
|
+
: undefined;
|
|
96
95
|
if (atomic_number === undefined) {
|
|
97
96
|
throw new Error(`Unknown element at site ${site_idx}: ${selected_element}`);
|
|
98
97
|
}
|
|
@@ -233,13 +232,11 @@ function wyckoff_rows_from_input_orbits(sym_data) {
|
|
|
233
232
|
};
|
|
234
233
|
});
|
|
235
234
|
}
|
|
236
|
-
// Generate Wyckoff table rows from symmetry data by grouping moyo's input-cell sites into
|
|
237
|
-
// crystallographic orbits. moyo's per-site arrays (wyckoffs, orbits, site_symmetry_symbols)
|
|
238
|
-
// always index the input cell and analyze_structure_symmetry always attaches input_cell, so
|
|
239
|
-
// the orbit grouping is the single source of truth for any input cell setting.
|
|
240
|
-
// Rows sort by ascending multiplicity, then Wyckoff label.
|
|
241
235
|
// Numeric multiplicity prefix of a Wyckoff label like `4a` (NaN when absent)
|
|
242
236
|
export const wyckoff_multiplicity = (label) => Number(/^\d+/.exec(label)?.[0] ?? NaN);
|
|
237
|
+
// Generate Wyckoff table rows from symmetry data via wyckoff_rows_from_input_orbits
|
|
238
|
+
// (analyze_structure_symmetry always attaches the input_cell that grouping needs).
|
|
239
|
+
// Rows sort by ascending multiplicity, then Wyckoff label.
|
|
243
240
|
export function wyckoff_positions_from_moyo(sym_data) {
|
|
244
241
|
if (!sym_data)
|
|
245
242
|
return [];
|