matterviz 0.3.3 → 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 +6 -5
- 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/element/data.js +1 -1
- 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 +23 -38
- 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;
|