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
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import InfoPaneCards from '../overlays/InfoPaneCards.svelte'
|
|
2
3
|
import DraggablePane from '../overlays/DraggablePane.svelte'
|
|
4
|
+
import { format_num } from '../labels'
|
|
3
5
|
import type { ComponentProps } from 'svelte'
|
|
4
6
|
import type { HTMLAttributes } from 'svelte/elements'
|
|
5
7
|
import ConvexHullStats from './ConvexHullStats.svelte'
|
|
6
8
|
import type { ConvexHullEntry, PhaseStats } from './types'
|
|
7
9
|
|
|
10
|
+
const usage_tips = [
|
|
11
|
+
{ label: `Single click`, value: `Select point`, key: `tip-click` },
|
|
12
|
+
{ label: `Double click`, value: `Copy info`, key: `tip-double-click` },
|
|
13
|
+
{ label: `Drag`, value: `Rotate view`, key: `tip-drag` },
|
|
14
|
+
{ label: `Scroll`, value: `Zoom in/out`, key: `tip-scroll` },
|
|
15
|
+
{ label: `Key r`, value: `Reset camera`, key: `tip-reset` },
|
|
16
|
+
{ label: `Key b`, value: `Toggle color mode`, key: `tip-color-mode` },
|
|
17
|
+
{ label: `Key s`, value: `Toggle stable points`, key: `tip-stable` },
|
|
18
|
+
{ label: `Key u`, value: `Toggle unstable points`, key: `tip-unstable` },
|
|
19
|
+
{ label: `Key l`, value: `Toggle labels`, key: `tip-labels` },
|
|
20
|
+
]
|
|
21
|
+
|
|
8
22
|
let {
|
|
9
23
|
phase_stats,
|
|
10
24
|
stable_entries,
|
|
@@ -27,6 +41,51 @@
|
|
|
27
41
|
toggle_props?: ComponentProps<typeof DraggablePane>[`toggle_props`]
|
|
28
42
|
pane_props?: ComponentProps<typeof DraggablePane>[`pane_props`]
|
|
29
43
|
} = $props()
|
|
44
|
+
|
|
45
|
+
const count_visible = (entries: ConvexHullEntry[]): number =>
|
|
46
|
+
entries.reduce((count, entry) => count + Number(entry.visible), 0)
|
|
47
|
+
|
|
48
|
+
let settings_rows = $derived([
|
|
49
|
+
{
|
|
50
|
+
label: `Visible stable`,
|
|
51
|
+
value: `${count_visible(stable_entries)} / ${stable_entries.length}`,
|
|
52
|
+
key: `hull-visible-stable`,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
label: `Visible unstable`,
|
|
56
|
+
value: `${count_visible(unstable_entries)} / ${unstable_entries.length}`,
|
|
57
|
+
key: `hull-visible-unstable`,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
label: `Points threshold`,
|
|
61
|
+
value: `${format_num(max_hull_dist_show_phases, `.3~f`)} eV/atom`,
|
|
62
|
+
key: `hull-show-threshold`,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
label: `Label threshold`,
|
|
66
|
+
value: `${format_num(max_hull_dist_show_labels, `.3~f`)} eV/atom`,
|
|
67
|
+
key: `hull-label-threshold`,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
label: `Entry limit for labels`,
|
|
71
|
+
value: `${label_threshold} entries`,
|
|
72
|
+
key: `hull-entry-limit-labels`,
|
|
73
|
+
},
|
|
74
|
+
])
|
|
75
|
+
|
|
76
|
+
let info_cards = $derived([
|
|
77
|
+
{ title: `Visualization Settings`, rows: settings_rows },
|
|
78
|
+
{ title: `Usage Tips`, rows: usage_tips },
|
|
79
|
+
])
|
|
80
|
+
|
|
81
|
+
const info_card_style = [
|
|
82
|
+
`--info-card-padding: 3pt`,
|
|
83
|
+
`--info-card-bg: var(--pane-bg, white)`,
|
|
84
|
+
`--info-card-heading-gap: 6px`,
|
|
85
|
+
`--info-row-padding: 1pt`,
|
|
86
|
+
`--row-label-max: 1fr`,
|
|
87
|
+
`--info-row-label-color: var(--text-color-muted, #666)`,
|
|
88
|
+
].join(`; `)
|
|
30
89
|
</script>
|
|
31
90
|
|
|
32
91
|
<DraggablePane
|
|
@@ -49,67 +108,15 @@
|
|
|
49
108
|
{phase_stats}
|
|
50
109
|
{stable_entries}
|
|
51
110
|
{unstable_entries}
|
|
52
|
-
style="padding: 3pt; background: var(--pane-bg)"
|
|
111
|
+
style="padding: 3pt; background: var(--pane-bg); --hull-stats-table-height: 30rem"
|
|
53
112
|
/>
|
|
54
113
|
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
<div class="setting-item" data-testid="hull-visible-unstable">
|
|
64
|
-
<span>Visible unstable:</span>
|
|
65
|
-
<span>{unstable_entries.filter((entry) => entry.visible).length} / {
|
|
66
|
-
unstable_entries.length
|
|
67
|
-
}</span>
|
|
68
|
-
</div>
|
|
69
|
-
<div class="setting-item" data-testid="hull-show-threshold">
|
|
70
|
-
<span>Points threshold:</span>
|
|
71
|
-
<span>{max_hull_dist_show_phases.toFixed(3)} eV/atom</span>
|
|
72
|
-
</div>
|
|
73
|
-
<div class="setting-item" data-testid="hull-label-threshold">
|
|
74
|
-
<span>Label threshold:</span>
|
|
75
|
-
<span>{max_hull_dist_show_labels.toFixed(3)} eV/atom</span>
|
|
76
|
-
</div>
|
|
77
|
-
<div class="setting-item" data-testid="hull-entry-limit-labels">
|
|
78
|
-
<span>Entry limit for labels:</span>
|
|
79
|
-
<span>{label_threshold} entries</span>
|
|
80
|
-
</div>
|
|
81
|
-
</section>
|
|
82
|
-
|
|
83
|
-
<section class="usage-tips">
|
|
84
|
-
<h5 id="usage-tips">Usage Tips</h5>
|
|
85
|
-
<div class="tips-item"><span>Single click:</span><span>Select point</span></div>
|
|
86
|
-
<div class="tips-item"><span>Double click:</span><span>Copy info</span></div>
|
|
87
|
-
<div class="tips-item"><span>Drag:</span><span>Rotate view</span></div>
|
|
88
|
-
<div class="tips-item"><span>Scroll:</span><span>Zoom in/out</span></div>
|
|
89
|
-
<div class="tips-item"><span>Key 'r':</span><span>Reset camera</span></div>
|
|
90
|
-
<div class="tips-item"><span>Key 'b':</span><span>Toggle color mode</span></div>
|
|
91
|
-
<div class="tips-item"><span>Key 's':</span><span>Toggle stable points</span></div>
|
|
92
|
-
<div class="tips-item"><span>Key 'u':</span><span>Toggle unstable points</span></div>
|
|
93
|
-
<div class="tips-item"><span>Key 'l':</span><span>Toggle labels</span></div>
|
|
94
|
-
</section>
|
|
114
|
+
<InfoPaneCards
|
|
115
|
+
cards={info_cards}
|
|
116
|
+
filter_placeholder="Filter hull info"
|
|
117
|
+
empty_label="hull info"
|
|
118
|
+
heading_level={5}
|
|
119
|
+
row_label_min="7em"
|
|
120
|
+
style={info_card_style}
|
|
121
|
+
/>
|
|
95
122
|
</DraggablePane>
|
|
96
|
-
|
|
97
|
-
<style>
|
|
98
|
-
.vis-settings, .usage-tips {
|
|
99
|
-
padding: 3pt;
|
|
100
|
-
background: var(--pane-bg, white);
|
|
101
|
-
}
|
|
102
|
-
.vis-settings h5, .usage-tips h5 {
|
|
103
|
-
margin: 0 0 6px 0;
|
|
104
|
-
}
|
|
105
|
-
.setting-item, .tips-item {
|
|
106
|
-
display: flex;
|
|
107
|
-
justify-content: space-between;
|
|
108
|
-
gap: 6pt;
|
|
109
|
-
padding: 1pt;
|
|
110
|
-
line-height: 1.5;
|
|
111
|
-
}
|
|
112
|
-
.setting-item span:first-child, .tips-item span:first-child {
|
|
113
|
-
color: var(--text-color-muted, #666);
|
|
114
|
-
}
|
|
115
|
-
</style>
|
|
@@ -49,6 +49,16 @@
|
|
|
49
49
|
// Formula filter: when set, table shows only entries with this reduced formula
|
|
50
50
|
let formula_filter = $state(``)
|
|
51
51
|
let show_export_dropdown = $state(false)
|
|
52
|
+
const table_scroll_height =
|
|
53
|
+
`var(--hull-stats-table-height, calc(var(--hull-stats-table-row-height, 2.35rem) * 10 + var(--hull-stats-table-header-height, 3.5rem)))`
|
|
54
|
+
const table_scroll_style = $derived(layout === `side-by-side`
|
|
55
|
+
? `flex: 1 1 0; height: ${table_scroll_height}; min-height: ${table_scroll_height}; max-width: 100%; overflow: auto`
|
|
56
|
+
: `height: ${table_scroll_height}; min-height: ${table_scroll_height}; max-height: var(--hull-stats-max-height, 70vh); max-width: 100%; overflow: auto`
|
|
57
|
+
)
|
|
58
|
+
const table_root_style = $derived(layout === `side-by-side`
|
|
59
|
+
? `flex: 1 1 0; min-height: ${table_scroll_height}; margin-inline: 0`
|
|
60
|
+
: `min-width: 0; margin-inline: 0`
|
|
61
|
+
)
|
|
52
62
|
|
|
53
63
|
async function copy_to_clipboard(label: string, value: string, key: string) {
|
|
54
64
|
try {
|
|
@@ -638,13 +648,9 @@
|
|
|
638
648
|
data={table_data}
|
|
639
649
|
columns={table_columns}
|
|
640
650
|
initial_sort={{ column: `E<sub>hull</sub>`, direction: `asc` }}
|
|
641
|
-
scroll_style={
|
|
642
|
-
? `flex: 1 1 0; max-width: 100%; overflow: auto`
|
|
643
|
-
: `max-height: var(--hull-stats-max-height, 500px)`}
|
|
651
|
+
scroll_style={table_scroll_style}
|
|
644
652
|
style="width: 100%"
|
|
645
|
-
root_style={
|
|
646
|
-
? `flex: 1 1 0; min-height: 0; margin-inline: 0`
|
|
647
|
-
: undefined}
|
|
653
|
+
root_style={table_root_style}
|
|
648
654
|
onrowclick={on_entry_click ? handle_row_click : undefined}
|
|
649
655
|
export_data={false}
|
|
650
656
|
/>
|
|
@@ -681,6 +687,9 @@
|
|
|
681
687
|
.convex-hull-stats {
|
|
682
688
|
background: var(--hull-stats-bg, var(--hull-bg));
|
|
683
689
|
border-radius: var(--hull-border-radius, var(--border-radius, 3pt));
|
|
690
|
+
box-sizing: border-box;
|
|
691
|
+
max-width: 100%;
|
|
692
|
+
overflow: hidden;
|
|
684
693
|
padding: var(--hull-stats-padding, 1em);
|
|
685
694
|
}
|
|
686
695
|
.convex-hull-stats.side-by-side {
|
|
@@ -700,6 +709,7 @@
|
|
|
700
709
|
.table-pane {
|
|
701
710
|
flex: 1 1 0;
|
|
702
711
|
max-width: 100%;
|
|
712
|
+
min-height: var(--hull-stats-table-height, calc(var(--hull-stats-table-row-height, 2.35rem) * 10 + var(--hull-stats-table-header-height, 3.5rem)));
|
|
703
713
|
min-width: 0;
|
|
704
714
|
overflow: auto;
|
|
705
715
|
display: flex;
|
|
@@ -747,9 +757,15 @@
|
|
|
747
757
|
.view-toggle {
|
|
748
758
|
display: flex;
|
|
749
759
|
margin-bottom: 8pt;
|
|
760
|
+
max-width: 100%;
|
|
761
|
+
min-width: 0;
|
|
750
762
|
}
|
|
751
763
|
.view-toggle button {
|
|
764
|
+
display: inline-flex;
|
|
765
|
+
align-items: center;
|
|
766
|
+
justify-content: center;
|
|
752
767
|
flex: 1;
|
|
768
|
+
min-width: 0;
|
|
753
769
|
padding: 2pt 8pt;
|
|
754
770
|
border: 1px solid
|
|
755
771
|
var(--hull-stats-border-color, color-mix(in srgb, currentColor 20%, transparent));
|
|
@@ -757,6 +773,7 @@
|
|
|
757
773
|
color: inherit;
|
|
758
774
|
cursor: pointer;
|
|
759
775
|
font-size: 0.85em;
|
|
776
|
+
text-align: center;
|
|
760
777
|
}
|
|
761
778
|
.view-toggle button:first-child {
|
|
762
779
|
border-radius: 4pt 0 0 4pt;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
GasThermodynamicsConfig,
|
|
6
6
|
} from './types'
|
|
7
7
|
import { sanitize_html } from '../sanitize'
|
|
8
|
-
import { tooltip } from 'svelte-multiselect'
|
|
8
|
+
import { tooltip } from 'svelte-multiselect/attachments'
|
|
9
9
|
import type { HTMLAttributes } from 'svelte/elements'
|
|
10
10
|
import {
|
|
11
11
|
compute_gas_chemical_potential,
|
|
@@ -94,8 +94,8 @@
|
|
|
94
94
|
const P = slider_to_pressure(+(event.currentTarget as HTMLInputElement).value)
|
|
95
95
|
pressures = { ...pressures, [gas]: P }
|
|
96
96
|
// Clear only this gas's preview (don't reset other sliders being dragged simultaneously)
|
|
97
|
-
const { [gas]:
|
|
98
|
-
preview_pressures =
|
|
97
|
+
const { [gas]: _removed_preview, ...remaining_previews } = preview_pressures
|
|
98
|
+
preview_pressures = remaining_previews
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
function set_pressure_direct(gas: GasSpecies, value: number): void {
|
|
@@ -144,7 +144,7 @@ export const TETRAHEDRON_VERTICES = [
|
|
|
144
144
|
];
|
|
145
145
|
export function composition_to_barycentric_4d(composition, elements) {
|
|
146
146
|
if (elements.length !== 4) {
|
|
147
|
-
throw new Error(`Quaternary barycentric coordinates require exactly
|
|
147
|
+
throw new Error(`Quaternary barycentric coordinates require exactly 4 elements`);
|
|
148
148
|
}
|
|
149
149
|
const amounts = elements.map((el) => composition[el] || 0);
|
|
150
150
|
const total = amounts.reduce((sum, amount) => sum + amount, 0);
|
|
@@ -168,7 +168,7 @@ export function barycentric_to_tetrahedral(barycentric) {
|
|
|
168
168
|
}
|
|
169
169
|
export function compute_4d_coords(entries, elements) {
|
|
170
170
|
if (elements.length !== 4) {
|
|
171
|
-
throw new Error(`Quaternary convex hull requires exactly
|
|
171
|
+
throw new Error(`Quaternary convex hull requires exactly 4 elements`);
|
|
172
172
|
}
|
|
173
173
|
// Use Set for O(1) lookups instead of O(n) includes
|
|
174
174
|
const element_set = new Set(elements);
|
|
@@ -26,13 +26,17 @@ export const is_unary_entry = (entry) => get_arity(entry) === 1;
|
|
|
26
26
|
export function get_energy_color_scale(color_mode, color_scale, plot_entries) {
|
|
27
27
|
if (color_mode !== `energy` || plot_entries.length === 0)
|
|
28
28
|
return null;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
let lo = Number.POSITIVE_INFINITY;
|
|
30
|
+
let hi_raw = 0.1;
|
|
31
|
+
for (const entry of plot_entries) {
|
|
32
|
+
const val = entry.e_above_hull;
|
|
33
|
+
if (typeof val !== `number` || !Number.isFinite(val))
|
|
34
|
+
continue;
|
|
35
|
+
lo = Math.min(lo, val);
|
|
36
|
+
hi_raw = Math.max(hi_raw, val);
|
|
37
|
+
}
|
|
38
|
+
if (!Number.isFinite(lo))
|
|
33
39
|
return null;
|
|
34
|
-
const lo = Math.min(...hull_distances);
|
|
35
|
-
const hi_raw = Math.max(...hull_distances, 0.1);
|
|
36
40
|
const hi = Math.max(hi_raw, lo + 1e-6);
|
|
37
41
|
const interpolator = get_d3_interpolator(color_scale);
|
|
38
42
|
return scaleSequential(interpolator).domain([lo, hi]);
|
|
@@ -70,10 +74,14 @@ export async function parse_hull_entries_from_drop(event) {
|
|
|
70
74
|
export function calc_max_hull_dist_in_data(processed_entries) {
|
|
71
75
|
if (processed_entries.length === 0)
|
|
72
76
|
return 0.5;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
let max_hull_dist = 0;
|
|
78
|
+
for (const entry of processed_entries) {
|
|
79
|
+
const val = entry.e_above_hull;
|
|
80
|
+
if (typeof val === `number` && Number.isFinite(val)) {
|
|
81
|
+
max_hull_dist = Math.max(max_hull_dist, val);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const max_val = max_hull_dist + 0.001;
|
|
77
85
|
return Math.max(0.1, max_val);
|
|
78
86
|
}
|
|
79
87
|
// Smart threshold for showing unstable entries based on entry count.
|
|
@@ -157,8 +165,18 @@ energy_source_mode) {
|
|
|
157
165
|
const has_precomputed_e_form = entries.length > 0 && entries.every((entry) => typeof entry.e_form_per_atom === `number`);
|
|
158
166
|
const has_precomputed_hull = entries.length > 0 && entries.every((entry) => typeof entry.e_above_hull === `number`);
|
|
159
167
|
const unary_refs = find_lowest_energy_unary_refs_fn(entries);
|
|
160
|
-
const elements_in_entries =
|
|
161
|
-
const
|
|
168
|
+
const elements_in_entries = new Set();
|
|
169
|
+
for (const entry of entries) {
|
|
170
|
+
for (const el of Object.keys(entry.composition))
|
|
171
|
+
elements_in_entries.add(el);
|
|
172
|
+
}
|
|
173
|
+
let can_compute_e_form = true;
|
|
174
|
+
for (const el of elements_in_entries) {
|
|
175
|
+
if (!unary_refs[el]) {
|
|
176
|
+
can_compute_e_form = false;
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
162
180
|
const can_compute_hull = can_compute_e_form;
|
|
163
181
|
// Resolve mode to avoid inconsistent states:
|
|
164
182
|
// - If full precomputed available, honor user toggle
|
|
@@ -467,30 +485,30 @@ export function get_canvas_text_color(dark_mode, element) {
|
|
|
467
485
|
}
|
|
468
486
|
// Create a Path2D for a marker symbol. Uses d3-shape for consistent rendering with ScatterPlot.
|
|
469
487
|
export function create_marker_path(size, marker = `circle`) {
|
|
488
|
+
const safe_size = Number.isFinite(size) ? size : 0;
|
|
489
|
+
const rounded_size = Math.max(0, Number(safe_size.toFixed(3)));
|
|
470
490
|
// Capitalize first letter to get D3 symbol name (e.g. 'circle' -> 'Circle')
|
|
471
491
|
const d3_name = marker.charAt(0).toUpperCase() + marker.slice(1);
|
|
472
492
|
const symbol_type = symbol_map[d3_name];
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
return path;
|
|
477
|
-
}
|
|
478
|
-
const symbol_area = Math.PI * size * size;
|
|
479
|
-
const path_data = symbol().type(symbol_type).size(symbol_area)();
|
|
493
|
+
const symbol_area = Math.PI * rounded_size * rounded_size;
|
|
494
|
+
const path_data = symbol_type ? symbol().type(symbol_type).size(symbol_area)() : null;
|
|
495
|
+
const path = new Path2D(path_data ?? undefined);
|
|
480
496
|
if (!path_data) {
|
|
481
|
-
|
|
482
|
-
path.arc(0, 0, size, 0, 2 * Math.PI);
|
|
483
|
-
return path;
|
|
497
|
+
path.arc(0, 0, rounded_size, 0, 2 * Math.PI);
|
|
484
498
|
}
|
|
485
|
-
return
|
|
499
|
+
return path;
|
|
486
500
|
}
|
|
487
501
|
// Analyze entries for temperature-dependent free energy data.
|
|
488
502
|
// Returns available temperatures (union of all T values across entries) if any entries have temp data.
|
|
489
503
|
export function analyze_temperature_data(entries) {
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
504
|
+
const unique_temperatures = new Set();
|
|
505
|
+
for (const entry of entries) {
|
|
506
|
+
if (!entry_has_temp_data(entry))
|
|
507
|
+
continue;
|
|
508
|
+
for (const temperature of entry.temperatures ?? [])
|
|
509
|
+
unique_temperatures.add(temperature);
|
|
510
|
+
}
|
|
511
|
+
const available_temperatures = [...unique_temperatures].sort((a, b) => a - b);
|
|
494
512
|
return {
|
|
495
513
|
has_temp_data: available_temperatures.length > 0,
|
|
496
514
|
available_temperatures,
|
|
@@ -311,7 +311,7 @@ export function calculate_e_above_hull(input, reference_entries) {
|
|
|
311
311
|
}
|
|
312
312
|
// Ensure corner points (pure elements default to e_form = 0)
|
|
313
313
|
for (let el_idx = 0; el_idx < arity; el_idx++) {
|
|
314
|
-
const corner =
|
|
314
|
+
const corner = Array(arity + 1).fill(0);
|
|
315
315
|
corner[el_idx] = 1; // ith barycentric coord = 1
|
|
316
316
|
if (!ref_points.some((pt) => norm_nd(subtract_nd(pt, corner)) < EPS)) {
|
|
317
317
|
ref_points.push(corner);
|
|
@@ -1511,7 +1511,7 @@ function solve_linear_system(matrix_a, vec_b) {
|
|
|
1511
1511
|
}
|
|
1512
1512
|
}
|
|
1513
1513
|
// Back substitution
|
|
1514
|
-
const result =
|
|
1514
|
+
const result = Array(n).fill(0);
|
|
1515
1515
|
for (let row = n - 1; row >= 0; row--) {
|
|
1516
1516
|
let sum = aug[row][n];
|
|
1517
1517
|
for (let col = row + 1; col < n; col++) {
|
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { SVGAttributes } from 'svelte/elements'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
let {
|
|
5
|
+
symbol = ``,
|
|
6
|
+
name = ``,
|
|
7
|
+
shells,
|
|
8
|
+
adapt_size = true,
|
|
9
|
+
shell_width = 20,
|
|
10
|
+
size = adapt_size ? (shells.length + 1) * 2 * shell_width + 30 : 270,
|
|
11
|
+
base_fill = `var(--text-color)`,
|
|
12
|
+
orbital_period = 3,
|
|
13
|
+
nucleus_props = {},
|
|
14
|
+
shell_props = {},
|
|
15
|
+
electron_props = {},
|
|
16
|
+
highlight_shell = null,
|
|
17
|
+
number_electrons = false,
|
|
18
|
+
electron_label_props = {},
|
|
19
|
+
...rest
|
|
20
|
+
}: SVGAttributes<SVGSVGElement> & {
|
|
5
21
|
// https://svelte.dev/repl/17d71b590f554b5a9eba6e04023dd41c
|
|
6
22
|
symbol?: string // usually H, He, etc. but can be anything
|
|
7
23
|
name?: string // usually Hydrogen, Helium, etc. but can be anything
|
|
@@ -23,41 +39,23 @@
|
|
|
23
39
|
| `sequential`
|
|
24
40
|
| ((idx: number) => string)
|
|
25
41
|
electron_label_props?: Record<string, string | number>
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
let {
|
|
29
|
-
symbol = ``,
|
|
30
|
-
name = ``,
|
|
31
|
-
shells,
|
|
32
|
-
adapt_size = true,
|
|
33
|
-
shell_width = 20,
|
|
34
|
-
size = adapt_size ? (shells.length + 1) * 2 * shell_width + 30 : 270,
|
|
35
|
-
base_fill = `var(--text-color)`,
|
|
36
|
-
orbital_period = 3,
|
|
37
|
-
nucleus_props = {},
|
|
38
|
-
shell_props = {},
|
|
39
|
-
electron_props = {},
|
|
40
|
-
highlight_shell = null,
|
|
41
|
-
number_electrons = false,
|
|
42
|
-
electron_label_props = {},
|
|
43
|
-
...rest
|
|
44
|
-
}: Props = $props()
|
|
42
|
+
} = $props()
|
|
45
43
|
|
|
46
44
|
// Bohr atom electron orbital period is given by
|
|
47
45
|
// T = (n^3 h^3) / (4pi^2 m K e^4 Z^2) = 1.52 * 10^-16 * n^3 / Z^2 s
|
|
48
46
|
// with n the shell number, Z the atomic number, m the mass of the electron
|
|
49
|
-
let
|
|
47
|
+
let nucleus_svg_props = $derived({
|
|
50
48
|
r: 20,
|
|
51
49
|
fill: base_fill,
|
|
52
50
|
'fill-opacity': `0.3`,
|
|
53
51
|
...nucleus_props,
|
|
54
52
|
})
|
|
55
|
-
let
|
|
53
|
+
let shell_svg_props = $derived({
|
|
56
54
|
stroke: base_fill,
|
|
57
55
|
fill: `none`,
|
|
58
56
|
...shell_props,
|
|
59
57
|
})
|
|
60
|
-
let
|
|
58
|
+
let electron_svg_props = $derived({
|
|
61
59
|
r: 3,
|
|
62
60
|
stroke: base_fill,
|
|
63
61
|
fill: `blue`,
|
|
@@ -75,7 +73,7 @@
|
|
|
75
73
|
{...rest}
|
|
76
74
|
>
|
|
77
75
|
<!-- nucleus -->
|
|
78
|
-
<circle class="nucleus" {...
|
|
76
|
+
<circle class="nucleus" {...nucleus_svg_props}>
|
|
79
77
|
{#if name}
|
|
80
78
|
<title>{name}</title>
|
|
81
79
|
{/if}
|
|
@@ -87,12 +85,12 @@
|
|
|
87
85
|
<!-- electron orbitals -->
|
|
88
86
|
{#each shells as electrons, shell_idx (`${shell_idx}-${electrons}`)}
|
|
89
87
|
{@const n = shell_idx + 1}
|
|
90
|
-
{@const shell_radius =
|
|
88
|
+
{@const shell_radius = nucleus_svg_props.r + n * shell_width}
|
|
91
89
|
{@const active = n === highlight_shell}
|
|
92
90
|
<g class="shell" style:animation-duration="{orbital_period * n ** 1.5}s">
|
|
93
91
|
<circle
|
|
94
92
|
r={shell_radius}
|
|
95
|
-
{...
|
|
93
|
+
{...shell_svg_props}
|
|
96
94
|
style:stroke-width={active ? 2 : 1}
|
|
97
95
|
style:stroke={active ? `yellow` : base_fill}
|
|
98
96
|
/>
|
|
@@ -101,7 +99,7 @@
|
|
|
101
99
|
{#each Array(electrons) as _, elec_idx (elec_idx)}
|
|
102
100
|
{@const elec_x = Math.cos((2 * Math.PI * elec_idx) / electrons) * shell_radius}
|
|
103
101
|
{@const elec_y = Math.sin((2 * Math.PI * elec_idx) / electrons) * shell_radius}
|
|
104
|
-
<circle class="electron" cx={elec_x} cy={elec_y} {...
|
|
102
|
+
<circle class="electron" cx={elec_x} cy={elec_y} {...electron_svg_props}>
|
|
105
103
|
<title>Electron {elec_idx + 1}</title>
|
|
106
104
|
</circle>
|
|
107
105
|
{#if number_electrons}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SVGAttributes } from 'svelte/elements';
|
|
2
|
-
type
|
|
2
|
+
type $$ComponentProps = SVGAttributes<SVGSVGElement> & {
|
|
3
3
|
symbol?: string;
|
|
4
4
|
name?: string;
|
|
5
5
|
shells: number[];
|
|
@@ -15,6 +15,6 @@ type Props = SVGAttributes<SVGSVGElement> & {
|
|
|
15
15
|
number_electrons?: boolean | `hierarchical` | `sequential` | ((idx: number) => string);
|
|
16
16
|
electron_label_props?: Record<string, string | number>;
|
|
17
17
|
};
|
|
18
|
-
declare const BohrAtom: import("svelte").Component
|
|
18
|
+
declare const BohrAtom: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
19
19
|
type BohrAtom = ReturnType<typeof BohrAtom>;
|
|
20
20
|
export default BohrAtom;
|
package/dist/element/data.d.ts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
export default _default;
|
|
1
|
+
import data from './data.json.gz';
|
|
2
|
+
export default data;
|
|
@@ -492,7 +492,7 @@
|
|
|
492
492
|
offset={{ x: 12, y: -12 }}
|
|
493
493
|
bg_color={hover_data.surface_color}
|
|
494
494
|
fixed
|
|
495
|
-
style="z-index: 100000001; backdrop-filter: blur(4px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3)"
|
|
495
|
+
style="z-index: var(--z-index-overlay-nav, 100000001); backdrop-filter: blur(4px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3)"
|
|
496
496
|
>
|
|
497
497
|
<FermiSurfaceTooltip {hover_data} tooltip={tooltip_config} />
|
|
498
498
|
</PlotTooltip>
|
|
@@ -555,7 +555,10 @@
|
|
|
555
555
|
top: var(--fermi-buttons-top, var(--ctrl-btn-top, 1ex));
|
|
556
556
|
right: var(--fermi-buttons-right, var(--ctrl-btn-right, 1ex));
|
|
557
557
|
gap: clamp(6pt, 1cqmin, 9pt);
|
|
558
|
-
z-index: var(
|
|
558
|
+
z-index: var(
|
|
559
|
+
--fermi-buttons-z-index,
|
|
560
|
+
var(--z-index-overlay-controls, 100000000)
|
|
561
|
+
);
|
|
559
562
|
opacity: 0;
|
|
560
563
|
pointer-events: none;
|
|
561
564
|
transition: opacity 0.2s ease;
|
|
@@ -71,10 +71,10 @@ function upsample_grid(grid, factor) {
|
|
|
71
71
|
for (let iz = 0; iz < new_nz; iz++)
|
|
72
72
|
fz_arr[iz] = (iz / new_nz) * nz;
|
|
73
73
|
// Preallocate output grid
|
|
74
|
-
const new_grid =
|
|
74
|
+
const new_grid = Array(new_nx);
|
|
75
75
|
for (let ix = 0; ix < new_nx; ix++) {
|
|
76
76
|
const fx = fx_arr[ix];
|
|
77
|
-
const iy_arr =
|
|
77
|
+
const iy_arr = Array(new_ny);
|
|
78
78
|
for (let iy = 0; iy < new_ny; iy++) {
|
|
79
79
|
const fy = fy_arr[iy];
|
|
80
80
|
const iz_arr = new Float64Array(new_nz);
|
|
@@ -492,7 +492,7 @@ function slice_surface_with_plane(surface, plane_normal, plane_distance, in_plan
|
|
|
492
492
|
const last = contour_points[contour_points.length - 1];
|
|
493
493
|
const is_closed = math.euclidean_dist(first, last) < CLOSED_CONTOUR_TOLERANCE;
|
|
494
494
|
// Project to 2D (inlined dot product for speed)
|
|
495
|
-
const points_2d =
|
|
495
|
+
const points_2d = Array(contour_points.length);
|
|
496
496
|
for (let idx = 0; idx < contour_points.length; idx++) {
|
|
497
497
|
const point = contour_points[idx];
|
|
498
498
|
points_2d[idx] = [
|
|
@@ -97,10 +97,10 @@ function parse_bxsf(content) {
|
|
|
97
97
|
throw new Error(`Band ${band_idx}: expected ${total_points} values, got ${energy_values.length}`);
|
|
98
98
|
}
|
|
99
99
|
// Reshape into 3D grid [kx][ky][kz] (preallocated for speed)
|
|
100
|
-
const band_grid =
|
|
100
|
+
const band_grid = Array(nx);
|
|
101
101
|
let val_idx = 0;
|
|
102
102
|
for (let ix = 0; ix < nx; ix++) {
|
|
103
|
-
const iy_arr =
|
|
103
|
+
const iy_arr = Array(ny);
|
|
104
104
|
for (let iy = 0; iy < ny; iy++) {
|
|
105
105
|
const iz_arr = energy_values.slice(val_idx, val_idx + nz);
|
|
106
106
|
val_idx += nz;
|
|
@@ -29,7 +29,7 @@ function generate_oh_symmetry_matrices() {
|
|
|
29
29
|
for (const perm of AXIS_PERMUTATIONS) {
|
|
30
30
|
for (const sign of SIGN_COMBINATIONS) {
|
|
31
31
|
// Column-major 4x4 matrix: element at (row, col) is at index col*4 + row
|
|
32
|
-
const mat =
|
|
32
|
+
const mat = Array(16).fill(0);
|
|
33
33
|
mat[15] = 1; // w component
|
|
34
34
|
// Build 3x3 rotation part: M[row][col] = sign[row] if perm[row] === col
|
|
35
35
|
for (let col = 0; col < 3; col++) {
|
|
@@ -286,8 +286,8 @@
|
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
-
const col_has_data =
|
|
290
|
-
const row_has_data =
|
|
289
|
+
const col_has_data = Array(x_items.length).fill(false)
|
|
290
|
+
const row_has_data = Array(y_items.length).fill(false)
|
|
291
291
|
for (let y_idx = 0; y_idx < y_items.length; y_idx++) {
|
|
292
292
|
for (let x_idx = 0; x_idx < x_items.length; x_idx++) {
|
|
293
293
|
if (get_value(x_idx, y_idx) !== null) {
|
|
@@ -437,7 +437,7 @@
|
|
|
437
437
|
let n_x = $derived(x_items.length)
|
|
438
438
|
let bg_flat = $derived.by(() => {
|
|
439
439
|
const n_y = y_items.length
|
|
440
|
-
const colors
|
|
440
|
+
const colors: (string | null)[] = Array(n_x * n_y)
|
|
441
441
|
for (let y_idx = 0; y_idx < n_y; y_idx++) {
|
|
442
442
|
const row_offset = y_idx * n_x
|
|
443
443
|
for (let x_idx = 0; x_idx < n_x; x_idx++) {
|
|
@@ -1010,11 +1010,11 @@
|
|
|
1010
1010
|
grid_offset_top = first_rendered_cell.offsetTop - vis_row * tile_stride_px
|
|
1011
1011
|
}
|
|
1012
1012
|
|
|
1013
|
-
function compute_summary(
|
|
1014
|
-
if (!
|
|
1015
|
-
if (summary_fn) return summary_fn(
|
|
1016
|
-
const total =
|
|
1017
|
-
return total /
|
|
1013
|
+
function compute_summary(summary_values: number[]): number | null {
|
|
1014
|
+
if (!summary_values.length) return null
|
|
1015
|
+
if (summary_fn) return summary_fn(summary_values)
|
|
1016
|
+
const total = summary_values.reduce((sum, value) => sum + value, 0)
|
|
1017
|
+
return total / summary_values.length
|
|
1018
1018
|
}
|
|
1019
1019
|
|
|
1020
1020
|
function summarize_axis_values(
|