matterviz 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/EmptyState.svelte +10 -2
- package/dist/FilePicker.svelte +123 -82
- package/dist/Icon.svelte +18 -12
- package/dist/MillerIndexInput.svelte +27 -21
- package/dist/api/optimade.js +6 -6
- package/dist/app.css +216 -207
- package/dist/brillouin/BrillouinZone.svelte +292 -149
- package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
- package/dist/brillouin/BrillouinZoneExportPane.svelte +69 -42
- package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
- package/dist/brillouin/BrillouinZoneScene.svelte +275 -163
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneTooltip.svelte +17 -7
- package/dist/brillouin/compute.js +11 -6
- package/dist/chempot-diagram/ChemPotDiagram.svelte +162 -27
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +451 -281
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2148 -1642
- package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -5
- package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
- package/dist/chempot-diagram/async-compute.svelte.js +77 -0
- package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
- package/dist/chempot-diagram/chempot-worker.js +11 -0
- package/dist/chempot-diagram/color.js +1 -2
- package/dist/chempot-diagram/compute.d.ts +10 -0
- package/dist/chempot-diagram/compute.js +250 -88
- package/dist/chempot-diagram/index.d.ts +2 -1
- package/dist/chempot-diagram/index.js +2 -1
- package/dist/chempot-diagram/temperature.js +8 -9
- package/dist/chempot-diagram/types.d.ts +3 -0
- package/dist/chempot-diagram/types.js +1 -0
- package/dist/colors/index.d.ts +1 -1
- package/dist/colors/index.js +5 -3
- package/dist/composition/BarChart.svelte +128 -55
- package/dist/composition/BubbleChart.svelte +102 -49
- package/dist/composition/Composition.svelte +100 -79
- package/dist/composition/Formula.svelte +108 -62
- package/dist/composition/FormulaFilter.svelte +665 -537
- package/dist/composition/PieChart.svelte +183 -108
- package/dist/composition/format.d.ts +5 -0
- package/dist/composition/format.js +20 -3
- package/dist/composition/parse.js +14 -9
- package/dist/convex-hull/ConvexHull.svelte +93 -40
- package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull2D.svelte +549 -360
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +1296 -827
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +1004 -688
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullControls.svelte +115 -28
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
- package/dist/convex-hull/ConvexHullStats.svelte +425 -328
- package/dist/convex-hull/ConvexHullTooltip.svelte +40 -16
- package/dist/convex-hull/GasPressureControls.svelte +104 -61
- package/dist/convex-hull/StructurePopup.svelte +25 -4
- package/dist/convex-hull/TemperatureSlider.svelte +45 -25
- package/dist/convex-hull/barycentric-coords.js +13 -7
- package/dist/convex-hull/demo-temperature.js +8 -4
- package/dist/convex-hull/gas-thermodynamics.js +17 -12
- package/dist/convex-hull/helpers.d.ts +9 -0
- package/dist/convex-hull/helpers.js +77 -34
- package/dist/convex-hull/thermodynamics.js +61 -56
- package/dist/convex-hull/types.d.ts +9 -14
- package/dist/convex-hull/types.js +0 -17
- package/dist/coordination/CoordinationBarPlot.svelte +227 -154
- package/dist/element/BohrAtom.svelte +55 -12
- package/dist/element/ElementHeading.svelte +7 -2
- package/dist/element/ElementPhoto.svelte +15 -9
- package/dist/element/ElementStats.svelte +10 -4
- package/dist/element/ElementTile.svelte +137 -73
- package/dist/element/Nucleus.svelte +39 -11
- package/dist/feedback/ClickFeedback.svelte +16 -5
- package/dist/feedback/DragOverlay.svelte +10 -2
- package/dist/feedback/Spinner.svelte +4 -2
- package/dist/feedback/StatusMessage.svelte +8 -2
- package/dist/fermi-surface/FermiSlice.svelte +118 -88
- package/dist/fermi-surface/FermiSurface.svelte +328 -187
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +535 -342
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
- package/dist/fermi-surface/compute.js +16 -20
- package/dist/fermi-surface/parse.js +24 -14
- package/dist/fermi-surface/symmetry.js +2 -7
- package/dist/fermi-surface/types.d.ts +3 -5
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +1019 -765
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +1 -1
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +76 -22
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +2 -3
- package/dist/icons.js +47 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/io/decompress.js +1 -1
- package/dist/io/export.d.ts +3 -0
- package/dist/io/export.js +129 -143
- package/dist/io/is-binary.js +2 -3
- package/dist/io/url-drop.js +1 -2
- package/dist/isosurface/Isosurface.svelte +202 -148
- package/dist/isosurface/IsosurfaceControls.svelte +46 -28
- package/dist/isosurface/parse.js +34 -29
- package/dist/isosurface/slice.js +5 -10
- package/dist/isosurface/types.d.ts +2 -1
- package/dist/isosurface/types.js +61 -12
- package/dist/labels.js +11 -8
- package/dist/layout/FullscreenToggle.svelte +11 -2
- package/dist/layout/InfoCard.svelte +38 -6
- package/dist/layout/InfoTag.svelte +63 -32
- package/dist/layout/PropertyFilter.svelte +82 -37
- package/dist/layout/SettingsSection.svelte +85 -55
- package/dist/layout/SubpageGrid.svelte +10 -2
- package/dist/layout/json-tree/JsonNode.svelte +183 -138
- package/dist/layout/json-tree/JsonTree.svelte +499 -413
- package/dist/layout/json-tree/JsonValue.svelte +127 -99
- package/dist/layout/json-tree/utils.js +4 -2
- package/dist/marching-cubes.js +25 -2
- package/dist/math.d.ts +13 -17
- package/dist/math.js +133 -67
- package/dist/overlays/ContextMenu.svelte +65 -40
- package/dist/overlays/DraggablePane.svelte +211 -139
- package/dist/periodic-table/PeriodicTable.svelte +278 -145
- package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
- package/dist/periodic-table/PropertySelect.svelte +25 -7
- package/dist/periodic-table/TableInset.svelte +8 -3
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +446 -309
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramControls.svelte +102 -43
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +63 -40
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +71 -28
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +158 -101
- package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
- package/dist/phase-diagram/build-diagram.js +9 -9
- package/dist/phase-diagram/colors.js +1 -3
- package/dist/phase-diagram/parse.js +10 -9
- package/dist/phase-diagram/svg-to-diagram.js +53 -49
- package/dist/phase-diagram/utils.d.ts +1 -0
- package/dist/phase-diagram/utils.js +80 -25
- package/dist/plot/AxisLabel.svelte +28 -3
- package/dist/plot/BarPlot.svelte +1182 -734
- package/dist/plot/BarPlot.svelte.d.ts +2 -2
- package/dist/plot/BarPlotControls.svelte +31 -5
- package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ColorBar.svelte +479 -329
- package/dist/plot/ColorScaleSelect.svelte +27 -6
- package/dist/plot/ElementScatter.svelte +36 -15
- package/dist/plot/FillArea.svelte +152 -95
- package/dist/plot/Histogram.svelte +934 -571
- package/dist/plot/Histogram.svelte.d.ts +1 -1
- package/dist/plot/HistogramControls.svelte +53 -9
- package/dist/plot/HistogramControls.svelte.d.ts +1 -1
- package/dist/plot/InteractiveAxisLabel.svelte +34 -11
- package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
- package/dist/plot/Line.svelte +63 -28
- package/dist/plot/PlotControls.svelte +157 -114
- package/dist/plot/PlotControls.svelte.d.ts +1 -1
- package/dist/plot/PlotLegend.svelte +174 -91
- package/dist/plot/PlotTooltip.svelte +45 -6
- package/dist/plot/PortalSelect.svelte +175 -147
- package/dist/plot/ReferenceLine.svelte +76 -22
- package/dist/plot/ReferenceLine3D.svelte +132 -107
- package/dist/plot/ReferencePlane.svelte +146 -121
- package/dist/plot/ScatterPlot.svelte +1681 -1091
- package/dist/plot/ScatterPlot.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3D.svelte +256 -131
- package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3DControls.svelte +113 -63
- package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
- package/dist/plot/ScatterPlot3DScene.svelte +608 -403
- package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlotControls.svelte +65 -25
- package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ScatterPoint.svelte +98 -26
- package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
- package/dist/plot/SpacegroupBarPlot.svelte +142 -85
- package/dist/plot/Surface3D.svelte +159 -108
- package/dist/plot/ZeroLines.svelte +55 -3
- package/dist/plot/ZoomRect.svelte +4 -2
- package/dist/plot/axis-utils.js +1 -3
- package/dist/plot/data-cleaning.js +12 -28
- package/dist/plot/data-transform.js +2 -1
- package/dist/plot/fill-utils.js +2 -0
- package/dist/plot/layout.d.ts +4 -1
- package/dist/plot/layout.js +33 -14
- package/dist/plot/reference-line.d.ts +2 -2
- package/dist/plot/reference-line.js +7 -5
- package/dist/plot/scales.js +24 -36
- package/dist/plot/types.d.ts +11 -23
- package/dist/plot/types.js +6 -11
- package/dist/plot/utils/label-placement.d.ts +32 -15
- package/dist/plot/utils/label-placement.js +227 -66
- package/dist/plot/utils/series-visibility.js +2 -3
- package/dist/rdf/RdfPlot.svelte +143 -91
- package/dist/rdf/calc-rdf.js +4 -5
- package/dist/sanitize.d.ts +4 -0
- package/dist/sanitize.js +107 -0
- package/dist/settings.d.ts +18 -6
- package/dist/settings.js +46 -16
- package/dist/spectral/Bands.svelte +632 -453
- package/dist/spectral/BandsAndDos.svelte +90 -49
- package/dist/spectral/BrillouinBandsDos.svelte +151 -93
- package/dist/spectral/Dos.svelte +389 -258
- package/dist/spectral/helpers.js +55 -43
- package/dist/state.svelte.d.ts +1 -1
- package/dist/state.svelte.js +3 -2
- package/dist/structure/Arrow.svelte +59 -20
- package/dist/structure/AtomLegend.svelte +215 -134
- package/dist/structure/Bond.svelte +73 -47
- package/dist/structure/CanvasTooltip.svelte +10 -2
- package/dist/structure/CellSelect.svelte +72 -45
- package/dist/structure/Cylinder.svelte +33 -17
- package/dist/structure/Lattice.svelte +88 -33
- package/dist/structure/Structure.svelte +1063 -797
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureControls.svelte +349 -118
- package/dist/structure/StructureExportPane.svelte +124 -89
- package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +304 -237
- package/dist/structure/StructureScene.svelte +879 -443
- package/dist/structure/StructureScene.svelte.d.ts +15 -7
- package/dist/structure/atom-properties.js +8 -8
- package/dist/structure/bonding.js +6 -7
- package/dist/structure/export.js +14 -29
- package/dist/structure/ferrox-wasm.js +1 -1
- package/dist/structure/index.d.ts +13 -3
- package/dist/structure/index.js +83 -23
- package/dist/structure/measure.d.ts +2 -2
- package/dist/structure/measure.js +4 -44
- package/dist/structure/parse.js +113 -141
- package/dist/structure/partial-occupancy.js +7 -10
- package/dist/structure/pbc.d.ts +1 -0
- package/dist/structure/pbc.js +16 -6
- package/dist/structure/supercell.d.ts +2 -2
- package/dist/structure/supercell.js +12 -22
- package/dist/structure/validation.js +1 -2
- package/dist/symmetry/SymmetryStats.svelte +84 -41
- package/dist/symmetry/WyckoffTable.svelte +26 -6
- package/dist/symmetry/cell-transform.js +5 -3
- package/dist/symmetry/index.js +8 -7
- package/dist/symmetry/spacegroups.js +148 -148
- package/dist/table/HeatmapTable.svelte +790 -554
- package/dist/table/HeatmapTable.svelte.d.ts +1 -1
- package/dist/table/ToggleMenu.svelte +125 -92
- package/dist/table/index.js +2 -4
- package/dist/theme/ThemeControl.svelte +21 -12
- package/dist/time.js +4 -1
- package/dist/tooltip/TooltipContent.svelte +33 -8
- package/dist/trajectory/Trajectory.svelte +758 -558
- package/dist/trajectory/TrajectoryError.svelte +14 -3
- package/dist/trajectory/TrajectoryExportPane.svelte +137 -83
- package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
- package/dist/trajectory/extract.js +10 -26
- package/dist/trajectory/format-detect.js +5 -5
- package/dist/trajectory/frame-reader.d.ts +1 -1
- package/dist/trajectory/frame-reader.js +5 -12
- package/dist/trajectory/helpers.d.ts +0 -1
- package/dist/trajectory/helpers.js +2 -17
- package/dist/trajectory/index.js +14 -12
- package/dist/trajectory/parse/ase.js +5 -4
- package/dist/trajectory/parse/hdf5.js +26 -18
- package/dist/trajectory/parse/index.js +13 -18
- package/dist/trajectory/parse/lammps.js +17 -7
- package/dist/trajectory/parse/vasp.js +5 -2
- package/dist/trajectory/parse/xyz.js +8 -7
- package/dist/trajectory/plotting.js +13 -8
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +13 -0
- package/dist/xrd/XrdPlot.svelte +337 -247
- package/dist/xrd/broadening.js +14 -9
- package/dist/xrd/calc-xrd.js +12 -18
- package/dist/xrd/parse.d.ts +1 -1
- package/dist/xrd/parse.js +17 -17
- package/package.json +99 -103
- package/readme.md +1 -1
- /package/dist/theme/{themes.js → themes.mjs} +0 -0
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
<script lang="ts"
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Scene wrapper that enables Threlte pointer events for hover tooltips.
|
|
3
|
+
// Must be rendered inside <Canvas>, mirroring ScatterPlot3DScene / StructureScene pattern.
|
|
4
|
+
import { interactivity } from '@threlte/extras'
|
|
5
|
+
import type { Snippet } from 'svelte'
|
|
6
|
+
|
|
7
|
+
let { children }: { children?: Snippet } = $props()
|
|
8
|
+
interactivity()
|
|
6
9
|
</script>
|
|
7
10
|
|
|
8
11
|
{@render children?.()}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Async wrapper for compute_chempot_diagram via Web Worker.
|
|
2
|
+
// Falls back to synchronous main-thread computation during SSR.
|
|
3
|
+
import { compute_chempot_diagram, formula_key_from_composition } from './compute';
|
|
4
|
+
let worker = null;
|
|
5
|
+
let next_id = 0;
|
|
6
|
+
const pending = new Map();
|
|
7
|
+
const pending_by_key = new Map();
|
|
8
|
+
function make_compute_request_key(entries, config) {
|
|
9
|
+
const keyed_entries = entries
|
|
10
|
+
.map((entry) => [
|
|
11
|
+
formula_key_from_composition(entry.composition),
|
|
12
|
+
entry.energy,
|
|
13
|
+
entry.energy_per_atom ?? ``,
|
|
14
|
+
entry.e_form_per_atom ?? ``,
|
|
15
|
+
entry.is_stable ?? ``,
|
|
16
|
+
entry.e_above_hull ?? ``,
|
|
17
|
+
entry.exclude_from_hull ?? ``,
|
|
18
|
+
].join(`:`))
|
|
19
|
+
.sort();
|
|
20
|
+
return `${keyed_entries.join(`,`)}|${JSON.stringify(config)}`;
|
|
21
|
+
}
|
|
22
|
+
function track_pending(request_key, promise) {
|
|
23
|
+
pending_by_key.set(request_key, promise);
|
|
24
|
+
promise.then(() => pending_by_key.delete(request_key), () => pending_by_key.delete(request_key));
|
|
25
|
+
return promise;
|
|
26
|
+
}
|
|
27
|
+
function get_worker() {
|
|
28
|
+
if (typeof Worker === `undefined`)
|
|
29
|
+
return null;
|
|
30
|
+
if (!worker) {
|
|
31
|
+
worker = new Worker(new URL(`./chempot-worker.ts`, import.meta.url), { type: `module` });
|
|
32
|
+
worker.onmessage = ({ data: { id, result, error } }) => {
|
|
33
|
+
const req = pending.get(id);
|
|
34
|
+
if (!req)
|
|
35
|
+
return;
|
|
36
|
+
pending.delete(id);
|
|
37
|
+
if (error || !result)
|
|
38
|
+
req.reject(new Error(error ?? `Worker returned null`));
|
|
39
|
+
else
|
|
40
|
+
req.resolve(result);
|
|
41
|
+
};
|
|
42
|
+
worker.onerror = (event) => {
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
const err = new Error(event.message || `Worker initialization error`);
|
|
45
|
+
for (const req of pending.values())
|
|
46
|
+
req.reject(err);
|
|
47
|
+
pending.clear();
|
|
48
|
+
worker = null;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return worker;
|
|
52
|
+
}
|
|
53
|
+
export function compute_chempot_async(entries, config = {}) {
|
|
54
|
+
const request_key = make_compute_request_key(entries, config);
|
|
55
|
+
const existing = pending_by_key.get(request_key);
|
|
56
|
+
if (existing)
|
|
57
|
+
return existing;
|
|
58
|
+
const wkr = get_worker();
|
|
59
|
+
// SSR / no-Worker fallback: run synchronously (wrapped so throws → rejections)
|
|
60
|
+
if (!wkr) {
|
|
61
|
+
const promise = Promise.resolve().then(() => compute_chempot_diagram(entries, config));
|
|
62
|
+
return track_pending(request_key, promise);
|
|
63
|
+
}
|
|
64
|
+
const promise = new Promise((resolve, reject) => {
|
|
65
|
+
const id = ++next_id;
|
|
66
|
+
pending.set(id, { resolve, reject });
|
|
67
|
+
try {
|
|
68
|
+
// $state.snapshot strips Svelte $state proxies (not structured-cloneable)
|
|
69
|
+
wkr.postMessage($state.snapshot({ id, entries, config }));
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
pending.delete(id);
|
|
73
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
return track_pending(request_key, promise);
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { compute_chempot_diagram } from './compute';
|
|
2
|
+
self.onmessage = (event) => {
|
|
3
|
+
const { id, entries, config } = event.data;
|
|
4
|
+
try {
|
|
5
|
+
const result = compute_chempot_diagram(entries, config);
|
|
6
|
+
postMessage({ id, result, error: null });
|
|
7
|
+
}
|
|
8
|
+
catch (err) {
|
|
9
|
+
postMessage({ id, result: null, error: err instanceof Error ? err.message : String(err) });
|
|
10
|
+
}
|
|
11
|
+
};
|
|
@@ -10,8 +10,7 @@ export function make_chempot_color_scale(values, interpolator_name, reverse) {
|
|
|
10
10
|
const finite_values = values.filter(Number.isFinite);
|
|
11
11
|
if (finite_values.length === 0)
|
|
12
12
|
return null;
|
|
13
|
-
let min_value = finite_values[0];
|
|
14
|
-
let max_raw_value = finite_values[0];
|
|
13
|
+
let [min_value, max_raw_value] = [finite_values[0], finite_values[0]];
|
|
15
14
|
for (let idx = 1; idx < finite_values.length; idx++) {
|
|
16
15
|
if (finite_values[idx] < min_value)
|
|
17
16
|
min_value = finite_values[idx];
|
|
@@ -35,4 +35,14 @@ export declare function get_3d_domain_simplexes_and_ann_loc(points_3d: number[][
|
|
|
35
35
|
simplex_indices: number[][];
|
|
36
36
|
ann_loc: number[];
|
|
37
37
|
};
|
|
38
|
+
export declare function bbox_diagonal(points: number[][]): number;
|
|
39
|
+
export declare function scale_to_font_range(sizes: number[], min_font: number, max_font: number): number[];
|
|
40
|
+
export interface VisibleDomainLabel {
|
|
41
|
+
formula: string;
|
|
42
|
+
position: [number, number, number];
|
|
43
|
+
label_font_size: number;
|
|
44
|
+
}
|
|
45
|
+
export declare function get_visible_domain_labels(face_positions: ArrayLike<number>, face_domain_map: string[], label_font_size_by_formula: ReadonlyMap<string, number>, pinned_labels?: VisibleDomainLabel[]): VisibleDomainLabel[];
|
|
46
|
+
export declare function get_ternary_combinations(elements: string[]): string[][];
|
|
47
|
+
export declare function make_nd_cache_key(entries: PhaseData[], formal_chempots: boolean, default_min_limit: number, limits: ChemPotDiagramConfig[`limits`]): string;
|
|
38
48
|
export declare function compute_chempot_diagram(entries: PhaseData[], config?: ChemPotDiagramConfig): ChemPotDiagramData;
|
|
@@ -1,9 +1,39 @@
|
|
|
1
1
|
// Core computational logic for chemical potential diagrams.
|
|
2
2
|
// Ports pymatgen's ChemicalPotentialDiagram algorithm to TypeScript.
|
|
3
3
|
// Reference: pymatgen/analysis/chempot_diagram.py
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
4
|
+
import { convex_hull_2d, EPS, polygon_centroid, solve_linear_system } from '../math';
|
|
5
|
+
import { CHEMPOT_DEFAULTS } from './types';
|
|
6
|
+
// Inlined from $lib/composition/parse to keep this module worker-safe
|
|
7
|
+
// ($lib/composition barrel transitively imports binary .json.gz data
|
|
8
|
+
// that the worker bundler can't handle).
|
|
9
|
+
const count_atoms_in_composition = (composition) => Object.values(composition).reduce((sum, count) => sum + (count ?? 0), 0);
|
|
10
|
+
const gcd = (num_a, num_b) => num_b === 0 ? num_a : gcd(num_b, num_a % num_b);
|
|
11
|
+
const get_reduced_formula = (composition) => {
|
|
12
|
+
const amounts = Object.values(composition).filter((amt) => amt > 0);
|
|
13
|
+
if (amounts.length === 0)
|
|
14
|
+
return {};
|
|
15
|
+
// For fractional amounts, find smallest multiplier (1–100) that yields near-integer ratios
|
|
16
|
+
let scale = 1;
|
|
17
|
+
if (!amounts.every((amt) => Number.isInteger(amt))) {
|
|
18
|
+
scale = 0;
|
|
19
|
+
for (let mult = 1; mult <= 100; mult++) {
|
|
20
|
+
if (amounts.every((amt) => Math.abs(amt * mult - Math.round(amt * mult)) < 0.03)) {
|
|
21
|
+
scale = mult;
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (scale === 0)
|
|
26
|
+
return composition;
|
|
27
|
+
}
|
|
28
|
+
const int_amounts = amounts.map((amt) => Math.round(amt * scale));
|
|
29
|
+
const divisor = int_amounts.reduce((acc, amt) => gcd(acc, amt));
|
|
30
|
+
if (scale === 1 && divisor <= 1)
|
|
31
|
+
return composition;
|
|
32
|
+
const factor = scale / divisor;
|
|
33
|
+
return Object.fromEntries(Object.entries(composition)
|
|
34
|
+
.filter(([, amt]) => amt > 0)
|
|
35
|
+
.map(([elem, amt]) => [elem, Math.round(amt * factor)]));
|
|
36
|
+
};
|
|
7
37
|
// === Entry Helpers ===
|
|
8
38
|
// Get energy per atom for a PhaseData entry
|
|
9
39
|
export function get_energy_per_atom(entry) {
|
|
@@ -88,9 +118,7 @@ export function renormalize_entries(entries, el_refs, elements) {
|
|
|
88
118
|
const atoms = count_atoms_in_composition(entry.composition);
|
|
89
119
|
let renorm_energy = 0;
|
|
90
120
|
for (const el of elements) {
|
|
91
|
-
const frac = atoms > 0
|
|
92
|
-
? (entry.composition[el] ?? 0) / atoms
|
|
93
|
-
: 0;
|
|
121
|
+
const frac = atoms > 0 ? (entry.composition[el] ?? 0) / atoms : 0;
|
|
94
122
|
const ref = el_refs[el];
|
|
95
123
|
if (ref)
|
|
96
124
|
renorm_energy += frac * get_energy_per_atom(ref);
|
|
@@ -114,6 +142,7 @@ export function build_hyperplanes(min_entries, el_refs, elements) {
|
|
|
114
142
|
});
|
|
115
143
|
const always_include = new Set(Object.values(el_refs));
|
|
116
144
|
const tol = 1e-6; // PhaseDiagram.formation_energy_tol
|
|
145
|
+
const use_precomputed_hull = min_entries.every((entry) => typeof entry.is_stable === `boolean` || typeof entry.e_above_hull === `number`);
|
|
117
146
|
const hyperplanes = [];
|
|
118
147
|
const hyperplane_entries = [];
|
|
119
148
|
for (const entry of min_entries) {
|
|
@@ -129,7 +158,14 @@ export function build_hyperplanes(min_entries, el_refs, elements) {
|
|
|
129
158
|
ref_energy += fraction * element_ref_energies[elem_idx];
|
|
130
159
|
}
|
|
131
160
|
const form_energy = energy_per_atom - ref_energy;
|
|
132
|
-
|
|
161
|
+
const on_precomputed_hull = use_precomputed_hull &&
|
|
162
|
+
!entry.exclude_from_hull &&
|
|
163
|
+
(entry.is_stable === true ||
|
|
164
|
+
(typeof entry.e_above_hull === `number` && entry.e_above_hull <= tol));
|
|
165
|
+
const include_entry = use_precomputed_hull
|
|
166
|
+
? on_precomputed_hull || always_include.has(entry)
|
|
167
|
+
: form_energy < -tol || always_include.has(entry);
|
|
168
|
+
if (include_entry) {
|
|
133
169
|
row[n_elems] = -energy_per_atom;
|
|
134
170
|
hyperplanes.push(row);
|
|
135
171
|
hyperplane_entries.push(entry);
|
|
@@ -175,15 +211,21 @@ function solve_3x3(a, b, c, offsets, out) {
|
|
|
175
211
|
if (Math.abs(det) < EPS)
|
|
176
212
|
return false;
|
|
177
213
|
const inv = 1 / det;
|
|
178
|
-
out[0] =
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
214
|
+
out[0] =
|
|
215
|
+
(offsets[0] * (b[1] * c[2] - b[2] * c[1]) -
|
|
216
|
+
a[1] * (offsets[1] * c[2] - b[2] * offsets[2]) +
|
|
217
|
+
a[2] * (offsets[1] * c[1] - b[1] * offsets[2])) *
|
|
218
|
+
inv;
|
|
219
|
+
out[1] =
|
|
220
|
+
(a[0] * (offsets[1] * c[2] - b[2] * offsets[2]) -
|
|
221
|
+
offsets[0] * (b[0] * c[2] - b[2] * c[0]) +
|
|
222
|
+
a[2] * (b[0] * offsets[2] - offsets[1] * c[0])) *
|
|
223
|
+
inv;
|
|
224
|
+
out[2] =
|
|
225
|
+
(a[0] * (b[1] * offsets[2] - offsets[1] * c[1]) -
|
|
226
|
+
a[1] * (b[0] * offsets[2] - offsets[1] * c[0]) +
|
|
227
|
+
offsets[0] * (b[0] * c[1] - b[1] * c[0])) *
|
|
228
|
+
inv;
|
|
187
229
|
return true;
|
|
188
230
|
}
|
|
189
231
|
// Compute chemical potential domains via vertex enumeration.
|
|
@@ -207,9 +249,7 @@ export function compute_domains(hyperplanes, border_hyperplanes, hyperplane_entr
|
|
|
207
249
|
const mu = new Array(dim).fill(0);
|
|
208
250
|
const offsets = new Array(dim).fill(0);
|
|
209
251
|
// For dim <= 3, use inline solvers; for larger dims, build A on the fly
|
|
210
|
-
const A_rows = dim > 3
|
|
211
|
-
? Array.from({ length: dim }, () => new Array(dim).fill(0))
|
|
212
|
-
: [];
|
|
252
|
+
const A_rows = dim > 3 ? Array.from({ length: dim }, () => new Array(dim).fill(0)) : [];
|
|
213
253
|
// Generate all combinations of dim indices from n_total halfspaces
|
|
214
254
|
const combo = new Array(dim).fill(0);
|
|
215
255
|
for (let idx = 0; idx < dim; idx++)
|
|
@@ -233,8 +273,8 @@ export function compute_domains(hyperplanes, border_hyperplanes, hyperplane_entr
|
|
|
233
273
|
has_entry_hyperplane = combo[0] < n_entries || combo[1] < n_entries;
|
|
234
274
|
}
|
|
235
275
|
else if (dim === 3) {
|
|
236
|
-
has_entry_hyperplane =
|
|
237
|
-
combo[2] < n_entries;
|
|
276
|
+
has_entry_hyperplane =
|
|
277
|
+
combo[0] < n_entries || combo[1] < n_entries || combo[2] < n_entries;
|
|
238
278
|
}
|
|
239
279
|
else {
|
|
240
280
|
for (let row = 0; row < dim; row++) {
|
|
@@ -283,7 +323,8 @@ export function compute_domains(hyperplanes, border_hyperplanes, hyperplane_entr
|
|
|
283
323
|
const selected_hs_idx_2 = dim > 2 ? combo[2] : -1;
|
|
284
324
|
for (let idx = 0; idx < n_total; idx++) {
|
|
285
325
|
if (dim <= 3) {
|
|
286
|
-
if (idx === selected_hs_idx_0 ||
|
|
326
|
+
if (idx === selected_hs_idx_0 ||
|
|
327
|
+
idx === selected_hs_idx_1 ||
|
|
287
328
|
idx === selected_hs_idx_2)
|
|
288
329
|
continue;
|
|
289
330
|
}
|
|
@@ -317,11 +358,7 @@ export function compute_domains(hyperplanes, border_hyperplanes, hyperplane_entr
|
|
|
317
358
|
}
|
|
318
359
|
if (solved) {
|
|
319
360
|
// Assign vertex to entries whose hyperplanes are active
|
|
320
|
-
const vertex = dim === 2
|
|
321
|
-
? [mu[0], mu[1]]
|
|
322
|
-
: dim === 3
|
|
323
|
-
? [mu[0], mu[1], mu[2]]
|
|
324
|
-
: [...mu];
|
|
361
|
+
const vertex = dim === 2 ? [mu[0], mu[1]] : dim === 3 ? [mu[0], mu[1], mu[2]] : [...mu];
|
|
325
362
|
for (let idx = 0; idx < dim; idx++) {
|
|
326
363
|
const hs_idx = combo[idx];
|
|
327
364
|
if (hs_idx < n_entries) {
|
|
@@ -376,8 +413,7 @@ export function pad_domain_points(pts, elem_indices, new_lims, default_min_limit
|
|
|
376
413
|
// Build per-axis min/max ranges for a set of points
|
|
377
414
|
export function build_axis_ranges(points, elements) {
|
|
378
415
|
return elements.map((element, axis_idx) => {
|
|
379
|
-
let min_val = Infinity;
|
|
380
|
-
let max_val = -Infinity;
|
|
416
|
+
let [min_val, max_val] = [Infinity, -Infinity];
|
|
381
417
|
for (const point of points) {
|
|
382
418
|
const val = point[axis_idx];
|
|
383
419
|
if (val < min_val)
|
|
@@ -518,8 +554,7 @@ export function get_3d_domain_simplexes_and_ann_loc(points_3d) {
|
|
|
518
554
|
// Map hull vertices back to original point indices using nearest projected
|
|
519
555
|
// vertex instead of stringified coordinates to avoid precision aliasing.
|
|
520
556
|
function nearest_projected_idx(target) {
|
|
521
|
-
let nearest_idx = 0;
|
|
522
|
-
let min_sq_dist = Infinity;
|
|
557
|
+
let [nearest_idx, min_sq_dist] = [0, Infinity];
|
|
523
558
|
for (let idx = 0; idx < pts_2d.length; idx++) {
|
|
524
559
|
const dx = pts_2d[idx][0] - target[0];
|
|
525
560
|
const dy = pts_2d[idx][1] - target[1];
|
|
@@ -542,6 +577,119 @@ export function get_3d_domain_simplexes_and_ann_loc(points_3d) {
|
|
|
542
577
|
}
|
|
543
578
|
return { simplex_indices, ann_loc };
|
|
544
579
|
}
|
|
580
|
+
// === Label Sizing ===
|
|
581
|
+
// Bounding box diagonal of a set of N-D points (Euclidean distance between
|
|
582
|
+
// the min and max corners). Returns 0 for fewer than 2 points.
|
|
583
|
+
export function bbox_diagonal(points) {
|
|
584
|
+
if (points.length < 2)
|
|
585
|
+
return 0;
|
|
586
|
+
let sq_sum = 0;
|
|
587
|
+
for (let col = 0; col < points[0].length; col++) {
|
|
588
|
+
let lo = Infinity, hi = -Infinity;
|
|
589
|
+
for (const pt of points) {
|
|
590
|
+
lo = Math.min(lo, pt[col]);
|
|
591
|
+
hi = Math.max(hi, pt[col]);
|
|
592
|
+
}
|
|
593
|
+
sq_sum += (hi - lo) ** 2;
|
|
594
|
+
}
|
|
595
|
+
return Math.sqrt(sq_sum);
|
|
596
|
+
}
|
|
597
|
+
// Map an array of raw size values to font sizes via linear interpolation.
|
|
598
|
+
// Returns a new array of font sizes in [min_font, max_font].
|
|
599
|
+
// If all sizes are equal, returns the midpoint for all.
|
|
600
|
+
export function scale_to_font_range(sizes, min_font, max_font) {
|
|
601
|
+
const min_size = Math.min(...sizes);
|
|
602
|
+
const max_size = Math.max(...sizes);
|
|
603
|
+
const range = max_size - min_size;
|
|
604
|
+
const mid = (min_font + max_font) / 2;
|
|
605
|
+
return sizes.map((size) => range > 0 ? min_font + ((max_font - min_font) * (size - min_size)) / range : mid);
|
|
606
|
+
}
|
|
607
|
+
export function get_visible_domain_labels(face_positions, face_domain_map, label_font_size_by_formula, pinned_labels = []) {
|
|
608
|
+
const n_faces = Math.min(Math.floor(face_positions.length / 9), face_domain_map.length);
|
|
609
|
+
const accum = new Map();
|
|
610
|
+
for (let face_idx = 0; face_idx < n_faces; face_idx++) {
|
|
611
|
+
const formula = face_domain_map[face_idx];
|
|
612
|
+
if (!formula || !label_font_size_by_formula.has(formula))
|
|
613
|
+
continue;
|
|
614
|
+
const base = face_idx * 9;
|
|
615
|
+
const ax = face_positions[base];
|
|
616
|
+
const ay = face_positions[base + 1];
|
|
617
|
+
const az = face_positions[base + 2];
|
|
618
|
+
const bx = face_positions[base + 3];
|
|
619
|
+
const by = face_positions[base + 4];
|
|
620
|
+
const bz = face_positions[base + 5];
|
|
621
|
+
const cx = face_positions[base + 6];
|
|
622
|
+
const cy = face_positions[base + 7];
|
|
623
|
+
const cz = face_positions[base + 8];
|
|
624
|
+
const abx = bx - ax;
|
|
625
|
+
const aby = by - ay;
|
|
626
|
+
const abz = bz - az;
|
|
627
|
+
const acx = cx - ax;
|
|
628
|
+
const acy = cy - ay;
|
|
629
|
+
const acz = cz - az;
|
|
630
|
+
const cross_x = aby * acz - abz * acy;
|
|
631
|
+
const cross_y = abz * acx - abx * acz;
|
|
632
|
+
const cross_z = abx * acy - aby * acx;
|
|
633
|
+
const area = Math.hypot(cross_x, cross_y, cross_z) / 2;
|
|
634
|
+
if (area <= EPS)
|
|
635
|
+
continue;
|
|
636
|
+
const centroid_x = (ax + bx + cx) / 3;
|
|
637
|
+
const centroid_y = (ay + by + cy) / 3;
|
|
638
|
+
const centroid_z = (az + bz + cz) / 3;
|
|
639
|
+
const entry = accum.get(formula) ?? { area: 0, x: 0, y: 0, z: 0 };
|
|
640
|
+
entry.area += area;
|
|
641
|
+
entry.x += centroid_x * area;
|
|
642
|
+
entry.y += centroid_y * area;
|
|
643
|
+
entry.z += centroid_z * area;
|
|
644
|
+
accum.set(formula, entry);
|
|
645
|
+
}
|
|
646
|
+
const visible_labels = [...accum.entries()]
|
|
647
|
+
.filter(([, entry]) => entry.area > EPS)
|
|
648
|
+
.map(([formula, entry]) => ({
|
|
649
|
+
formula,
|
|
650
|
+
position: [entry.x / entry.area, entry.y / entry.area, entry.z / entry.area],
|
|
651
|
+
label_font_size: label_font_size_by_formula.get(formula) ?? 12,
|
|
652
|
+
}));
|
|
653
|
+
for (const label of pinned_labels) {
|
|
654
|
+
if (!visible_labels.some((visible_label) => visible_label.formula === label.formula)) {
|
|
655
|
+
visible_labels.push(label);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return visible_labels.sort((label_a, label_b) => label_a.formula.localeCompare(label_b.formula));
|
|
659
|
+
}
|
|
660
|
+
// === Ternary Combinations ===
|
|
661
|
+
// Generate all C(n,3) ternary element combinations from a sorted element list.
|
|
662
|
+
// Each triplet is sorted alphabetically. Returns empty array for fewer than 3 elements.
|
|
663
|
+
export function get_ternary_combinations(elements) {
|
|
664
|
+
const sorted = [...elements].sort();
|
|
665
|
+
const n_elems = sorted.length;
|
|
666
|
+
if (n_elems < 3)
|
|
667
|
+
return [];
|
|
668
|
+
const combos = [];
|
|
669
|
+
for (let first = 0; first < n_elems - 2; first++) {
|
|
670
|
+
for (let second = first + 1; second < n_elems - 1; second++) {
|
|
671
|
+
for (let third = second + 1; third < n_elems; third++) {
|
|
672
|
+
combos.push([sorted[first], sorted[second], sorted[third]]);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return combos;
|
|
677
|
+
}
|
|
678
|
+
let _nd_cache = null;
|
|
679
|
+
// Content-based fingerprint for N-D result caching. Uses sorted formula keys
|
|
680
|
+
// so deserialized Web Worker copies match and different compositions never collide.
|
|
681
|
+
export function make_nd_cache_key(entries, formal_chempots, default_min_limit, limits) {
|
|
682
|
+
const keyed = entries
|
|
683
|
+
.map((entry) => [
|
|
684
|
+
formula_key_from_composition(entry.composition),
|
|
685
|
+
entry.energy,
|
|
686
|
+
entry.is_stable ?? ``,
|
|
687
|
+
entry.e_above_hull ?? ``,
|
|
688
|
+
entry.exclude_from_hull ?? ``,
|
|
689
|
+
].join(`:`))
|
|
690
|
+
.sort();
|
|
691
|
+
return `${keyed.join(`,`)}|${formal_chempots}|${default_min_limit}|${JSON.stringify(limits ?? {})}`;
|
|
692
|
+
}
|
|
545
693
|
// === Main Pipeline ===
|
|
546
694
|
// Compute the full chemical potential diagram from entries and config.
|
|
547
695
|
// Returns domains, elements, refs, and all intermediate data.
|
|
@@ -564,69 +712,83 @@ export function compute_chempot_diagram(entries, config = {}) {
|
|
|
564
712
|
}
|
|
565
713
|
const all_data_elements = Array.from(all_data_elements_set).sort();
|
|
566
714
|
// Display elements: user-specified order (controls axis mapping), or auto-detect
|
|
567
|
-
const display_elements = config.elements?.length
|
|
568
|
-
? [...config.elements]
|
|
569
|
-
: all_data_elements;
|
|
715
|
+
const display_elements = config.elements?.length ? [...config.elements] : all_data_elements;
|
|
570
716
|
// Projection mode: display fewer axes than the data has elements
|
|
571
717
|
// In this mode, compute in full N-D and project afterward
|
|
572
718
|
const is_projection = display_elements.length < all_data_elements.length &&
|
|
573
719
|
display_elements.every((el) => all_data_elements.includes(el));
|
|
574
720
|
// Computation elements: full element set for projection, display set for subsystem
|
|
575
721
|
const compute_elements = is_projection ? all_data_elements : display_elements;
|
|
576
|
-
//
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
722
|
+
// Try cache for projection mode (same entries + config = same N-D domains)
|
|
723
|
+
const cache_key = is_projection
|
|
724
|
+
? make_nd_cache_key(entries, formal_chempots, default_min_limit, limits)
|
|
725
|
+
: ``;
|
|
726
|
+
let nd_result = is_projection && _nd_cache?.key === cache_key ? _nd_cache.result : null;
|
|
727
|
+
if (!nd_result) {
|
|
728
|
+
// In subsystem mode, filter entries to only those within the element set
|
|
729
|
+
let working_entries = entries;
|
|
730
|
+
if (!is_projection && config.elements?.length) {
|
|
731
|
+
const target_set = new Set(config.elements);
|
|
732
|
+
working_entries = entries.filter((entry) => {
|
|
733
|
+
for (const [element, amount] of Object.entries(entry.composition)) {
|
|
734
|
+
if (amount > 0 && !target_set.has(element))
|
|
735
|
+
return false;
|
|
736
|
+
}
|
|
737
|
+
return true;
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
// Sort entries by composition (Schwartzian transform to avoid recomputing keys)
|
|
741
|
+
const sorted_entries = working_entries
|
|
742
|
+
.map((entry) => ({ entry, key: formula_key_from_composition(entry.composition) }))
|
|
743
|
+
.sort((a, b) => a.key.localeCompare(b.key))
|
|
744
|
+
.map(({ entry }) => entry);
|
|
745
|
+
// Get min entries and elemental references
|
|
746
|
+
let { min_entries, el_refs } = get_min_entries_and_el_refs(sorted_entries);
|
|
747
|
+
const dim = compute_elements.length;
|
|
748
|
+
if (dim < 2) {
|
|
749
|
+
throw new Error(`ChemicalPotentialDiagram requires 2+ elements, got ${dim}`);
|
|
750
|
+
}
|
|
751
|
+
// Check all elemental refs exist for the computation elements
|
|
752
|
+
const missing_refs = compute_elements.filter((el) => !el_refs[el]);
|
|
753
|
+
if (missing_refs.length > 0) {
|
|
754
|
+
throw new Error(`Missing elemental reference entries for: ${missing_refs.join(`, `)}`);
|
|
755
|
+
}
|
|
756
|
+
// Renormalize if using formal chemical potentials
|
|
757
|
+
if (formal_chempots) {
|
|
758
|
+
min_entries = renormalize_entries(min_entries, el_refs, compute_elements);
|
|
759
|
+
const renorm_result = get_min_entries_and_el_refs(min_entries);
|
|
760
|
+
el_refs = renorm_result.el_refs;
|
|
761
|
+
}
|
|
762
|
+
// Build limits array for computation elements
|
|
763
|
+
const compute_lims = compute_elements.map((el) => {
|
|
764
|
+
if (limits?.[el])
|
|
765
|
+
return limits[el];
|
|
766
|
+
return [default_min_limit, 0];
|
|
586
767
|
});
|
|
768
|
+
// Build hyperplanes and compute domains in full dimensionality
|
|
769
|
+
const { hyperplanes, hyperplane_entries } = build_hyperplanes(min_entries, el_refs, compute_elements);
|
|
770
|
+
const border_hyperplanes = build_border_hyperplanes(compute_lims);
|
|
771
|
+
const domains = compute_domains(hyperplanes, border_hyperplanes, hyperplane_entries, dim);
|
|
772
|
+
nd_result = {
|
|
773
|
+
domains,
|
|
774
|
+
el_refs,
|
|
775
|
+
min_entries,
|
|
776
|
+
hyperplanes,
|
|
777
|
+
hyperplane_entries,
|
|
778
|
+
compute_lims,
|
|
779
|
+
};
|
|
780
|
+
// Cache for projection mode reuse
|
|
781
|
+
if (is_projection) {
|
|
782
|
+
_nd_cache = { key: cache_key, result: nd_result };
|
|
783
|
+
}
|
|
587
784
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
.map((entry) => ({ entry, key: formula_key_from_composition(entry.composition) }))
|
|
591
|
-
.sort((a, b) => a.key.localeCompare(b.key))
|
|
592
|
-
.map(({ entry }) => entry);
|
|
593
|
-
// Get min entries and elemental references
|
|
594
|
-
let { min_entries, el_refs } = get_min_entries_and_el_refs(sorted_entries);
|
|
595
|
-
const dim = compute_elements.length;
|
|
596
|
-
if (dim < 2) {
|
|
597
|
-
throw new Error(`ChemicalPotentialDiagram requires 2+ elements, got ${dim}`);
|
|
598
|
-
}
|
|
599
|
-
// Check all elemental refs exist for the computation elements
|
|
600
|
-
const missing_refs = compute_elements.filter((el) => !el_refs[el]);
|
|
601
|
-
if (missing_refs.length > 0) {
|
|
602
|
-
throw new Error(`Missing elemental reference entries for: ${missing_refs.join(`, `)}`);
|
|
603
|
-
}
|
|
604
|
-
// Renormalize if using formal chemical potentials
|
|
605
|
-
if (formal_chempots) {
|
|
606
|
-
min_entries = renormalize_entries(min_entries, el_refs, compute_elements);
|
|
607
|
-
const renorm_result = get_min_entries_and_el_refs(min_entries);
|
|
608
|
-
el_refs = renorm_result.el_refs;
|
|
609
|
-
}
|
|
610
|
-
// Build limits array for computation elements
|
|
611
|
-
const compute_lims = compute_elements.map((el) => {
|
|
612
|
-
if (limits?.[el])
|
|
613
|
-
return limits[el];
|
|
614
|
-
return [default_min_limit, 0];
|
|
615
|
-
});
|
|
616
|
-
// Build hyperplanes and compute domains in full dimensionality
|
|
617
|
-
const { hyperplanes, hyperplane_entries } = build_hyperplanes(min_entries, el_refs, compute_elements);
|
|
618
|
-
const border_hyperplanes = build_border_hyperplanes(compute_lims);
|
|
619
|
-
let domains = compute_domains(hyperplanes, border_hyperplanes, hyperplane_entries, dim);
|
|
785
|
+
let domains = nd_result.domains;
|
|
786
|
+
let output_lims = nd_result.compute_lims;
|
|
620
787
|
// Project domain vertices from N-D to display axes (column extraction)
|
|
621
|
-
let output_lims = compute_lims;
|
|
622
788
|
if (is_projection) {
|
|
623
|
-
const compute_index_by_element = new Map();
|
|
624
|
-
for (let idx = 0; idx < compute_elements.length; idx++) {
|
|
625
|
-
compute_index_by_element.set(compute_elements[idx], idx);
|
|
626
|
-
}
|
|
627
789
|
const col_indices = display_elements.map((element) => {
|
|
628
|
-
const idx =
|
|
629
|
-
if (idx
|
|
790
|
+
const idx = compute_elements.indexOf(element);
|
|
791
|
+
if (idx < 0) {
|
|
630
792
|
throw new Error(`Display element ${element} not present in compute element set`);
|
|
631
793
|
}
|
|
632
794
|
return idx;
|
|
@@ -636,15 +798,15 @@ export function compute_chempot_diagram(entries, config = {}) {
|
|
|
636
798
|
projected[formula] = pts.map((pt) => col_indices.map((idx) => pt[idx]));
|
|
637
799
|
}
|
|
638
800
|
domains = projected;
|
|
639
|
-
output_lims = col_indices.map((
|
|
801
|
+
output_lims = col_indices.map((col_idx) => nd_result.compute_lims[col_idx]);
|
|
640
802
|
}
|
|
641
803
|
return {
|
|
642
804
|
domains,
|
|
643
805
|
elements: display_elements,
|
|
644
|
-
el_refs,
|
|
645
|
-
min_entries,
|
|
646
|
-
hyperplanes,
|
|
647
|
-
hyperplane_entries,
|
|
806
|
+
el_refs: nd_result.el_refs,
|
|
807
|
+
min_entries: nd_result.min_entries,
|
|
808
|
+
hyperplanes: nd_result.hyperplanes,
|
|
809
|
+
hyperplane_entries: nd_result.hyperplane_entries,
|
|
648
810
|
lims: output_lims,
|
|
649
811
|
};
|
|
650
812
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as ChemPotDiagram } from './ChemPotDiagram.svelte';
|
|
2
2
|
export { default as ChemPotDiagram2D } from './ChemPotDiagram2D.svelte';
|
|
3
3
|
export { default as ChemPotDiagram3D } from './ChemPotDiagram3D.svelte';
|
|
4
|
-
export {
|
|
4
|
+
export { compute_chempot_async } from './async-compute.svelte';
|
|
5
|
+
export { compute_chempot_diagram, get_ternary_combinations } from './compute';
|
|
5
6
|
export * from './types';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as ChemPotDiagram } from './ChemPotDiagram.svelte';
|
|
2
2
|
export { default as ChemPotDiagram2D } from './ChemPotDiagram2D.svelte';
|
|
3
3
|
export { default as ChemPotDiagram3D } from './ChemPotDiagram3D.svelte';
|
|
4
|
-
export {
|
|
4
|
+
export { compute_chempot_async } from './async-compute.svelte';
|
|
5
|
+
export { compute_chempot_diagram, get_ternary_combinations } from './compute';
|
|
5
6
|
export * from './types';
|
|
@@ -3,15 +3,14 @@ import { CHEMPOT_DEFAULTS } from './types';
|
|
|
3
3
|
export function get_projection_source_entries(entries, temp_filtered_entries) {
|
|
4
4
|
return temp_filtered_entries.length > 0 ? temp_filtered_entries : entries;
|
|
5
5
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
6
|
+
const resolve_temp_filter_options = (config, props) => ({
|
|
7
|
+
interpolate: config.interpolate_temperature ??
|
|
8
|
+
props.interpolate_temperature ??
|
|
9
|
+
CHEMPOT_DEFAULTS.interpolate_temperature,
|
|
10
|
+
max_interpolation_gap: config.max_interpolation_gap ??
|
|
11
|
+
props.max_interpolation_gap ??
|
|
12
|
+
CHEMPOT_DEFAULTS.max_interpolation_gap,
|
|
13
|
+
});
|
|
15
14
|
export function get_temp_filter_payload(entries, temperature, config, props) {
|
|
16
15
|
const { has_temp_data, available_temperatures } = analyze_temperature_data(entries);
|
|
17
16
|
if (!has_temp_data || temperature === undefined) {
|
|
@@ -2,6 +2,7 @@ import type { D3InterpolateName } from '../colors';
|
|
|
2
2
|
import type { PhaseData } from '../convex-hull/types';
|
|
3
3
|
export type ChemPotLimits = Partial<Record<string, [number, number]>>;
|
|
4
4
|
export type ChemPotColorMode = `none` | `energy` | `formation_energy` | `arity` | `entries`;
|
|
5
|
+
export type ChemPotProjectionMode = `single` | `grid`;
|
|
5
6
|
export interface ChemPotDiagramConfig {
|
|
6
7
|
formal_chempots?: boolean;
|
|
7
8
|
default_min_limit?: number;
|
|
@@ -20,6 +21,7 @@ export interface ChemPotDiagramConfig {
|
|
|
20
21
|
reverse_color_scale?: boolean;
|
|
21
22
|
interpolate_temperature?: boolean;
|
|
22
23
|
max_interpolation_gap?: number;
|
|
24
|
+
projection_mode?: ChemPotProjectionMode;
|
|
23
25
|
}
|
|
24
26
|
export interface ChemPotDiagramData {
|
|
25
27
|
domains: Record<string, number[][]>;
|
|
@@ -79,5 +81,6 @@ export declare const CHEMPOT_DEFAULTS: {
|
|
|
79
81
|
readonly reverse_color_scale: true;
|
|
80
82
|
readonly interpolate_temperature: true;
|
|
81
83
|
readonly max_interpolation_gap: 500;
|
|
84
|
+
readonly projection_mode: ChemPotProjectionMode;
|
|
82
85
|
readonly formula_colors: readonly ["#1b9e77", "#d95f02", "#7570b3", "#e7298a", "#66a61e", "#e6ab02", "#a6761d", "#666666"];
|
|
83
86
|
};
|