matterviz 0.3.5 → 0.3.7
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/MillerIndexInput.svelte +5 -5
- package/dist/api/optimade.js +3 -3
- package/dist/brillouin/BrillouinZone.svelte +5 -2
- package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneExportPane.svelte +1 -3
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +1 -1
- package/dist/brillouin/BrillouinZoneScene.svelte +5 -5
- package/dist/brillouin/compute.js +21 -21
- package/dist/brillouin/index.d.ts +1 -1
- package/dist/brillouin/index.js +0 -1
- package/dist/brillouin/types.d.ts +8 -13
- package/dist/chempot-diagram/ChemPotDiagram.svelte +3 -3
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +3 -4
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +33 -34
- package/dist/chempot-diagram/compute.js +1 -7
- package/dist/chempot-diagram/temperature.d.ts +1 -1
- package/dist/chempot-diagram/temperature.js +1 -3
- package/dist/chempot-diagram/types.d.ts +4 -9
- package/dist/colors/index.js +5 -5
- package/dist/composition/Composition.svelte +2 -1
- package/dist/composition/Formula.svelte +7 -4
- package/dist/composition/FormulaFilter.svelte +1 -3
- package/dist/composition/format.js +4 -4
- package/dist/composition/parse.d.ts +2 -1
- package/dist/composition/parse.js +61 -46
- package/dist/convex-hull/ConvexHull2D.svelte +62 -51
- package/dist/convex-hull/ConvexHull3D.svelte +101 -90
- package/dist/convex-hull/ConvexHull4D.svelte +70 -58
- package/dist/convex-hull/ConvexHullControls.svelte +24 -35
- package/dist/convex-hull/ConvexHullInfoPane.svelte +8 -5
- package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +2 -0
- package/dist/convex-hull/ConvexHullStats.svelte +9 -2
- package/dist/convex-hull/ConvexHullStats.svelte.d.ts +2 -0
- package/dist/convex-hull/GasPressureControls.svelte +7 -7
- package/dist/convex-hull/StructurePopup.svelte +65 -30
- package/dist/convex-hull/StructurePopup.svelte.d.ts +6 -6
- package/dist/convex-hull/TemperatureSlider.svelte +8 -5
- package/dist/convex-hull/barycentric-coords.d.ts +2 -2
- package/dist/convex-hull/barycentric-coords.js +2 -2
- package/dist/convex-hull/gas-thermodynamics.js +2 -4
- package/dist/convex-hull/helpers.d.ts +13 -2
- package/dist/convex-hull/helpers.js +37 -16
- package/dist/convex-hull/index.d.ts +1 -0
- package/dist/convex-hull/index.js +1 -0
- package/dist/convex-hull/thermodynamics.d.ts +2 -1
- package/dist/convex-hull/thermodynamics.js +7 -7
- package/dist/convex-hull/types.d.ts +15 -15
- package/dist/effects.svelte.d.ts +12 -0
- package/dist/effects.svelte.js +37 -0
- package/dist/element/BohrAtom.svelte +4 -4
- package/dist/element/data.json.gz.d.ts +3 -1
- package/dist/element/index.d.ts +1 -1
- package/dist/element/index.js +0 -1
- package/dist/fermi-surface/FermiSurface.svelte +4 -4
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte +15 -19
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +8 -6
- package/dist/fermi-surface/compute.js +2 -2
- package/dist/fermi-surface/export.js +13 -26
- package/dist/fermi-surface/parse.js +8 -12
- package/dist/fermi-surface/types.d.ts +2 -5
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +21 -3
- package/dist/heatmap-matrix/index.js +6 -6
- package/dist/io/decompress.d.ts +2 -1
- package/dist/io/decompress.js +1 -1
- package/dist/io/export.js +1 -1
- package/dist/io/index.d.ts +1 -1
- package/dist/io/index.js +0 -1
- package/dist/io/url-drop.js +7 -1
- package/dist/isosurface/IsosurfaceControls.svelte +11 -25
- package/dist/isosurface/slice.js +1 -1
- package/dist/isosurface/types.js +12 -12
- package/dist/labels.d.ts +1 -1
- package/dist/labels.js +14 -11
- package/dist/layout/InfoTag.svelte +6 -4
- package/dist/layout/PropertyFilter.svelte +4 -2
- package/dist/layout/json-tree/JsonTree.svelte +22 -14
- package/dist/layout/json-tree/JsonValue.svelte +2 -2
- package/dist/layout/json-tree/types.d.ts +3 -2
- package/dist/layout/json-tree/types.js +0 -1
- package/dist/layout/json-tree/utils.d.ts +4 -4
- package/dist/layout/json-tree/utils.js +12 -20
- package/dist/marching-cubes.js +13 -15
- package/dist/math.d.ts +11 -1
- package/dist/math.js +15 -6
- package/dist/overlays/DragControlTab.svelte +98 -0
- package/dist/overlays/DragControlTab.svelte.d.ts +8 -0
- package/dist/overlays/DraggablePane.svelte +7 -84
- package/dist/overlays/index.d.ts +1 -0
- package/dist/overlays/index.js +1 -0
- package/dist/periodic-table/PeriodicTable.svelte +11 -11
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +4 -2
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramControls.svelte +4 -9
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +2 -10
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +2 -3
- package/dist/phase-diagram/TdbInfoPanel.svelte +3 -3
- package/dist/phase-diagram/build-diagram.js +11 -18
- package/dist/phase-diagram/diagram-input.d.ts +5 -9
- package/dist/phase-diagram/index.d.ts +2 -2
- package/dist/phase-diagram/index.js +0 -2
- package/dist/phase-diagram/parse.d.ts +2 -2
- package/dist/phase-diagram/parse.js +6 -10
- package/dist/phase-diagram/svg-to-diagram.js +15 -15
- package/dist/phase-diagram/types.d.ts +5 -11
- package/dist/phase-diagram/utils.d.ts +2 -2
- package/dist/phase-diagram/utils.js +9 -11
- package/dist/plot/BarPlot.svelte +162 -314
- package/dist/plot/BarPlot.svelte.d.ts +5 -4
- package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
- package/dist/plot/BinnedScatterPlot.svelte +1114 -0
- package/dist/plot/BinnedScatterPlot.svelte.d.ts +66 -0
- package/dist/plot/ColorBar.svelte +19 -17
- package/dist/plot/ColorBar.svelte.d.ts +1 -1
- package/dist/plot/FillArea.svelte +2 -4
- package/dist/plot/FillArea.svelte.d.ts +1 -1
- package/dist/plot/Histogram.svelte +167 -281
- package/dist/plot/Histogram.svelte.d.ts +1 -1
- package/dist/plot/HistogramControls.svelte.d.ts +1 -1
- package/dist/plot/InteractiveAxisLabel.svelte +5 -3
- package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
- package/dist/plot/PlotAxis.svelte +169 -0
- package/dist/plot/PlotAxis.svelte.d.ts +24 -0
- package/dist/plot/PlotControls.svelte.d.ts +1 -1
- package/dist/plot/ReferenceLine3D.svelte +53 -51
- package/dist/plot/ReferencePlane.svelte +39 -42
- package/dist/plot/ScatterPlot.svelte +300 -367
- package/dist/plot/ScatterPlot.svelte.d.ts +8 -5
- package/dist/plot/ScatterPlot3D.svelte +33 -6
- package/dist/plot/ScatterPlot3D.svelte.d.ts +3 -2
- package/dist/plot/ScatterPlot3DControls.svelte +9 -9
- package/dist/plot/ScatterPlotControls.svelte +3 -4
- package/dist/plot/ScatterPoint.svelte +18 -27
- package/dist/plot/ScatterPoint.svelte.d.ts +4 -3
- package/dist/plot/Surface3D.svelte +4 -7
- package/dist/plot/ZeroLines.svelte +2 -1
- package/dist/plot/ZeroLines.svelte.d.ts +2 -1
- package/dist/plot/ZoomRect.svelte +2 -2
- package/dist/plot/ZoomRect.svelte.d.ts +3 -3
- package/dist/plot/adaptive-density.d.ts +69 -0
- package/dist/plot/adaptive-density.js +191 -0
- package/dist/plot/auto-place.d.ts +43 -0
- package/dist/plot/auto-place.js +122 -0
- package/dist/plot/axis-utils.js +3 -5
- package/dist/plot/binned-scatter-types.d.ts +59 -0
- package/dist/plot/binned-scatter-types.js +1 -0
- package/dist/plot/data-cleaning.js +1 -1
- package/dist/plot/data-transform.js +1 -1
- package/dist/plot/fill-utils.d.ts +4 -9
- package/dist/plot/fill-utils.js +29 -44
- package/dist/plot/index.d.ts +4 -0
- package/dist/plot/index.js +2 -0
- package/dist/plot/interactions.d.ts +4 -4
- package/dist/plot/interactions.js +4 -3
- package/dist/plot/layout.d.ts +20 -2
- package/dist/plot/layout.js +59 -16
- package/dist/plot/reference-line.d.ts +1 -1
- package/dist/plot/reference-line.js +9 -11
- package/dist/plot/scales.d.ts +1 -1
- package/dist/plot/scales.js +20 -23
- package/dist/plot/types.d.ts +30 -58
- package/dist/plot/types.js +2 -6
- package/dist/plot/utils/label-placement.d.ts +24 -3
- package/dist/plot/utils/label-placement.js +82 -12
- package/dist/plot/utils/series-visibility.d.ts +8 -2
- package/dist/plot/utils/series-visibility.js +23 -5
- package/dist/rdf/RdfPlot.svelte +5 -5
- package/dist/rdf/calc-rdf.js +3 -3
- package/dist/sanitize.d.ts +2 -0
- package/dist/sanitize.js +2 -0
- package/dist/spectral/Bands.svelte +1 -1
- package/dist/spectral/BandsAndDos.svelte +22 -16
- package/dist/spectral/BrillouinBandsDos.svelte +20 -16
- package/dist/spectral/Dos.svelte +1 -1
- package/dist/spectral/helpers.d.ts +4 -2
- package/dist/spectral/helpers.js +44 -35
- package/dist/spectral/index.d.ts +1 -1
- package/dist/spectral/index.js +0 -1
- package/dist/structure/AtomLegend.svelte +23 -6
- package/dist/structure/AtomLegend.svelte.d.ts +1 -0
- package/dist/structure/CanvasTooltip.svelte +9 -9
- package/dist/structure/CanvasTooltip.svelte.d.ts +1 -1
- package/dist/structure/CellSelect.svelte +14 -16
- package/dist/structure/Structure.svelte +317 -68
- package/dist/structure/Structure.svelte.d.ts +4 -2
- package/dist/structure/StructureControls.svelte +20 -45
- package/dist/structure/StructureExportPane.svelte +2 -1
- package/dist/structure/StructureInfoPane.svelte +10 -8
- package/dist/structure/StructureScene.svelte +527 -177
- package/dist/structure/StructureScene.svelte.d.ts +5 -2
- package/dist/structure/atom-properties.js +4 -4
- package/dist/structure/bond-order-perception.js +115 -98
- package/dist/structure/bonding.d.ts +27 -1
- package/dist/structure/bonding.js +187 -16
- package/dist/structure/export.js +1 -1
- package/dist/structure/index.d.ts +3 -2
- package/dist/structure/index.js +0 -2
- package/dist/structure/parse.js +88 -59
- package/dist/symmetry/WyckoffTable.svelte +7 -0
- package/dist/symmetry/index.js +13 -14
- package/dist/table/HeatmapTable.svelte +45 -66
- package/dist/table/HeatmapTable.svelte.d.ts +1 -1
- package/dist/table/ToggleMenu.svelte +19 -10
- package/dist/theme/themes.mjs +12 -0
- package/dist/tooltip/index.d.ts +1 -1
- package/dist/tooltip/index.js +0 -1
- package/dist/trajectory/Trajectory.svelte +43 -15
- package/dist/trajectory/TrajectoryInfoPane.svelte +2 -2
- package/dist/trajectory/extract.js +1 -1
- package/dist/trajectory/frame-reader.js +4 -4
- package/dist/trajectory/helpers.d.ts +5 -4
- package/dist/trajectory/helpers.js +9 -17
- package/dist/trajectory/index.d.ts +2 -2
- package/dist/trajectory/index.js +2 -2
- package/dist/trajectory/parse/ase.js +4 -4
- package/dist/trajectory/parse/hdf5.js +1 -1
- package/dist/trajectory/parse/index.js +2 -3
- package/dist/trajectory/parse/lammps.js +1 -1
- package/dist/trajectory/parse/vasp.js +1 -1
- package/dist/trajectory/plotting.d.ts +1 -1
- package/dist/trajectory/plotting.js +38 -38
- package/dist/trajectory/types.d.ts +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +9 -0
- package/dist/xrd/calc-xrd.js +3 -4
- package/dist/xrd/parse.js +1 -1
- package/package.json +42 -22
|
@@ -2,17 +2,31 @@
|
|
|
2
2
|
import { element_data } from '../element';
|
|
3
3
|
import * as math from '../math';
|
|
4
4
|
const element_lookup = new Map(element_data.map((el) => [el.symbol, el]));
|
|
5
|
-
const covalent_radii = new Map(element_data
|
|
6
|
-
.filter((el) => el.covalent_radius !== null)
|
|
7
|
-
.map((el) => [el.symbol, el.covalent_radius]));
|
|
5
|
+
const covalent_radii = new Map(element_data.flatMap((el) => el.covalent_radius === null ? [] : [[el.symbol, el.covalent_radius]]));
|
|
8
6
|
const is_zero_cell_shift = (cell_shift) => cell_shift === undefined || cell_shift.every((val) => val === 0);
|
|
9
7
|
const format_cell_shift = (cell_shift) => {
|
|
10
8
|
if (cell_shift === undefined || is_zero_cell_shift(cell_shift))
|
|
11
9
|
return ``;
|
|
12
10
|
return `@${cell_shift.join(`,`)}`;
|
|
13
11
|
};
|
|
14
|
-
const negate_cell_shift = (cell_shift) =>
|
|
12
|
+
const negate_cell_shift = (cell_shift) => [
|
|
13
|
+
cell_shift[0] === 0 ? 0 : -cell_shift[0],
|
|
14
|
+
cell_shift[1] === 0 ? 0 : -cell_shift[1],
|
|
15
|
+
cell_shift[2] === 0 ? 0 : -cell_shift[2],
|
|
16
|
+
];
|
|
17
|
+
const canonical_self_bond_shift = (cell_shift) => {
|
|
18
|
+
const first_non_zero = cell_shift.find((val) => val !== 0);
|
|
19
|
+
return first_non_zero !== undefined && first_non_zero < 0
|
|
20
|
+
? negate_cell_shift(cell_shift)
|
|
21
|
+
: cell_shift;
|
|
22
|
+
};
|
|
15
23
|
const normalize_bond_endpoints = (site_idx_1, site_idx_2, cell_shift) => {
|
|
24
|
+
if (site_idx_1 === site_idx_2) {
|
|
25
|
+
const ordered = { site_idx_1, site_idx_2 };
|
|
26
|
+
if (cell_shift === undefined || is_zero_cell_shift(cell_shift))
|
|
27
|
+
return ordered;
|
|
28
|
+
return { ...ordered, cell_shift: canonical_self_bond_shift(cell_shift) };
|
|
29
|
+
}
|
|
16
30
|
const ordered = site_idx_1 < site_idx_2
|
|
17
31
|
? { site_idx_1, site_idx_2 }
|
|
18
32
|
: { site_idx_1: site_idx_2, site_idx_2: site_idx_1 };
|
|
@@ -31,17 +45,174 @@ export const get_bond_key = (idx_1, idx_2, cell_shift) => {
|
|
|
31
45
|
const normalized = normalize_bond_endpoints(idx_1, idx_2, cell_shift);
|
|
32
46
|
return `${normalized.site_idx_1}-${normalized.site_idx_2}${format_cell_shift(normalized.cell_shift)}`;
|
|
33
47
|
};
|
|
48
|
+
export const BOND_ORDER_OPTIONS = [
|
|
49
|
+
{ order: 1, label: `Single` },
|
|
50
|
+
{ order: 1.5, label: `1.5` },
|
|
51
|
+
{ order: 2, label: `Double` },
|
|
52
|
+
{ order: 3, label: `Triple` },
|
|
53
|
+
{ order: `aromatic`, label: `Aromatic` },
|
|
54
|
+
];
|
|
55
|
+
const site_image_shift = (sites, site_idx) => {
|
|
56
|
+
const site = sites?.[site_idx];
|
|
57
|
+
const orig_site_idx = site?.properties?.orig_site_idx;
|
|
58
|
+
if (typeof orig_site_idx !== `number`)
|
|
59
|
+
return [0, 0, 0];
|
|
60
|
+
const orig_site = sites?.[orig_site_idx];
|
|
61
|
+
if (!site?.abc || !orig_site?.abc)
|
|
62
|
+
return [0, 0, 0];
|
|
63
|
+
return [
|
|
64
|
+
Math.round(site.abc[0] - orig_site.abc[0]),
|
|
65
|
+
Math.round(site.abc[1] - orig_site.abc[1]),
|
|
66
|
+
Math.round(site.abc[2] - orig_site.abc[2]),
|
|
67
|
+
];
|
|
68
|
+
};
|
|
69
|
+
const original_site_idx = (sites, site_idx) => {
|
|
70
|
+
const orig_site_idx = sites?.[site_idx]?.properties?.orig_site_idx;
|
|
71
|
+
return typeof orig_site_idx === `number` ? orig_site_idx : site_idx;
|
|
72
|
+
};
|
|
73
|
+
export const canonicalize_bond_target = (bond, sites) => {
|
|
74
|
+
const shift_1 = site_image_shift(sites, bond.site_idx_1);
|
|
75
|
+
const shift_2 = site_image_shift(sites, bond.site_idx_2);
|
|
76
|
+
const base_shift = bond.cell_shift ?? [0, 0, 0];
|
|
77
|
+
const cell_shift = [
|
|
78
|
+
base_shift[0] + shift_2[0] - shift_1[0],
|
|
79
|
+
base_shift[1] + shift_2[1] - shift_1[1],
|
|
80
|
+
base_shift[2] + shift_2[2] - shift_1[2],
|
|
81
|
+
];
|
|
82
|
+
return normalize_bond_endpoints(original_site_idx(sites, bond.site_idx_1), original_site_idx(sites, bond.site_idx_2), cell_shift);
|
|
83
|
+
};
|
|
84
|
+
const bond_key_for = (bond) => get_bond_key(bond.site_idx_1, bond.site_idx_2, bond.cell_shift);
|
|
85
|
+
const matches_bond_key = (bond, key) => bond_key_for(bond) === key;
|
|
86
|
+
const replace_bond = (bonds, next_bond) => {
|
|
87
|
+
const key = bond_key_for(next_bond);
|
|
88
|
+
return [...bonds.filter((bond) => !matches_bond_key(bond, key)), next_bond];
|
|
89
|
+
};
|
|
90
|
+
const remove_bond_key = (bonds, key) => bonds.filter((bond) => !matches_bond_key(bond, key));
|
|
91
|
+
const includes_bond_key = (bonds, key) => bonds.some((bond) => matches_bond_key(bond, key));
|
|
92
|
+
const get_bond_order = (bond) => bond?.bond_order ?? bond?.order ?? 1;
|
|
93
|
+
const find_bond_by_key = (bonds, key) => bonds.find((bond) => matches_bond_key(bond, key));
|
|
94
|
+
const make_bond_record = (bond, order) => normalize_structure_bond(bond.site_idx_1, bond.site_idx_2, order, bond.cell_shift);
|
|
95
|
+
export function has_visible_bond(edit_state, bond, calculated_bonds) {
|
|
96
|
+
const key = bond_key_for(bond);
|
|
97
|
+
if (includes_bond_key(edit_state.removed_bonds, key)) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
if (includes_bond_key(edit_state.added_bonds, key))
|
|
101
|
+
return true;
|
|
102
|
+
return includes_bond_key(calculated_bonds, key);
|
|
103
|
+
}
|
|
104
|
+
export function add_or_restore_bond(edit_state, bond, calculated_bonds, order) {
|
|
105
|
+
const record = make_bond_record(bond, order);
|
|
106
|
+
const key = bond_key_for(record);
|
|
107
|
+
const removed_bond = find_bond_by_key(edit_state.removed_bonds, key);
|
|
108
|
+
if (removed_bond) {
|
|
109
|
+
return {
|
|
110
|
+
action: `restored`,
|
|
111
|
+
changed: true,
|
|
112
|
+
state: {
|
|
113
|
+
...edit_state,
|
|
114
|
+
added_bonds: remove_bond_key(edit_state.added_bonds, key),
|
|
115
|
+
removed_bonds: remove_bond_key(edit_state.removed_bonds, key),
|
|
116
|
+
bond_order_overrides: removed_bond.order === order
|
|
117
|
+
? remove_bond_key(edit_state.bond_order_overrides, key)
|
|
118
|
+
: replace_bond(edit_state.bond_order_overrides, record),
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (has_visible_bond(edit_state, record, calculated_bonds)) {
|
|
123
|
+
return { action: `already-visible`, changed: false, state: edit_state };
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
action: `added`,
|
|
127
|
+
changed: true,
|
|
128
|
+
state: {
|
|
129
|
+
...edit_state,
|
|
130
|
+
added_bonds: replace_bond(edit_state.added_bonds, record),
|
|
131
|
+
bond_order_overrides: remove_bond_key(edit_state.bond_order_overrides, key),
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
export function delete_bond(edit_state, bond, calculated_bonds) {
|
|
136
|
+
const record = make_bond_record(bond, 1);
|
|
137
|
+
const key = bond_key_for(record);
|
|
138
|
+
if (includes_bond_key(edit_state.added_bonds, key)) {
|
|
139
|
+
return {
|
|
140
|
+
action: `deleted-added`,
|
|
141
|
+
changed: true,
|
|
142
|
+
state: {
|
|
143
|
+
...edit_state,
|
|
144
|
+
added_bonds: remove_bond_key(edit_state.added_bonds, key),
|
|
145
|
+
bond_order_overrides: remove_bond_key(edit_state.bond_order_overrides, key),
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const calculated = find_bond_by_key(calculated_bonds, key);
|
|
150
|
+
if (!calculated || includes_bond_key(edit_state.removed_bonds, key)) {
|
|
151
|
+
return { action: `not-visible`, changed: false, state: edit_state };
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
action: `deleted-calculated`,
|
|
155
|
+
changed: true,
|
|
156
|
+
state: {
|
|
157
|
+
...edit_state,
|
|
158
|
+
removed_bonds: replace_bond(edit_state.removed_bonds, {
|
|
159
|
+
...record,
|
|
160
|
+
order: get_bond_order(calculated),
|
|
161
|
+
}),
|
|
162
|
+
bond_order_overrides: remove_bond_key(edit_state.bond_order_overrides, key),
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
export function set_bond_order(edit_state, bond, calculated_bonds, order) {
|
|
167
|
+
const record = make_bond_record(bond, order);
|
|
168
|
+
const key = bond_key_for(record);
|
|
169
|
+
const calculated = find_bond_by_key(calculated_bonds, key);
|
|
170
|
+
if (calculated) {
|
|
171
|
+
const visible_order = get_bond_order(calculated);
|
|
172
|
+
const has_existing_edit = includes_bond_key(edit_state.added_bonds, key) ||
|
|
173
|
+
includes_bond_key(edit_state.removed_bonds, key) ||
|
|
174
|
+
includes_bond_key(edit_state.bond_order_overrides, key);
|
|
175
|
+
const next_overrides = order === visible_order
|
|
176
|
+
? remove_bond_key(edit_state.bond_order_overrides, key)
|
|
177
|
+
: replace_bond(edit_state.bond_order_overrides, record);
|
|
178
|
+
const next_state = {
|
|
179
|
+
added_bonds: remove_bond_key(edit_state.added_bonds, key),
|
|
180
|
+
removed_bonds: remove_bond_key(edit_state.removed_bonds, key),
|
|
181
|
+
bond_order_overrides: next_overrides,
|
|
182
|
+
};
|
|
183
|
+
return {
|
|
184
|
+
action: `ordered-calculated`,
|
|
185
|
+
changed: has_existing_edit || order !== visible_order,
|
|
186
|
+
state: next_state,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
action: `ordered-added`,
|
|
191
|
+
changed: true,
|
|
192
|
+
state: {
|
|
193
|
+
...edit_state,
|
|
194
|
+
added_bonds: replace_bond(edit_state.added_bonds, record),
|
|
195
|
+
bond_order_overrides: remove_bond_key(edit_state.bond_order_overrides, key),
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
34
199
|
export const merge_bond_edits = (base_bonds, added, removed, overrides) => {
|
|
35
200
|
const key_for = (bond) => get_bond_key(bond.site_idx_1, bond.site_idx_2, bond.cell_shift);
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
201
|
+
const normalize_record = (bond) => normalize_structure_bond(bond.site_idx_1, bond.site_idx_2, bond.order, bond.cell_shift);
|
|
202
|
+
const removed_keys = new Set(removed.map(key_for));
|
|
203
|
+
const merged = new Map(base_bonds
|
|
204
|
+
.filter((bond) => !removed_keys.has(key_for(bond)))
|
|
205
|
+
.map((bond) => [key_for(bond), normalize_record(bond)]));
|
|
39
206
|
// Apply additions before overrides so user-set bond orders win even if
|
|
40
207
|
// callers accidentally pass overlapping edit lists.
|
|
41
|
-
for (const bond of added)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
208
|
+
for (const bond of added) {
|
|
209
|
+
if (!removed_keys.has(key_for(bond)))
|
|
210
|
+
merged.set(key_for(bond), normalize_record(bond));
|
|
211
|
+
}
|
|
212
|
+
for (const bond of overrides) {
|
|
213
|
+
if (!removed_keys.has(key_for(bond)))
|
|
214
|
+
merged.set(key_for(bond), normalize_record(bond));
|
|
215
|
+
}
|
|
45
216
|
return [...merged.values()];
|
|
46
217
|
};
|
|
47
218
|
const is_record = (value) => typeof value === `object` && value !== null;
|
|
@@ -59,7 +230,7 @@ function normalize_cell_shift(cell_shift) {
|
|
|
59
230
|
return null;
|
|
60
231
|
return cell_shift.some((val) => typeof val !== `number` || !Number.isInteger(val))
|
|
61
232
|
? null
|
|
62
|
-
: cell_shift;
|
|
233
|
+
: [cell_shift[0], cell_shift[1], cell_shift[2]];
|
|
63
234
|
}
|
|
64
235
|
function lattice_translation(structure, cell_shift) {
|
|
65
236
|
if (cell_shift === undefined || is_zero_cell_shift(cell_shift))
|
|
@@ -123,10 +294,6 @@ export function get_explicit_bond_metadata(structure) {
|
|
|
123
294
|
console.warn(`Ignoring invalid explicit bond at index ${entry_idx}: site indices ${site_idx_1}, ${site_idx_2} are out of range for ${structure.sites.length} sites`);
|
|
124
295
|
continue;
|
|
125
296
|
}
|
|
126
|
-
if (site_idx_1 === site_idx_2) {
|
|
127
|
-
console.warn(`Ignoring invalid explicit bond at index ${entry_idx}: endpoints match`);
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
297
|
const bond_order = normalize_bond_order(order);
|
|
131
298
|
if (bond_order === null) {
|
|
132
299
|
console.warn(`Ignoring invalid explicit bond at index ${entry_idx}: unsupported order ${String(order)}`);
|
|
@@ -137,6 +304,10 @@ export function get_explicit_bond_metadata(structure) {
|
|
|
137
304
|
console.warn(`Ignoring invalid explicit bond at index ${entry_idx}: cell_shift must be three integers`);
|
|
138
305
|
continue;
|
|
139
306
|
}
|
|
307
|
+
if (site_idx_1 === site_idx_2 && is_zero_cell_shift(cell_shift)) {
|
|
308
|
+
console.warn(`Ignoring invalid explicit bond at index ${entry_idx}: endpoints match`);
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
140
311
|
if (cell_shift !== undefined &&
|
|
141
312
|
!is_zero_cell_shift(cell_shift) &&
|
|
142
313
|
!(`lattice` in structure)) {
|
package/dist/structure/export.js
CHANGED
|
@@ -164,7 +164,7 @@ function convert_instanced_meshes_to_regular(scene) {
|
|
|
164
164
|
// Check if this is a shader material (bonds)
|
|
165
165
|
const mat = instanced_mesh.material;
|
|
166
166
|
const is_shader = Array.isArray(mat)
|
|
167
|
-
? mat.some((
|
|
167
|
+
? mat.some((material) => material instanceof ShaderMaterial)
|
|
168
168
|
: mat instanceof ShaderMaterial;
|
|
169
169
|
if (is_shader) {
|
|
170
170
|
// Extract bond colors for each instance from geometry attributes
|
|
@@ -3,9 +3,9 @@ import type { ElementSymbol } from '../element';
|
|
|
3
3
|
import type { Vec3 } from '../math';
|
|
4
4
|
import * as math from '../math';
|
|
5
5
|
import type { ComponentProps } from 'svelte';
|
|
6
|
-
import LatticeComponent from './Lattice.svelte';
|
|
6
|
+
import type LatticeComponent from './Lattice.svelte';
|
|
7
7
|
import type { Pbc } from './pbc';
|
|
8
|
-
import StructureSceneComponent from './StructureScene.svelte';
|
|
8
|
+
import type StructureSceneComponent from './StructureScene.svelte';
|
|
9
9
|
export { default as Arrow } from './Arrow.svelte';
|
|
10
10
|
export * from './atom-properties';
|
|
11
11
|
export { default as AtomLegend } from './AtomLegend.svelte';
|
|
@@ -22,6 +22,7 @@ export { default as StructureInfoPane } from './StructureInfoPane.svelte';
|
|
|
22
22
|
export { default as StructureScene } from './StructureScene.svelte';
|
|
23
23
|
export * from './supercell';
|
|
24
24
|
export type MeasureMode = `distance` | `angle` | `edit-bonds` | `edit-atoms`;
|
|
25
|
+
export type BondEditMode = `add` | `delete`;
|
|
25
26
|
export type Species = {
|
|
26
27
|
element: ElementSymbol;
|
|
27
28
|
occu: number;
|
package/dist/structure/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { ATOMIC_WEIGHTS } from '../composition/parse';
|
|
2
2
|
import { element_data } from '../element';
|
|
3
3
|
import * as math from '../math';
|
|
4
|
-
import LatticeComponent from './Lattice.svelte';
|
|
5
|
-
import StructureSceneComponent from './StructureScene.svelte';
|
|
6
4
|
export { default as Arrow } from './Arrow.svelte';
|
|
7
5
|
export * from './atom-properties';
|
|
8
6
|
export { default as AtomLegend } from './AtomLegend.svelte';
|
package/dist/structure/parse.js
CHANGED
|
@@ -4,6 +4,26 @@ import * as math from '../math';
|
|
|
4
4
|
import { wrap_to_unit_cell } from './pbc';
|
|
5
5
|
import { normalize_scientific_notation } from '../utils';
|
|
6
6
|
import { load as yaml_load } from 'js-yaml';
|
|
7
|
+
const cif_coords_key = (coords) => `${coords[0].toFixed(6)},${coords[1].toFixed(6)},${coords[2].toFixed(6)}`;
|
|
8
|
+
const cif_site_key = (element, abc, label) => `${element}|${label}|${cif_coords_key(abc)}`;
|
|
9
|
+
const clone_structure_properties = (properties) => structuredClone(properties);
|
|
10
|
+
const FALLBACK_ELEMENTS = [`H`, `He`, `Li`, `Be`, `B`, `C`, `N`, `O`, `F`, `Ne`];
|
|
11
|
+
const is_known_element_symbol = (symbol) => ELEM_SYMBOLS.includes(symbol);
|
|
12
|
+
const vec3_from_values = (values, context) => {
|
|
13
|
+
if (values?.length !== 3) {
|
|
14
|
+
throw new Error(`Invalid ${context}: expected 3 coordinates, got ${values?.length ?? 0}`);
|
|
15
|
+
}
|
|
16
|
+
const coords = math.finite_vec3_from_values(values);
|
|
17
|
+
if (coords)
|
|
18
|
+
return coords;
|
|
19
|
+
for (let idx = 0; idx < 3; idx++) {
|
|
20
|
+
const value = values[idx];
|
|
21
|
+
if (typeof value !== `number` || !Number.isFinite(value)) {
|
|
22
|
+
throw new TypeError(`Invalid ${context}: coordinate ${idx} must be finite, got ${String(value)}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
throw new Error(`Invalid ${context}: expected 3 finite coordinates`);
|
|
26
|
+
};
|
|
7
27
|
// Parse a coordinate value that might be in various scientific notation formats
|
|
8
28
|
function parse_coordinate(str) {
|
|
9
29
|
const normalized = normalize_scientific_notation(str.trim());
|
|
@@ -34,12 +54,10 @@ function parse_coordinate_line(line) {
|
|
|
34
54
|
function validate_element_symbol(symbol, index) {
|
|
35
55
|
// Clean symbol (remove suffixes like _pv, /hash)
|
|
36
56
|
const clean_symbol = symbol.split(/[_/]/)[0];
|
|
37
|
-
if (
|
|
57
|
+
if (is_known_element_symbol(clean_symbol))
|
|
38
58
|
return clean_symbol;
|
|
39
|
-
}
|
|
40
59
|
// Fallback to default elements by atomic number
|
|
41
|
-
const
|
|
42
|
-
const fallback = fallback_elements[index % fallback_elements.length];
|
|
60
|
+
const fallback = FALLBACK_ELEMENTS[index % FALLBACK_ELEMENTS.length] ?? `H`;
|
|
43
61
|
console.warn(`Invalid element symbol '${symbol}', using fallback '${fallback}'`);
|
|
44
62
|
return fallback;
|
|
45
63
|
}
|
|
@@ -73,10 +91,7 @@ export function parse_poscar(content) {
|
|
|
73
91
|
// Parse lattice vectors (lines 3-5)
|
|
74
92
|
const parse_vector = (line, line_num) => {
|
|
75
93
|
const coords = line.trim().split(/\s+/).map(parse_coordinate);
|
|
76
|
-
|
|
77
|
-
throw new Error(`Invalid lattice vector on line ${line_num}: expected 3 coordinates, got ${coords.length}`);
|
|
78
|
-
}
|
|
79
|
-
return coords;
|
|
94
|
+
return vec3_from_values(coords, `lattice vector on line ${line_num}`);
|
|
80
95
|
};
|
|
81
96
|
const lattice_vecs = [
|
|
82
97
|
parse_vector(lines[2], 3),
|
|
@@ -168,7 +183,11 @@ export function parse_poscar(content) {
|
|
|
168
183
|
return null;
|
|
169
184
|
}
|
|
170
185
|
// Parse atomic positions
|
|
171
|
-
const poscar_axis_lengths =
|
|
186
|
+
const poscar_axis_lengths = [
|
|
187
|
+
Math.hypot(...scaled_lattice[0]),
|
|
188
|
+
Math.hypot(...scaled_lattice[1]),
|
|
189
|
+
Math.hypot(...scaled_lattice[2]),
|
|
190
|
+
];
|
|
172
191
|
const poscar_frac_to_cart = math.create_frac_to_cart(scaled_lattice);
|
|
173
192
|
const poscar_cart_to_frac = try_create_cart_to_frac(scaled_lattice);
|
|
174
193
|
if (!is_direct && !poscar_cart_to_frac) {
|
|
@@ -185,7 +204,7 @@ export function parse_poscar(content) {
|
|
|
185
204
|
console.error(`Not enough coordinate lines in POSCAR`);
|
|
186
205
|
return null;
|
|
187
206
|
}
|
|
188
|
-
const coords = parse_coordinate_line(lines[coord_line_idx]);
|
|
207
|
+
const coords = vec3_from_values(parse_coordinate_line(lines[coord_line_idx]), `POSCAR atom coordinates on line ${coord_line_idx + 1}`);
|
|
189
208
|
// Parse selective dynamics if present
|
|
190
209
|
let selective_dynamics;
|
|
191
210
|
if (has_selective_dynamics) {
|
|
@@ -196,15 +215,14 @@ export function parse_poscar(content) {
|
|
|
196
215
|
}
|
|
197
216
|
let xyz;
|
|
198
217
|
let abc;
|
|
199
|
-
const [x, y, z] = coords;
|
|
200
218
|
if (is_direct) {
|
|
201
219
|
// Store fractional coordinates, wrapping to [0, 1) range
|
|
202
|
-
abc = wrap_to_unit_cell(
|
|
220
|
+
abc = wrap_to_unit_cell(coords);
|
|
203
221
|
xyz = poscar_frac_to_cart(abc);
|
|
204
222
|
}
|
|
205
223
|
else {
|
|
206
224
|
// Already Cartesian, scale if needed
|
|
207
|
-
xyz = math.scale(
|
|
225
|
+
xyz = math.scale(coords, scale_factor);
|
|
208
226
|
const raw_abc = poscar_cart_to_frac
|
|
209
227
|
? poscar_cart_to_frac(xyz)
|
|
210
228
|
: approximate_cart_to_frac(xyz, poscar_axis_lengths);
|
|
@@ -288,9 +306,9 @@ export function parse_xyz(content) {
|
|
|
288
306
|
const lattice_values = lattice_match[1].split(/\s+/).map(parse_coordinate);
|
|
289
307
|
if (lattice_values.length === 9) {
|
|
290
308
|
const lattice_vectors = [
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
309
|
+
vec3_from_values(lattice_values.slice(0, 3), `XYZ lattice vector 1`),
|
|
310
|
+
vec3_from_values(lattice_values.slice(3, 6), `XYZ lattice vector 2`),
|
|
311
|
+
vec3_from_values(lattice_values.slice(6, 9), `XYZ lattice vector 3`),
|
|
294
312
|
];
|
|
295
313
|
const lattice_params = math.calc_lattice_params(lattice_vectors);
|
|
296
314
|
lattice = { matrix: lattice_vectors, ...lattice_params };
|
|
@@ -317,13 +335,7 @@ export function parse_xyz(content) {
|
|
|
317
335
|
return null;
|
|
318
336
|
}
|
|
319
337
|
const element = validate_element_symbol(parts[0], atom_idx);
|
|
320
|
-
const
|
|
321
|
-
parse_coordinate(parts[1]),
|
|
322
|
-
parse_coordinate(parts[2]),
|
|
323
|
-
parse_coordinate(parts[3]),
|
|
324
|
-
];
|
|
325
|
-
// For XYZ files, coordinates are typically in Cartesian
|
|
326
|
-
const xyz = [coords[0], coords[1], coords[2]];
|
|
338
|
+
const xyz = vec3_from_values(parts.slice(1, 4).map(parse_coordinate), `XYZ atom position ${atom_idx + 1}`);
|
|
327
339
|
// Calculate fractional coordinates if lattice is available
|
|
328
340
|
let abc = [0, 0, 0];
|
|
329
341
|
if (lattice && xyz_frac_to_cart && xyz_axis_lengths) {
|
|
@@ -430,10 +442,9 @@ const apply_symmetry_ops = (atom, symmetry_ops, wrap_fractional_coords) => {
|
|
|
430
442
|
const wrap = (coords) => wrap_fractional_coords ? wrap_to_unit_cell(coords) : coords;
|
|
431
443
|
// Use 6 decimal places for deduplication to handle floating point imprecision
|
|
432
444
|
// from compound symmetry operations like x-y, -x+y which can produce small errors
|
|
433
|
-
const key = (coords) => `${coords[0].toFixed(6)},${coords[1].toFixed(6)},${coords[2].toFixed(6)}`;
|
|
434
445
|
// Always include base atom (optionally wrapped)
|
|
435
446
|
const base_coords = wrap(atom.coords);
|
|
436
|
-
seen.add(
|
|
447
|
+
seen.add(cif_coords_key(base_coords));
|
|
437
448
|
equivalent_atoms.push({ ...atom, coords: base_coords });
|
|
438
449
|
for (const operation of symmetry_ops) {
|
|
439
450
|
const operation_match = /['"]([^'"]+)['"]/.exec(operation);
|
|
@@ -453,7 +464,7 @@ const apply_symmetry_ops = (atom, symmetry_ops, wrap_fractional_coords) => {
|
|
|
453
464
|
}
|
|
454
465
|
// Wrap and deduplicate transformed coordinates
|
|
455
466
|
const wrapped = wrap(new_coords);
|
|
456
|
-
const cache_key =
|
|
467
|
+
const cache_key = cif_coords_key(wrapped);
|
|
457
468
|
if (seen.has(cache_key))
|
|
458
469
|
continue;
|
|
459
470
|
seen.add(cache_key);
|
|
@@ -516,7 +527,7 @@ const parse_cif_atom_data = (raw_data, indices, coords_type) => {
|
|
|
516
527
|
if (coord_indices.some((idx) => idx === undefined)) {
|
|
517
528
|
throw new Error(`Missing coordinate indices`);
|
|
518
529
|
}
|
|
519
|
-
const coords_triplet = coord_indices.map((idx) => {
|
|
530
|
+
const coords_triplet = vec3_from_values(coord_indices.map((idx) => {
|
|
520
531
|
if (idx === undefined)
|
|
521
532
|
throw new Error(`Invalid coordinate index`);
|
|
522
533
|
const coord_str = raw_data[idx];
|
|
@@ -526,7 +537,7 @@ const parse_cif_atom_data = (raw_data, indices, coords_type) => {
|
|
|
526
537
|
if (isNaN(coord))
|
|
527
538
|
throw new Error(`Invalid coordinate: ${coord_str}`);
|
|
528
539
|
return coord;
|
|
529
|
-
});
|
|
540
|
+
}), `CIF atom coordinates`);
|
|
530
541
|
const occu = occupancy >= 0 && raw_data[occupancy]
|
|
531
542
|
? parseFloat(raw_data[occupancy].split(`(`)[0]) || 1.0
|
|
532
543
|
: 1.0;
|
|
@@ -750,7 +761,6 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
750
761
|
// Global deduplication of final sites (per element + coordinates + label)
|
|
751
762
|
// Use 6 decimal places to handle floating point imprecision from compound symmetry ops
|
|
752
763
|
const seen_site_keys = new Set();
|
|
753
|
-
const site_key = (element, abc, label) => `${element}|${label}|${abc[0].toFixed(6)},${abc[1].toFixed(6)},${abc[2].toFixed(6)}`;
|
|
754
764
|
for (const atom of atoms) {
|
|
755
765
|
const element = validate_element_symbol(atom.element, all_sites.length);
|
|
756
766
|
// Convert to fractional coordinates if needed
|
|
@@ -779,7 +789,7 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
779
789
|
equiv_atom.coords[1] + cv[1],
|
|
780
790
|
equiv_atom.coords[2] + cv[2],
|
|
781
791
|
]);
|
|
782
|
-
const key =
|
|
792
|
+
const key = cif_site_key(element, abc, equiv_atom.id);
|
|
783
793
|
if (seen_site_keys.has(key))
|
|
784
794
|
continue;
|
|
785
795
|
seen_site_keys.add(key);
|
|
@@ -806,12 +816,16 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
806
816
|
function convert_phonopy_cell(cell) {
|
|
807
817
|
const sites = [];
|
|
808
818
|
// Phonopy stores lattice vectors as rows, use them directly
|
|
809
|
-
const lattice_matrix =
|
|
819
|
+
const lattice_matrix = [
|
|
820
|
+
vec3_from_values(cell.lattice[0], `phonopy lattice vector 1`),
|
|
821
|
+
vec3_from_values(cell.lattice[1], `phonopy lattice vector 2`),
|
|
822
|
+
vec3_from_values(cell.lattice[2], `phonopy lattice vector 3`),
|
|
823
|
+
];
|
|
810
824
|
// Process each atomic site
|
|
811
825
|
const phonopy_frac_to_cart = math.create_frac_to_cart(lattice_matrix);
|
|
812
826
|
for (const point of cell.points) {
|
|
813
827
|
const element = validate_element_symbol(point.symbol, sites.length);
|
|
814
|
-
const abc =
|
|
828
|
+
const abc = vec3_from_values(point.coordinates, `phonopy point coordinates`);
|
|
815
829
|
const xyz = phonopy_frac_to_cart(abc);
|
|
816
830
|
const properties = {
|
|
817
831
|
mass: point.mass,
|
|
@@ -825,6 +839,19 @@ function convert_phonopy_cell(cell) {
|
|
|
825
839
|
const calculated_lattice_params = math.calc_lattice_params(lattice_matrix);
|
|
826
840
|
return { sites, lattice: { matrix: lattice_matrix, ...calculated_lattice_params } };
|
|
827
841
|
}
|
|
842
|
+
const is_phonopy_cell = (value) => {
|
|
843
|
+
if (!value || typeof value !== `object`)
|
|
844
|
+
return false;
|
|
845
|
+
const lattice = `lattice` in value ? value.lattice : undefined;
|
|
846
|
+
const points = `points` in value ? value.points : undefined;
|
|
847
|
+
return Array.isArray(lattice) && Array.isArray(points);
|
|
848
|
+
};
|
|
849
|
+
const get_phonopy_cell = (data, cell_type) => {
|
|
850
|
+
if (!data || typeof data !== `object`)
|
|
851
|
+
return undefined;
|
|
852
|
+
const cell = Reflect.get(data, cell_type);
|
|
853
|
+
return is_phonopy_cell(cell) ? cell : undefined;
|
|
854
|
+
};
|
|
828
855
|
// Parse phonopy YAML file and return the requested cell type (or preferred single structure)
|
|
829
856
|
export function parse_phonopy_yaml(content, cell_type) {
|
|
830
857
|
try {
|
|
@@ -857,7 +884,7 @@ export function parse_phonopy_yaml(content, cell_type) {
|
|
|
857
884
|
}
|
|
858
885
|
// If specific cell type requested, parse only that one
|
|
859
886
|
if (cell_type && cell_type !== `auto`) {
|
|
860
|
-
const cell = data
|
|
887
|
+
const cell = get_phonopy_cell(data, cell_type);
|
|
861
888
|
if (cell)
|
|
862
889
|
return convert_phonopy_cell(cell);
|
|
863
890
|
else {
|
|
@@ -871,17 +898,13 @@ export function parse_phonopy_yaml(content, cell_type) {
|
|
|
871
898
|
// 3. unit_cell
|
|
872
899
|
// 4. phonon_primitive_cell
|
|
873
900
|
// 5. primitive_cell
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
return convert_phonopy_cell(data.phonon_primitive_cell);
|
|
882
|
-
}
|
|
883
|
-
else if (data.primitive_cell)
|
|
884
|
-
return convert_phonopy_cell(data.primitive_cell);
|
|
901
|
+
const auto_cell = get_phonopy_cell(data, `supercell`) ??
|
|
902
|
+
get_phonopy_cell(data, `phonon_supercell`) ??
|
|
903
|
+
get_phonopy_cell(data, `unit_cell`) ??
|
|
904
|
+
get_phonopy_cell(data, `phonon_primitive_cell`) ??
|
|
905
|
+
get_phonopy_cell(data, `primitive_cell`);
|
|
906
|
+
if (auto_cell)
|
|
907
|
+
return convert_phonopy_cell(auto_cell);
|
|
885
908
|
console.error(`No valid cells found in phonopy YAML`);
|
|
886
909
|
return null;
|
|
887
910
|
}
|
|
@@ -924,17 +947,15 @@ function find_structure_in_json(obj, visited = new WeakSet()) {
|
|
|
924
947
|
function is_parsed_structure(obj) {
|
|
925
948
|
if (!obj || typeof obj !== `object`)
|
|
926
949
|
return false;
|
|
927
|
-
const
|
|
928
|
-
const sites = parsed_obj.sites;
|
|
950
|
+
const sites = `sites` in obj ? obj.sites : undefined;
|
|
929
951
|
if (!Array.isArray(sites) || sites.length === 0)
|
|
930
952
|
return false;
|
|
931
953
|
const first_site = sites[0];
|
|
932
954
|
if (!first_site || typeof first_site !== `object`)
|
|
933
955
|
return false;
|
|
934
|
-
const
|
|
935
|
-
const
|
|
936
|
-
const
|
|
937
|
-
const xyz = first_site_obj.xyz;
|
|
956
|
+
const species = `species` in first_site ? first_site.species : undefined;
|
|
957
|
+
const abc = `abc` in first_site ? first_site.abc : undefined;
|
|
958
|
+
const xyz = `xyz` in first_site ? first_site.xyz : undefined;
|
|
938
959
|
const has_species = Array.isArray(species) && species.length > 0;
|
|
939
960
|
const has_coords = Array.isArray(abc) || Array.isArray(xyz);
|
|
940
961
|
return has_species && has_coords;
|
|
@@ -1082,7 +1103,6 @@ export function parse_structure_file(content, filename) {
|
|
|
1082
1103
|
}
|
|
1083
1104
|
// Universal parser that handles JSON and structure files
|
|
1084
1105
|
export function parse_any_structure(content, filename) {
|
|
1085
|
-
const clone_structure_properties = (properties) => structuredClone(properties);
|
|
1086
1106
|
const finalize_structure = (structure) => ({
|
|
1087
1107
|
sites: structure.sites,
|
|
1088
1108
|
charge: 0,
|
|
@@ -1145,7 +1165,13 @@ export function parse_optimade_from_raw(raw) {
|
|
|
1145
1165
|
const positions = positions_raw;
|
|
1146
1166
|
const species = species_raw;
|
|
1147
1167
|
// Optimade stores lattice vectors as rows, so use as is
|
|
1148
|
-
const lattice_matrix = attrs.lattice_vectors
|
|
1168
|
+
const lattice_matrix = attrs.lattice_vectors
|
|
1169
|
+
? [
|
|
1170
|
+
vec3_from_values(attrs.lattice_vectors[0], `OPTIMADE lattice vector 1`),
|
|
1171
|
+
vec3_from_values(attrs.lattice_vectors[1], `OPTIMADE lattice vector 2`),
|
|
1172
|
+
vec3_from_values(attrs.lattice_vectors[2], `OPTIMADE lattice vector 3`),
|
|
1173
|
+
]
|
|
1174
|
+
: undefined;
|
|
1149
1175
|
const optimade_lattice_params = lattice_matrix
|
|
1150
1176
|
? math.calc_lattice_params(lattice_matrix)
|
|
1151
1177
|
: null;
|
|
@@ -1168,12 +1194,15 @@ export function parse_optimade_from_raw(raw) {
|
|
|
1168
1194
|
for (let idx = 0; idx < positions.length; idx++) {
|
|
1169
1195
|
const pos = positions[idx];
|
|
1170
1196
|
const element_symbol = species[idx];
|
|
1171
|
-
|
|
1172
|
-
|
|
1197
|
+
let xyz;
|
|
1198
|
+
try {
|
|
1199
|
+
xyz = vec3_from_values(pos, `OPTIMADE site ${idx} position`);
|
|
1200
|
+
}
|
|
1201
|
+
catch (error) {
|
|
1202
|
+
console.warn(`Invalid position data at site ${idx}: ${error}`);
|
|
1173
1203
|
continue;
|
|
1174
1204
|
}
|
|
1175
1205
|
const element = validate_element_symbol(element_symbol, idx);
|
|
1176
|
-
const xyz = [pos[0], pos[1], pos[2]];
|
|
1177
1206
|
// Calculate fractional coordinates if lattice is available
|
|
1178
1207
|
const abc = optimade_cart_to_frac ? optimade_cart_to_frac(xyz) : [0, 0, 0];
|
|
1179
1208
|
const site = {
|
|
@@ -1248,9 +1277,9 @@ export function optimade_to_crystal(optimade_structure) {
|
|
|
1248
1277
|
}
|
|
1249
1278
|
try {
|
|
1250
1279
|
const lattice_matrix = [
|
|
1251
|
-
lattice_vectors[0],
|
|
1252
|
-
lattice_vectors[1],
|
|
1253
|
-
lattice_vectors[2],
|
|
1280
|
+
vec3_from_values(lattice_vectors[0], `OPTIMADE lattice vector 1`),
|
|
1281
|
+
vec3_from_values(lattice_vectors[1], `OPTIMADE lattice vector 2`),
|
|
1282
|
+
vec3_from_values(lattice_vectors[2], `OPTIMADE lattice vector 3`),
|
|
1254
1283
|
];
|
|
1255
1284
|
const lattice_params = math.calc_lattice_params(lattice_matrix);
|
|
1256
1285
|
// Build species lookup for site properties (mass, concentration, etc.)
|
|
@@ -1262,7 +1291,7 @@ export function optimade_to_crystal(optimade_structure) {
|
|
|
1262
1291
|
if (!element_symbol)
|
|
1263
1292
|
throw new Error(`Missing species for site ${idx}`);
|
|
1264
1293
|
const element = validate_element_symbol(element_symbol, idx);
|
|
1265
|
-
const xyz =
|
|
1294
|
+
const xyz = vec3_from_values(pos, `OPTIMADE atom position ${idx + 1}`);
|
|
1266
1295
|
const abc = crystal_cart_to_frac ? crystal_cart_to_frac(xyz) : [0, 0, 0];
|
|
1267
1296
|
// Extract mass/concentration from species data
|
|
1268
1297
|
const spec = species_map.get(element_symbol);
|
|
@@ -24,6 +24,13 @@
|
|
|
24
24
|
`${wyckoff_pos.wyckoff}-${wyckoff_pos.elem}-${
|
|
25
25
|
wyckoff_pos.site_indices?.join(`,`) ?? `none`
|
|
26
26
|
}-${row_idx}`
|
|
27
|
+
$effect(() => {
|
|
28
|
+
if (!selected_key || wyckoff_positions?.some((pos, idx) => get_row_key(pos, idx) === selected_key)) {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
selected_key = null
|
|
32
|
+
on_click?.(null)
|
|
33
|
+
})
|
|
27
34
|
</script>
|
|
28
35
|
|
|
29
36
|
{#if wyckoff_positions && wyckoff_positions.length > 0}
|