matterviz 0.3.4 → 0.3.5
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 +1 -1
- package/dist/app.css +7 -0
- package/dist/brillouin/BrillouinZone.svelte +5 -2
- package/dist/brillouin/compute.js +8 -4
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +6 -6
- package/dist/chempot-diagram/async-compute.svelte.js +5 -4
- package/dist/chempot-diagram/chempot-worker.js +2 -2
- package/dist/chempot-diagram/compute.js +16 -16
- package/dist/composition/FormulaFilter.svelte +3 -3
- package/dist/constants.js +2 -8
- package/dist/convex-hull/ConvexHull.svelte +2 -2
- package/dist/convex-hull/ConvexHull2D.svelte +11 -10
- package/dist/convex-hull/ConvexHull3D.svelte +16 -14
- package/dist/convex-hull/ConvexHull4D.svelte +26 -14
- package/dist/convex-hull/ConvexHullControls.svelte +1 -1
- package/dist/convex-hull/ConvexHullInfoPane.svelte +68 -61
- package/dist/convex-hull/ConvexHullStats.svelte +23 -6
- package/dist/convex-hull/GasPressureControls.svelte +3 -3
- package/dist/convex-hull/TemperatureSlider.svelte +1 -1
- package/dist/convex-hull/barycentric-coords.js +2 -2
- package/dist/convex-hull/helpers.js +45 -27
- package/dist/convex-hull/thermodynamics.js +2 -2
- package/dist/element/BohrAtom.svelte +25 -27
- package/dist/element/BohrAtom.svelte.d.ts +2 -2
- package/dist/element/data.d.ts +2 -3
- package/dist/fermi-surface/FermiSurface.svelte +5 -2
- package/dist/fermi-surface/compute.js +3 -3
- package/dist/fermi-surface/parse.js +2 -2
- package/dist/fermi-surface/symmetry.js +1 -1
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +8 -8
- package/dist/icons.d.ts +6 -6
- package/dist/icons.js +6 -6
- package/dist/io/decompress.js +12 -7
- package/dist/io/export.js +20 -16
- package/dist/io/is-binary.js +19 -4
- package/dist/isosurface/parse.js +8 -8
- package/dist/isosurface/types.js +9 -9
- package/dist/layout/InfoTag.svelte +1 -1
- package/dist/layout/json-tree/JsonNode.svelte +1 -0
- package/dist/layout/json-tree/utils.js +2 -1
- package/dist/marching-cubes.js +1 -1
- package/dist/math.js +1 -1
- package/dist/overlays/CopyButton.svelte +45 -0
- package/dist/overlays/CopyButton.svelte.d.ts +8 -0
- package/dist/overlays/InfoPaneCards.svelte +149 -0
- package/dist/overlays/InfoPaneCards.svelte.d.ts +22 -0
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +33 -35
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +2 -2
- package/dist/phase-diagram/PhaseDiagramControls.svelte +27 -29
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +2 -2
- package/dist/phase-diagram/parse.js +3 -3
- package/dist/phase-diagram/svg-to-diagram.js +10 -12
- package/dist/plot/BarPlot.svelte +24 -15
- package/dist/plot/BarPlot.svelte.d.ts +3 -2
- package/dist/plot/FillArea.svelte +2 -3
- package/dist/plot/FillArea.svelte.d.ts +3 -2
- package/dist/plot/Histogram.svelte +37 -19
- package/dist/plot/Line.svelte +2 -3
- package/dist/plot/Line.svelte.d.ts +2 -2
- package/dist/plot/PlotLegend.svelte +79 -8
- package/dist/plot/PlotLegend.svelte.d.ts +4 -0
- package/dist/plot/PortalSelect.svelte +5 -5
- package/dist/plot/ScatterPlot.svelte +47 -33
- package/dist/plot/ScatterPlot.svelte.d.ts +5 -4
- package/dist/plot/ScatterPlot3D.svelte +6 -3
- package/dist/plot/ScatterPoint.svelte +10 -4
- package/dist/plot/ScatterPoint.svelte.d.ts +4 -2
- package/dist/plot/SpacegroupBarPlot.svelte +5 -4
- package/dist/plot/data-cleaning.js +9 -9
- package/dist/plot/index.d.ts +0 -6
- package/dist/plot/scales.d.ts +3 -3
- package/dist/plot/scales.js +29 -29
- package/dist/plot/types.d.ts +5 -9
- package/dist/rdf/calc-rdf.js +1 -1
- package/dist/sanitize.js +22 -15
- package/dist/settings.d.ts +2 -0
- package/dist/settings.js +12 -3
- package/dist/spectral/Bands.svelte +6 -6
- package/dist/spectral/BandsAndDos.svelte +4 -4
- package/dist/spectral/BrillouinBandsDos.svelte +3 -3
- package/dist/spectral/Dos.svelte +2 -2
- package/dist/spectral/helpers.js +1 -1
- package/dist/structure/AtomLegend.svelte +4 -4
- package/dist/structure/AtomLegend.svelte.d.ts +1 -1
- package/dist/structure/Cylinder.svelte +7 -7
- package/dist/structure/Structure.svelte +169 -27
- package/dist/structure/Structure.svelte.d.ts +6 -2
- package/dist/structure/StructureControls.svelte +130 -16
- package/dist/structure/StructureControls.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +519 -218
- package/dist/structure/StructureInfoPane.svelte.d.ts +2 -1
- package/dist/structure/StructureScene.svelte +399 -68
- package/dist/structure/StructureScene.svelte.d.ts +8 -4
- package/dist/structure/atom-properties.js +3 -1
- package/dist/structure/bond-order-perception.d.ts +13 -0
- package/dist/structure/bond-order-perception.js +367 -0
- package/dist/structure/bonding.d.ts +10 -1
- package/dist/structure/bonding.js +232 -11
- package/dist/structure/export.js +6 -4
- package/dist/structure/index.d.ts +19 -4
- package/dist/structure/index.js +3 -0
- package/dist/structure/label-placement.d.ts +14 -0
- package/dist/structure/label-placement.js +72 -0
- package/dist/structure/parse.d.ts +2 -1
- package/dist/structure/parse.js +25 -36
- package/dist/structure/supercell.js +35 -2
- package/dist/symmetry/SymmetryStats.svelte +1 -1
- package/dist/symmetry/cell-transform.js +15 -1
- package/dist/symmetry/index.js +3 -3
- package/dist/table/HeatmapTable.svelte +3 -3
- package/dist/table/ToggleMenu.svelte +1 -1
- package/dist/trajectory/Trajectory.svelte +2 -2
- package/dist/trajectory/TrajectoryInfoPane.svelte +14 -88
- package/dist/trajectory/extract.js +4 -4
- package/dist/trajectory/frame-reader.js +2 -2
- package/dist/trajectory/parse/ase.js +2 -6
- package/dist/trajectory/parse/hdf5.js +1 -3
- package/dist/trajectory/plotting.js +1 -1
- package/dist/utils.js +1 -1
- package/dist/xrd/calc-xrd.js +1 -1
- package/package.json +22 -37
- package/dist/structure/ferrox-wasm-types.d.ts +0 -46
- package/dist/structure/ferrox-wasm-types.js +0 -18
- package/dist/structure/ferrox-wasm.d.ts +0 -94
- package/dist/structure/ferrox-wasm.js +0 -249
|
@@ -5,6 +5,215 @@ const element_lookup = new Map(element_data.map((el) => [el.symbol, el]));
|
|
|
5
5
|
const covalent_radii = new Map(element_data
|
|
6
6
|
.filter((el) => el.covalent_radius !== null)
|
|
7
7
|
.map((el) => [el.symbol, el.covalent_radius]));
|
|
8
|
+
const is_zero_cell_shift = (cell_shift) => cell_shift === undefined || cell_shift.every((val) => val === 0);
|
|
9
|
+
const format_cell_shift = (cell_shift) => {
|
|
10
|
+
if (cell_shift === undefined || is_zero_cell_shift(cell_shift))
|
|
11
|
+
return ``;
|
|
12
|
+
return `@${cell_shift.join(`,`)}`;
|
|
13
|
+
};
|
|
14
|
+
const negate_cell_shift = (cell_shift) => cell_shift.map((val) => (val === 0 ? 0 : -val));
|
|
15
|
+
const normalize_bond_endpoints = (site_idx_1, site_idx_2, cell_shift) => {
|
|
16
|
+
const ordered = site_idx_1 < site_idx_2
|
|
17
|
+
? { site_idx_1, site_idx_2 }
|
|
18
|
+
: { site_idx_1: site_idx_2, site_idx_2: site_idx_1 };
|
|
19
|
+
if (cell_shift === undefined || is_zero_cell_shift(cell_shift))
|
|
20
|
+
return ordered;
|
|
21
|
+
return {
|
|
22
|
+
...ordered,
|
|
23
|
+
cell_shift: site_idx_1 < site_idx_2 ? cell_shift : negate_cell_shift(cell_shift),
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
export const normalize_structure_bond = (site_idx_1, site_idx_2, order, cell_shift) => {
|
|
27
|
+
const bond = normalize_bond_endpoints(site_idx_1, site_idx_2, cell_shift);
|
|
28
|
+
return { ...bond, order };
|
|
29
|
+
};
|
|
30
|
+
export const get_bond_key = (idx_1, idx_2, cell_shift) => {
|
|
31
|
+
const normalized = normalize_bond_endpoints(idx_1, idx_2, cell_shift);
|
|
32
|
+
return `${normalized.site_idx_1}-${normalized.site_idx_2}${format_cell_shift(normalized.cell_shift)}`;
|
|
33
|
+
};
|
|
34
|
+
export const merge_bond_edits = (base_bonds, added, removed, overrides) => {
|
|
35
|
+
const key_for = (bond) => get_bond_key(bond.site_idx_1, bond.site_idx_2, bond.cell_shift);
|
|
36
|
+
const merged = new Map(base_bonds.map((bond) => [key_for(bond), bond]));
|
|
37
|
+
for (const bond of removed)
|
|
38
|
+
merged.delete(key_for(bond));
|
|
39
|
+
// Apply additions before overrides so user-set bond orders win even if
|
|
40
|
+
// callers accidentally pass overlapping edit lists.
|
|
41
|
+
for (const bond of added)
|
|
42
|
+
merged.set(key_for(bond), bond);
|
|
43
|
+
for (const bond of overrides)
|
|
44
|
+
merged.set(key_for(bond), bond);
|
|
45
|
+
return [...merged.values()];
|
|
46
|
+
};
|
|
47
|
+
const is_record = (value) => typeof value === `object` && value !== null;
|
|
48
|
+
export function normalize_bond_order(order) {
|
|
49
|
+
if (order === `aromatic`)
|
|
50
|
+
return order;
|
|
51
|
+
if (order === 1 || order === 1.5 || order === 2 || order === 3)
|
|
52
|
+
return order;
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
function normalize_cell_shift(cell_shift) {
|
|
56
|
+
if (cell_shift === undefined)
|
|
57
|
+
return undefined;
|
|
58
|
+
if (!Array.isArray(cell_shift) || cell_shift.length !== 3)
|
|
59
|
+
return null;
|
|
60
|
+
return cell_shift.some((val) => typeof val !== `number` || !Number.isInteger(val))
|
|
61
|
+
? null
|
|
62
|
+
: cell_shift;
|
|
63
|
+
}
|
|
64
|
+
function lattice_translation(structure, cell_shift) {
|
|
65
|
+
if (cell_shift === undefined || is_zero_cell_shift(cell_shift))
|
|
66
|
+
return [0, 0, 0];
|
|
67
|
+
if (!(`lattice` in structure)) {
|
|
68
|
+
throw new Error(`Explicit bond cell_shift requires a crystal lattice`);
|
|
69
|
+
}
|
|
70
|
+
const [shift_a, shift_b, shift_c] = cell_shift;
|
|
71
|
+
const [vec_a, vec_b, vec_c] = structure.lattice.matrix;
|
|
72
|
+
return math.add(math.scale(vec_a, shift_a), math.scale(vec_b, shift_b), math.scale(vec_c, shift_c));
|
|
73
|
+
}
|
|
74
|
+
export function structure_bond_to_bond_pair(structure, bond) {
|
|
75
|
+
const { site_idx_1, site_idx_2, order, cell_shift } = bond;
|
|
76
|
+
const site_1 = structure.sites[site_idx_1];
|
|
77
|
+
const site_2 = structure.sites[site_idx_2];
|
|
78
|
+
if (!site_1 || !site_2) {
|
|
79
|
+
throw new Error(`Cannot create bond pair for invalid site indices ${site_idx_1}, ${site_idx_2}`);
|
|
80
|
+
}
|
|
81
|
+
const pos_1 = site_1.xyz;
|
|
82
|
+
const pos_2 = math.add(site_2.xyz, lattice_translation(structure, cell_shift));
|
|
83
|
+
return {
|
|
84
|
+
pos_1,
|
|
85
|
+
pos_2,
|
|
86
|
+
site_idx_1,
|
|
87
|
+
site_idx_2,
|
|
88
|
+
bond_length: math.euclidean_dist(pos_1, pos_2),
|
|
89
|
+
strength: 1,
|
|
90
|
+
bond_order: order,
|
|
91
|
+
cell_shift,
|
|
92
|
+
transform_matrix: compute_bond_transform(pos_1, pos_2),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export function get_explicit_bond_metadata(structure) {
|
|
96
|
+
const raw_bonds = structure.properties?.bonds;
|
|
97
|
+
if (raw_bonds === undefined)
|
|
98
|
+
return [];
|
|
99
|
+
if (!Array.isArray(raw_bonds)) {
|
|
100
|
+
console.warn(`Ignoring structure.properties.bonds because it is not an array`);
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
const explicit_bonds = new Map();
|
|
104
|
+
for (const [entry_idx, raw_bond] of raw_bonds.entries()) {
|
|
105
|
+
if (!is_record(raw_bond)) {
|
|
106
|
+
console.warn(`Ignoring invalid explicit bond at index ${entry_idx}: expected object`);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const { order } = raw_bond;
|
|
110
|
+
const site_idx_1 = raw_bond.site_idx_1;
|
|
111
|
+
const site_idx_2 = raw_bond.site_idx_2;
|
|
112
|
+
if (typeof site_idx_1 !== `number` ||
|
|
113
|
+
typeof site_idx_2 !== `number` ||
|
|
114
|
+
!Number.isInteger(site_idx_1) ||
|
|
115
|
+
!Number.isInteger(site_idx_2)) {
|
|
116
|
+
console.warn(`Ignoring invalid explicit bond at index ${entry_idx}: site indices must be integers`);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (site_idx_1 < 0 ||
|
|
120
|
+
site_idx_2 < 0 ||
|
|
121
|
+
site_idx_1 >= structure.sites.length ||
|
|
122
|
+
site_idx_2 >= structure.sites.length) {
|
|
123
|
+
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
|
+
continue;
|
|
125
|
+
}
|
|
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
|
+
const bond_order = normalize_bond_order(order);
|
|
131
|
+
if (bond_order === null) {
|
|
132
|
+
console.warn(`Ignoring invalid explicit bond at index ${entry_idx}: unsupported order ${String(order)}`);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const cell_shift = normalize_cell_shift(raw_bond.cell_shift);
|
|
136
|
+
if (cell_shift === null) {
|
|
137
|
+
console.warn(`Ignoring invalid explicit bond at index ${entry_idx}: cell_shift must be three integers`);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (cell_shift !== undefined &&
|
|
141
|
+
!is_zero_cell_shift(cell_shift) &&
|
|
142
|
+
!(`lattice` in structure)) {
|
|
143
|
+
console.warn(`Ignoring invalid explicit bond at index ${entry_idx}: cell_shift requires a crystal lattice`);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const key = get_bond_key(site_idx_1, site_idx_2, cell_shift);
|
|
147
|
+
if (explicit_bonds.has(key)) {
|
|
148
|
+
console.warn(`Duplicate explicit bond definition at index ${entry_idx} for site indices ${site_idx_1}, ${site_idx_2} with order ${bond_order}; will overwrite the previous entry`);
|
|
149
|
+
}
|
|
150
|
+
explicit_bonds.set(key, normalize_structure_bond(site_idx_1, site_idx_2, bond_order, cell_shift));
|
|
151
|
+
}
|
|
152
|
+
return [...explicit_bonds.values()];
|
|
153
|
+
}
|
|
154
|
+
export function apply_explicit_bond_metadata(structure, bonds) {
|
|
155
|
+
const explicit_bonds = get_explicit_bond_metadata(structure);
|
|
156
|
+
if (explicit_bonds.length === 0)
|
|
157
|
+
return bonds;
|
|
158
|
+
const explicit_by_key = new Map(explicit_bonds.map((bond) => [
|
|
159
|
+
get_bond_key(bond.site_idx_1, bond.site_idx_2, bond.cell_shift),
|
|
160
|
+
bond,
|
|
161
|
+
]));
|
|
162
|
+
const merged = bonds.map((bond) => {
|
|
163
|
+
const key = get_bond_key(bond.site_idx_1, bond.site_idx_2, bond.cell_shift);
|
|
164
|
+
const explicit = explicit_by_key.get(key);
|
|
165
|
+
if (!explicit)
|
|
166
|
+
return bond;
|
|
167
|
+
explicit_by_key.delete(key);
|
|
168
|
+
return { ...bond, bond_order: explicit.order };
|
|
169
|
+
});
|
|
170
|
+
for (const explicit_bond of explicit_by_key.values()) {
|
|
171
|
+
merged.push(structure_bond_to_bond_pair(structure, explicit_bond));
|
|
172
|
+
}
|
|
173
|
+
return merged;
|
|
174
|
+
}
|
|
175
|
+
export function scale_and_offset_bond_matrix(transform_matrix, offset, radius_scale) {
|
|
176
|
+
const matrix = new Float32Array(transform_matrix);
|
|
177
|
+
// Column-major 4x4 layout: 0-2 are the right vector, 8-10 are the forward
|
|
178
|
+
// vector. Scale orientation columns for cylinder radius, not translation.
|
|
179
|
+
for (const matrix_idx of [0, 1, 2, 8, 9, 10]) {
|
|
180
|
+
matrix[matrix_idx] *= radius_scale;
|
|
181
|
+
}
|
|
182
|
+
const right_len = Math.hypot(matrix[0], matrix[1], matrix[2]) || 1;
|
|
183
|
+
const offset_dir = [
|
|
184
|
+
matrix[0] / right_len,
|
|
185
|
+
matrix[1] / right_len,
|
|
186
|
+
matrix[2] / right_len,
|
|
187
|
+
];
|
|
188
|
+
matrix[12] += offset_dir[0] * offset;
|
|
189
|
+
matrix[13] += offset_dir[1] * offset;
|
|
190
|
+
matrix[14] += offset_dir[2] * offset;
|
|
191
|
+
return matrix;
|
|
192
|
+
}
|
|
193
|
+
export function get_bond_render_matrices(bond, bond_thickness) {
|
|
194
|
+
const order = bond.bond_order ?? 1;
|
|
195
|
+
const gap = bond_thickness * 1.8;
|
|
196
|
+
const offsets_and_scales = order === 2
|
|
197
|
+
? [
|
|
198
|
+
[-gap / 2, 0.65],
|
|
199
|
+
[gap / 2, 0.65],
|
|
200
|
+
]
|
|
201
|
+
: order === 3
|
|
202
|
+
? [
|
|
203
|
+
[-gap, 0.55],
|
|
204
|
+
[0, 0.55],
|
|
205
|
+
[gap, 0.55],
|
|
206
|
+
]
|
|
207
|
+
: order === 1.5 || order === `aromatic`
|
|
208
|
+
? [
|
|
209
|
+
[-gap / 2, 0.75],
|
|
210
|
+
[gap / 2, 0.4],
|
|
211
|
+
]
|
|
212
|
+
: [];
|
|
213
|
+
return offsets_and_scales.length === 0
|
|
214
|
+
? [bond.transform_matrix]
|
|
215
|
+
: offsets_and_scales.map(([offset, radius_scale]) => scale_and_offset_bond_matrix(bond.transform_matrix, offset, radius_scale));
|
|
216
|
+
}
|
|
8
217
|
// Get the species with highest occupancy from a site.
|
|
9
218
|
const get_majority_species = (site) => (site.species ?? []).reduce((max_species, species) => (species.occu > max_species.occu ? species : max_species), site.species?.[0] ?? { element: ``, occu: -1 });
|
|
10
219
|
// Helper to extract numeric index from site properties
|
|
@@ -70,10 +279,22 @@ export function compute_bond_transform(pos_1, pos_2) {
|
|
|
70
279
|
];
|
|
71
280
|
return new Float32Array([
|
|
72
281
|
// Return flattened column-major 4x4 matrix for Three.js
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
282
|
+
m00,
|
|
283
|
+
m10,
|
|
284
|
+
m20,
|
|
285
|
+
0,
|
|
286
|
+
m01 * height,
|
|
287
|
+
m11 * height,
|
|
288
|
+
m21 * height,
|
|
289
|
+
0,
|
|
290
|
+
m02,
|
|
291
|
+
m12,
|
|
292
|
+
m22,
|
|
293
|
+
0,
|
|
294
|
+
px,
|
|
295
|
+
py,
|
|
296
|
+
pz,
|
|
297
|
+
1,
|
|
77
298
|
]);
|
|
78
299
|
}
|
|
79
300
|
// Build spatial grid by dividing 3D space into cubic cells.
|
|
@@ -175,7 +396,7 @@ strength_threshold = 0.3, // Minimum bond strength to include in results
|
|
|
175
396
|
if (dist > expected * max_distance_ratio)
|
|
176
397
|
continue;
|
|
177
398
|
const electroneg_diff = Math.abs(props_a.electroneg - props_b.electroneg);
|
|
178
|
-
const
|
|
399
|
+
const electroneg_balance = electroneg_diff / (props_a.electroneg + props_b.electroneg);
|
|
179
400
|
let bond_strength = 1.0;
|
|
180
401
|
if (props_a.is_metal && props_b.is_metal) {
|
|
181
402
|
bond_strength *= metal_metal_penalty;
|
|
@@ -190,7 +411,7 @@ strength_threshold = 0.3, // Minimum bond strength to include in results
|
|
|
190
411
|
bond_strength *= similar_electronegativity_bonus;
|
|
191
412
|
}
|
|
192
413
|
const dist_weight = Math.exp(-((dist / expected - 1) ** 2) / 0.18);
|
|
193
|
-
const electroneg_weight = 1.0 - 0.3 *
|
|
414
|
+
const electroneg_weight = 1.0 - 0.3 * electroneg_balance;
|
|
194
415
|
let strength = bond_strength * dist_weight * electroneg_weight;
|
|
195
416
|
if (props_a.element === props_b.element)
|
|
196
417
|
strength *= same_species_penalty;
|
|
@@ -248,7 +469,7 @@ strength_threshold = 0.3, // Minimum bond strength to include in results
|
|
|
248
469
|
});
|
|
249
470
|
}
|
|
250
471
|
}
|
|
251
|
-
return bonds;
|
|
472
|
+
return apply_explicit_bond_metadata(structure, bonds);
|
|
252
473
|
}
|
|
253
474
|
// Solid angle-based bonding using geometric proximity heuristics.
|
|
254
475
|
// Inspired by Voronoi tessellation without having to actually compute Voronoi cells.
|
|
@@ -281,11 +502,11 @@ export function solid_angle(structure, { min_solid_angle = 0.01, min_face_area =
|
|
|
281
502
|
}
|
|
282
503
|
const avg_radius = (radius_a + radius_b) / 2.0;
|
|
283
504
|
const face_area = Math.PI * avg_radius * avg_radius;
|
|
284
|
-
const
|
|
285
|
-
if (
|
|
505
|
+
const bond_solid_angle = face_area / dist_sq;
|
|
506
|
+
if (bond_solid_angle < min_solid_angle || face_area < min_face_area)
|
|
286
507
|
continue;
|
|
287
508
|
const dist_penalty = Math.exp(-((dist / (radius_a + radius_b) - 1) ** 2) / 0.4);
|
|
288
|
-
const angle_strength = Math.min(
|
|
509
|
+
const angle_strength = Math.min(bond_solid_angle / (4.0 * Math.PI), 1.0);
|
|
289
510
|
const strength = angle_strength * dist_penalty;
|
|
290
511
|
if (strength > strength_threshold) {
|
|
291
512
|
bonds.push({
|
|
@@ -300,5 +521,5 @@ export function solid_angle(structure, { min_solid_angle = 0.01, min_face_area =
|
|
|
300
521
|
}
|
|
301
522
|
}
|
|
302
523
|
}
|
|
303
|
-
return bonds;
|
|
524
|
+
return apply_explicit_bond_metadata(structure, bonds);
|
|
304
525
|
}
|
package/dist/structure/export.js
CHANGED
|
@@ -467,11 +467,13 @@ export function structure_to_cif_str(structure) {
|
|
|
467
467
|
structure.symmetry &&
|
|
468
468
|
typeof structure.symmetry === `object`) {
|
|
469
469
|
const symmetry = structure.symmetry;
|
|
470
|
-
|
|
471
|
-
|
|
470
|
+
const { space_group_number, space_group_symbol } = symmetry;
|
|
471
|
+
if (typeof space_group_symbol === `string` && space_group_symbol) {
|
|
472
|
+
lines.push(`_space_group_name_H-M_alt ${space_group_symbol}`);
|
|
472
473
|
}
|
|
473
|
-
if (
|
|
474
|
-
|
|
474
|
+
if ((typeof space_group_number === `number` || typeof space_group_number === `string`) &&
|
|
475
|
+
space_group_number) {
|
|
476
|
+
lines.push(`_space_group_IT_number ${space_group_number}`);
|
|
475
477
|
}
|
|
476
478
|
}
|
|
477
479
|
lines.push(``);
|
|
@@ -2,9 +2,10 @@ import type { CompositionType } from '../composition';
|
|
|
2
2
|
import type { ElementSymbol } from '../element';
|
|
3
3
|
import type { Vec3 } from '../math';
|
|
4
4
|
import * as math from '../math';
|
|
5
|
-
import type { Lattice, StructureScene } from './';
|
|
6
5
|
import type { ComponentProps } from 'svelte';
|
|
6
|
+
import LatticeComponent from './Lattice.svelte';
|
|
7
7
|
import type { Pbc } from './pbc';
|
|
8
|
+
import StructureSceneComponent from './StructureScene.svelte';
|
|
8
9
|
export { default as Arrow } from './Arrow.svelte';
|
|
9
10
|
export * from './atom-properties';
|
|
10
11
|
export { default as AtomLegend } from './AtomLegend.svelte';
|
|
@@ -46,12 +47,22 @@ export type Molecule = {
|
|
|
46
47
|
sites: Site[];
|
|
47
48
|
charge?: number;
|
|
48
49
|
id?: string;
|
|
49
|
-
properties?:
|
|
50
|
+
properties?: StructureProperties;
|
|
50
51
|
};
|
|
51
52
|
export type Crystal = Molecule & {
|
|
52
53
|
lattice: LatticeType;
|
|
53
54
|
};
|
|
54
55
|
export type AnyStructure = Crystal | Molecule;
|
|
56
|
+
export type BondOrder = 1 | 1.5 | 2 | 3 | `aromatic`;
|
|
57
|
+
export type StructureBond = {
|
|
58
|
+
site_idx_1: number;
|
|
59
|
+
site_idx_2: number;
|
|
60
|
+
order: BondOrder;
|
|
61
|
+
cell_shift?: Vec3;
|
|
62
|
+
};
|
|
63
|
+
export type StructureProperties = Record<string, unknown> & {
|
|
64
|
+
bonds?: StructureBond[];
|
|
65
|
+
};
|
|
55
66
|
export type BondPair = {
|
|
56
67
|
pos_1: Vec3;
|
|
57
68
|
pos_2: Vec3;
|
|
@@ -59,8 +70,12 @@ export type BondPair = {
|
|
|
59
70
|
site_idx_2: number;
|
|
60
71
|
bond_length: number;
|
|
61
72
|
strength: number;
|
|
73
|
+
bond_order?: BondOrder;
|
|
74
|
+
cell_shift?: Vec3;
|
|
62
75
|
transform_matrix: Float32Array;
|
|
63
76
|
};
|
|
77
|
+
export type { PerceivedBond, PerceptionOptions } from './bond-order-perception';
|
|
78
|
+
export { compose_perceived_bonds, perceive_bond_orders, } from './bond-order-perception';
|
|
64
79
|
export declare function get_element_counts(structure: AnyStructure): Partial<Record<"S" | "K" | "B" | "H" | "He" | "Li" | "Be" | "C" | "N" | "O" | "F" | "Ne" | "Na" | "Mg" | "Al" | "Si" | "P" | "Cl" | "Ar" | "Ca" | "Sc" | "Ti" | "V" | "Cr" | "Mn" | "Fe" | "Co" | "Ni" | "Cu" | "Zn" | "Ga" | "Ge" | "As" | "Se" | "Br" | "Kr" | "Rb" | "Sr" | "Y" | "Zr" | "Nb" | "Mo" | "Tc" | "Ru" | "Rh" | "Pd" | "Ag" | "Cd" | "In" | "Sn" | "Sb" | "Te" | "I" | "Xe" | "Cs" | "Ba" | "La" | "Ce" | "Pr" | "Nd" | "Pm" | "Sm" | "Eu" | "Gd" | "Tb" | "Dy" | "Ho" | "Er" | "Tm" | "Yb" | "Lu" | "Hf" | "Ta" | "W" | "Re" | "Os" | "Ir" | "Pt" | "Au" | "Hg" | "Tl" | "Pb" | "Bi" | "Po" | "At" | "Rn" | "Fr" | "Ra" | "Ac" | "Th" | "Pa" | "U" | "Np" | "Pu" | "Am" | "Cm" | "Bk" | "Cf" | "Es" | "Fm" | "Md" | "No" | "Lr" | "Rf" | "Db" | "Sg" | "Bh" | "Hs" | "Mt" | "Ds" | "Rg" | "Cn" | "Nh" | "Fl" | "Mc" | "Lv" | "Ts" | "Og", number>>;
|
|
65
80
|
export declare function format_chemical_formula(structure: AnyStructure, sort_fn: (symbols: ElementSymbol[]) => ElementSymbol[]): string;
|
|
66
81
|
export declare function format_formula_by_electronegativity(structure: AnyStructure): string;
|
|
@@ -94,8 +109,8 @@ export interface StructureHandlerData {
|
|
|
94
109
|
camera_has_moved?: boolean;
|
|
95
110
|
color_scheme?: string;
|
|
96
111
|
performance_mode?: `quality` | `speed`;
|
|
97
|
-
scene_props?: ComponentProps<typeof
|
|
98
|
-
lattice_props?: ComponentProps<typeof
|
|
112
|
+
scene_props?: ComponentProps<typeof StructureSceneComponent>;
|
|
113
|
+
lattice_props?: ComponentProps<typeof LatticeComponent>;
|
|
99
114
|
}
|
|
100
115
|
export interface BondInstance {
|
|
101
116
|
matrix: Float32Array;
|
package/dist/structure/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
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';
|
|
4
6
|
export { default as Arrow } from './Arrow.svelte';
|
|
5
7
|
export * from './atom-properties';
|
|
6
8
|
export { default as AtomLegend } from './AtomLegend.svelte';
|
|
@@ -17,6 +19,7 @@ export { default as StructureInfoPane } from './StructureInfoPane.svelte';
|
|
|
17
19
|
export { default as StructureScene } from './StructureScene.svelte';
|
|
18
20
|
export * from './supercell';
|
|
19
21
|
export const LATTICE_PARAM_KEYS = [`a`, `b`, `c`, `alpha`, `beta`, `gamma`];
|
|
22
|
+
export { compose_perceived_bonds, perceive_bond_orders, } from './bond-order-perception';
|
|
20
23
|
export function get_element_counts(structure) {
|
|
21
24
|
const elements = {};
|
|
22
25
|
for (const site of structure.sites) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Vec3 } from '../math';
|
|
2
|
+
import type { Camera, Object3D } from 'three';
|
|
3
|
+
export declare const LABEL_OFFSET_EPS = 1e-9;
|
|
4
|
+
type LabelOffsetSource = Vec3 | (() => Vec3);
|
|
5
|
+
export declare const choose_site_label_offset: (bond_directions: Vec3[], base_offset: Vec3) => Vec3;
|
|
6
|
+
export declare const label_screen_position: (atom_position: Vec3, label_offset: Vec3, visual_radius: number, label_screen_margin: number, label_camera: Camera, size: {
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
}) => [number, number];
|
|
10
|
+
export declare const make_label_position_calculator: (_atom_position: Vec3, label_offset: LabelOffsetSource, visual_radius: number, label_screen_margin: number) => (object: Object3D, label_camera: Camera, size: {
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
}) => [number, number];
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as math from '../math';
|
|
2
|
+
import { Vector3 } from 'three';
|
|
3
|
+
export const LABEL_OFFSET_EPS = 1e-9;
|
|
4
|
+
const LABEL_OFFSET_DIRECTIONS = [
|
|
5
|
+
[0, -1, 0],
|
|
6
|
+
[1, 0, 0],
|
|
7
|
+
[-1, 0, 0],
|
|
8
|
+
[1, 1, 0],
|
|
9
|
+
[-1, 1, 0],
|
|
10
|
+
[1, -1, 0],
|
|
11
|
+
[-1, -1, 0],
|
|
12
|
+
[0, 0, 1],
|
|
13
|
+
[0, 0, -1],
|
|
14
|
+
];
|
|
15
|
+
const label_direction_score = (direction, bond_directions, preferred_direction) => {
|
|
16
|
+
let max_bond_alignment = 0;
|
|
17
|
+
for (const bond_direction of bond_directions) {
|
|
18
|
+
max_bond_alignment = Math.max(max_bond_alignment, Math.max(0, math.dot(direction, bond_direction)));
|
|
19
|
+
}
|
|
20
|
+
const preferred_bonus = Math.max(0, math.dot(direction, preferred_direction)) * 0.05;
|
|
21
|
+
const screen_plane_bonus = Math.abs(direction[2]) < LABEL_OFFSET_EPS ? 0.02 : 0;
|
|
22
|
+
return -max_bond_alignment + preferred_bonus + screen_plane_bonus;
|
|
23
|
+
};
|
|
24
|
+
export const choose_site_label_offset = (bond_directions, base_offset) => {
|
|
25
|
+
const offset_length = Math.hypot(...base_offset);
|
|
26
|
+
if (offset_length < LABEL_OFFSET_EPS || bond_directions.length === 0) {
|
|
27
|
+
return base_offset;
|
|
28
|
+
}
|
|
29
|
+
const preferred_direction = math.normalize_vec3(base_offset, [0, 1, 0]);
|
|
30
|
+
const repulsion_direction = math.normalize_vec3(bond_directions.reduce((offset_sum, bond_direction) => math.subtract(offset_sum, bond_direction), [0, 0, 0]), preferred_direction);
|
|
31
|
+
let best_direction = preferred_direction;
|
|
32
|
+
let best_score = -Infinity;
|
|
33
|
+
for (const candidate_direction of [
|
|
34
|
+
preferred_direction,
|
|
35
|
+
repulsion_direction,
|
|
36
|
+
...LABEL_OFFSET_DIRECTIONS,
|
|
37
|
+
]) {
|
|
38
|
+
const direction = math.normalize_vec3(candidate_direction, preferred_direction);
|
|
39
|
+
const score = label_direction_score(direction, bond_directions, preferred_direction);
|
|
40
|
+
if (score <= best_score + LABEL_OFFSET_EPS)
|
|
41
|
+
continue;
|
|
42
|
+
best_score = score;
|
|
43
|
+
best_direction = direction;
|
|
44
|
+
}
|
|
45
|
+
return math.scale(best_direction, offset_length);
|
|
46
|
+
};
|
|
47
|
+
const project_to_screen = (position, label_camera, size) => {
|
|
48
|
+
const projected = new Vector3(...position).project(label_camera);
|
|
49
|
+
return [
|
|
50
|
+
(projected.x * size.width) / 2 + size.width / 2,
|
|
51
|
+
(-projected.y * size.height) / 2 + size.height / 2,
|
|
52
|
+
];
|
|
53
|
+
};
|
|
54
|
+
export const label_screen_position = (atom_position, label_offset, visual_radius, label_screen_margin, label_camera, size) => {
|
|
55
|
+
const atom_screen = project_to_screen(atom_position, label_camera, size);
|
|
56
|
+
const offset_screen = project_to_screen(math.add(atom_position, label_offset), label_camera, size);
|
|
57
|
+
const offset_x = offset_screen[0] - atom_screen[0];
|
|
58
|
+
const offset_y = offset_screen[1] - atom_screen[1];
|
|
59
|
+
const offset_length = Math.hypot(offset_x, offset_y);
|
|
60
|
+
const direction_x = offset_length > LABEL_OFFSET_EPS ? offset_x / offset_length : 0;
|
|
61
|
+
const direction_y = offset_length > LABEL_OFFSET_EPS ? offset_y / offset_length : label_offset[1] >= 0 ? -1 : 1;
|
|
62
|
+
const radius_direction = math.normalize_vec3(label_offset, [0, 1, 0]);
|
|
63
|
+
const radius_edge = math.add(atom_position, math.scale(radius_direction, visual_radius));
|
|
64
|
+
const radius_screen = project_to_screen(radius_edge, label_camera, size);
|
|
65
|
+
const atom_screen_radius = Math.hypot(radius_screen[0] - atom_screen[0], radius_screen[1] - atom_screen[1]);
|
|
66
|
+
const screen_gap = atom_screen_radius + label_screen_margin;
|
|
67
|
+
return [atom_screen[0] + direction_x * screen_gap, atom_screen[1] + direction_y * screen_gap];
|
|
68
|
+
};
|
|
69
|
+
export const make_label_position_calculator = (_atom_position, label_offset, visual_radius, label_screen_margin) => (object, label_camera, size) => {
|
|
70
|
+
const world_position = object.getWorldPosition(new Vector3());
|
|
71
|
+
return label_screen_position([world_position.x, world_position.y, world_position.z], typeof label_offset === `function` ? label_offset() : label_offset, visual_radius, label_screen_margin, label_camera, size);
|
|
72
|
+
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { OptimadeStructure } from '../api/optimade';
|
|
2
2
|
import * as math from '../math';
|
|
3
|
-
import type { AnyStructure, Crystal, Site } from './';
|
|
3
|
+
import type { AnyStructure, Crystal, Site, StructureProperties } from './';
|
|
4
4
|
import type { Pbc } from './pbc';
|
|
5
5
|
export interface ParsedStructure {
|
|
6
6
|
sites: Site[];
|
|
7
|
+
properties?: StructureProperties;
|
|
7
8
|
lattice?: {
|
|
8
9
|
matrix: math.Matrix3x3;
|
|
9
10
|
a: number;
|
package/dist/structure/parse.js
CHANGED
|
@@ -250,16 +250,18 @@ export function parse_xyz(content) {
|
|
|
250
250
|
// Split into frames by reading the atom count and slicing lines
|
|
251
251
|
const all_lines = normalized_content.split(/\r?\n/);
|
|
252
252
|
const frames = [];
|
|
253
|
-
let
|
|
254
|
-
while (
|
|
255
|
-
const numAtoms = parseInt(all_lines[
|
|
256
|
-
if (!isNaN(numAtoms) &&
|
|
257
|
-
|
|
253
|
+
let frame_line_idx = 0;
|
|
254
|
+
while (frame_line_idx < all_lines.length) {
|
|
255
|
+
const numAtoms = parseInt(all_lines[frame_line_idx].trim(), 10);
|
|
256
|
+
if (!isNaN(numAtoms) &&
|
|
257
|
+
numAtoms > 0 &&
|
|
258
|
+
frame_line_idx + numAtoms + 1 < all_lines.length) {
|
|
259
|
+
const frameLines = all_lines.slice(frame_line_idx, frame_line_idx + numAtoms + 2);
|
|
258
260
|
frames.push(frameLines.join(`\n`));
|
|
259
|
-
|
|
261
|
+
frame_line_idx += numAtoms + 2;
|
|
260
262
|
}
|
|
261
263
|
else
|
|
262
|
-
|
|
264
|
+
frame_line_idx++;
|
|
263
265
|
}
|
|
264
266
|
// If no frames found, try simple parsing
|
|
265
267
|
if (frames.length === 0)
|
|
@@ -738,8 +740,8 @@ export function parse_cif(content, wrap_fractional_coords = true, strict = true)
|
|
|
738
740
|
return map;
|
|
739
741
|
})();
|
|
740
742
|
const observed_counts = {};
|
|
741
|
-
for (const
|
|
742
|
-
observed_counts[
|
|
743
|
+
for (const atom of atoms) {
|
|
744
|
+
observed_counts[atom.element] = (observed_counts[atom.element] || 0) + 1;
|
|
743
745
|
}
|
|
744
746
|
const has_expected_counts = Object.keys(atom_type_counts).length > 0;
|
|
745
747
|
const already_enumerated = has_expected_counts &&
|
|
@@ -1080,46 +1082,33 @@ export function parse_structure_file(content, filename) {
|
|
|
1080
1082
|
}
|
|
1081
1083
|
// Universal parser that handles JSON and structure files
|
|
1082
1084
|
export function parse_any_structure(content, filename) {
|
|
1085
|
+
const clone_structure_properties = (properties) => structuredClone(properties);
|
|
1086
|
+
const finalize_structure = (structure) => ({
|
|
1087
|
+
sites: structure.sites,
|
|
1088
|
+
charge: 0,
|
|
1089
|
+
...(structure.properties && {
|
|
1090
|
+
properties: clone_structure_properties(structure.properties),
|
|
1091
|
+
}),
|
|
1092
|
+
...(structure.lattice && {
|
|
1093
|
+
lattice: { ...structure.lattice, pbc: [true, true, true] },
|
|
1094
|
+
}),
|
|
1095
|
+
});
|
|
1083
1096
|
// Try JSON first, but handle nested structures properly
|
|
1084
1097
|
try {
|
|
1085
1098
|
const parsed = JSON.parse(content);
|
|
1086
1099
|
// Check if it's already a valid structure using proper type guard
|
|
1087
1100
|
if (is_parsed_structure(parsed)) {
|
|
1088
|
-
// Ensure PBC is set for structures with lattice
|
|
1089
|
-
if (parsed.lattice && !parsed.lattice.pbc) {
|
|
1090
|
-
parsed.lattice.pbc = [true, true, true];
|
|
1091
|
-
}
|
|
1092
1101
|
// Normalize coordinates (wrap fractional to [0,1) and recompute Cartesian)
|
|
1093
|
-
return normalize_fractional_coords(parsed);
|
|
1102
|
+
return finalize_structure(normalize_fractional_coords(parsed));
|
|
1094
1103
|
}
|
|
1095
1104
|
// If not, use parse_structure_file to find nested structures
|
|
1096
1105
|
const structure = parse_structure_file(content, filename);
|
|
1097
|
-
|
|
1098
|
-
return {
|
|
1099
|
-
sites: structure.sites,
|
|
1100
|
-
charge: 0,
|
|
1101
|
-
...(structure.lattice && {
|
|
1102
|
-
lattice: { ...structure.lattice, pbc: [true, true, true] },
|
|
1103
|
-
}),
|
|
1104
|
-
};
|
|
1105
|
-
}
|
|
1106
|
-
else
|
|
1107
|
-
return null;
|
|
1106
|
+
return structure ? finalize_structure(structure) : null;
|
|
1108
1107
|
}
|
|
1109
1108
|
catch {
|
|
1110
1109
|
// Try structure file formats
|
|
1111
1110
|
const parsed = parse_structure_file(content, filename);
|
|
1112
|
-
|
|
1113
|
-
return {
|
|
1114
|
-
sites: parsed.sites,
|
|
1115
|
-
charge: 0,
|
|
1116
|
-
...(parsed.lattice && {
|
|
1117
|
-
lattice: { ...parsed.lattice, pbc: [true, true, true] },
|
|
1118
|
-
}),
|
|
1119
|
-
};
|
|
1120
|
-
}
|
|
1121
|
-
else
|
|
1122
|
-
return null;
|
|
1111
|
+
return parsed ? finalize_structure(parsed) : null;
|
|
1123
1112
|
}
|
|
1124
1113
|
}
|
|
1125
1114
|
// Parse OPTIMADE JSON format
|
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
import * as math from '../math';
|
|
2
2
|
import { scale_lattice_matrix } from '../math';
|
|
3
3
|
import { wrap_frac_coord } from './pbc';
|
|
4
|
+
import { normalize_structure_bond } from './bonding';
|
|
5
|
+
const mod = (value, divisor) => ((value % divisor) + divisor) % divisor;
|
|
6
|
+
const replicate_bonds_for_supercell = (bonds, n_sites, scaling_factors) => {
|
|
7
|
+
const [scale_x, scale_y, scale_z] = scaling_factors;
|
|
8
|
+
const replicated = [];
|
|
9
|
+
const site_offset = ([cell_x, cell_y, cell_z]) => (cell_z * scale_x * scale_y + cell_y * scale_x + cell_x) * n_sites;
|
|
10
|
+
for (const [source_cell_idx, source_cell] of generate_lattice_points(scaling_factors).entries()) {
|
|
11
|
+
const source_offset = source_cell_idx * n_sites;
|
|
12
|
+
for (const { site_idx_1, site_idx_2, order, cell_shift = [0, 0, 0] } of bonds) {
|
|
13
|
+
const target_raw = [
|
|
14
|
+
source_cell[0] + cell_shift[0],
|
|
15
|
+
source_cell[1] + cell_shift[1],
|
|
16
|
+
source_cell[2] + cell_shift[2],
|
|
17
|
+
];
|
|
18
|
+
const target_cell = [
|
|
19
|
+
mod(target_raw[0], scale_x),
|
|
20
|
+
mod(target_raw[1], scale_y),
|
|
21
|
+
mod(target_raw[2], scale_z),
|
|
22
|
+
];
|
|
23
|
+
const supercell_shift = target_raw.map((val, idx) => Math.floor(val / scaling_factors[idx]));
|
|
24
|
+
const target_offset = site_offset(target_cell);
|
|
25
|
+
replicated.push(normalize_structure_bond(site_idx_1 + source_offset, site_idx_2 + target_offset, order, supercell_shift));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return replicated;
|
|
29
|
+
};
|
|
4
30
|
// Parse supercell scaling input from various formats. Can be "2x2x2", "2", [2,2,2], or a single number.
|
|
5
31
|
// Returns [x, y, z] scaling factors.
|
|
6
32
|
export function parse_supercell_scaling(scaling) {
|
|
@@ -40,7 +66,7 @@ export function parse_supercell_scaling(scaling) {
|
|
|
40
66
|
export function generate_lattice_points(scaling_factors) {
|
|
41
67
|
const [scale_x, scale_y, scale_z] = scaling_factors;
|
|
42
68
|
const count = scale_x * scale_y * scale_z;
|
|
43
|
-
const points =
|
|
69
|
+
const points = Array(count);
|
|
44
70
|
let write_idx = 0;
|
|
45
71
|
// Generate in x, y, z order to match expected test results
|
|
46
72
|
for (let kk = 0; kk < scale_z; kk++) {
|
|
@@ -79,7 +105,7 @@ export function make_supercell(structure, scaling, to_unit_cell = true) {
|
|
|
79
105
|
};
|
|
80
106
|
// Pre-allocate sites array
|
|
81
107
|
const n_sites = structure.sites.length;
|
|
82
|
-
const new_sites =
|
|
108
|
+
const new_sites = Array(n_sites * total_cells);
|
|
83
109
|
// Destructure lattice vectors for fast inline arithmetic (avoid function calls in hot loop)
|
|
84
110
|
const [[ax, ay, az], [bx, by, bz], [cx, cy, cz]] = orig_matrix;
|
|
85
111
|
const [sx, sy, sz] = supercell_scaling;
|
|
@@ -116,10 +142,17 @@ export function make_supercell(structure, scaling, to_unit_cell = true) {
|
|
|
116
142
|
}
|
|
117
143
|
}
|
|
118
144
|
}
|
|
145
|
+
const properties = structure.properties?.bonds === undefined
|
|
146
|
+
? structure.properties
|
|
147
|
+
: {
|
|
148
|
+
...structure.properties,
|
|
149
|
+
bonds: replicate_bonds_for_supercell(structure.properties.bonds, n_sites, supercell_scaling),
|
|
150
|
+
};
|
|
119
151
|
return {
|
|
120
152
|
...structure,
|
|
121
153
|
lattice: new_lattice,
|
|
122
154
|
sites: new_sites,
|
|
155
|
+
properties,
|
|
123
156
|
charge: structure.charge ? structure.charge * total_cells : structure.charge,
|
|
124
157
|
supercell_scaling,
|
|
125
158
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { MoyoDataset } from '@spglib/moyo-wasm'
|
|
3
3
|
import type { Snippet } from 'svelte'
|
|
4
|
-
import { tooltip } from 'svelte-multiselect'
|
|
4
|
+
import { tooltip } from 'svelte-multiselect/attachments'
|
|
5
5
|
import type { HTMLAttributes } from 'svelte/elements'
|
|
6
6
|
import { SETTINGS_CONFIG } from '../settings'
|
|
7
7
|
import type { SymmetrySettings } from './index'
|