matterviz 0.3.1 → 0.3.2
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 +37 -20
- package/dist/Icon.svelte +2 -2
- package/dist/app.css +29 -0
- package/dist/brillouin/BrillouinZone.svelte +19 -61
- package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneExportPane.svelte +12 -20
- package/dist/brillouin/BrillouinZoneScene.svelte +2 -2
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
- package/dist/chempot-diagram/ChemPotDiagram.svelte +192 -0
- package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +677 -0
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2688 -0
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
- package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -0
- package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
- package/dist/chempot-diagram/color.d.ts +10 -0
- package/dist/chempot-diagram/color.js +33 -0
- package/dist/chempot-diagram/compute.d.ts +38 -0
- package/dist/chempot-diagram/compute.js +650 -0
- package/dist/chempot-diagram/index.d.ts +5 -0
- package/dist/chempot-diagram/index.js +5 -0
- package/dist/chempot-diagram/pointer.d.ts +16 -0
- package/dist/chempot-diagram/pointer.js +40 -0
- package/dist/chempot-diagram/temperature.d.ts +15 -0
- package/dist/chempot-diagram/temperature.js +37 -0
- package/dist/chempot-diagram/types.d.ts +83 -0
- package/dist/chempot-diagram/types.js +27 -0
- package/dist/colors/index.d.ts +3 -1
- package/dist/colors/index.js +4 -0
- package/dist/composition/BarChart.svelte +13 -22
- package/dist/composition/BubbleChart.svelte +5 -3
- package/dist/composition/FormulaFilter.svelte +586 -94
- package/dist/composition/FormulaFilter.svelte.d.ts +35 -1
- package/dist/composition/PieChart.svelte +43 -18
- package/dist/composition/PieChart.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull.svelte +4 -2
- package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull2D.svelte +13 -44
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +16 -7
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +17 -7
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullStats.svelte +701 -226
- package/dist/convex-hull/ConvexHullStats.svelte.d.ts +6 -1
- package/dist/convex-hull/ConvexHullTooltip.svelte +1 -0
- package/dist/convex-hull/demo-temperature.d.ts +6 -0
- package/dist/convex-hull/demo-temperature.js +36 -0
- package/dist/convex-hull/helpers.d.ts +1 -1
- package/dist/convex-hull/helpers.js +2 -4
- package/dist/convex-hull/index.d.ts +1 -0
- package/dist/convex-hull/index.js +1 -0
- package/dist/convex-hull/thermodynamics.d.ts +8 -21
- package/dist/convex-hull/thermodynamics.js +106 -17
- package/dist/convex-hull/types.d.ts +5 -0
- package/dist/convex-hull/types.js +5 -0
- package/dist/coordination/CoordinationBarPlot.svelte +29 -46
- package/dist/element/BohrAtom.svelte +1 -1
- package/dist/element/data.js +2 -14
- package/dist/element/data.json.gz +0 -0
- package/dist/element/types.d.ts +1 -0
- package/dist/fermi-surface/FermiSurface.svelte +20 -64
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
- package/dist/fermi-surface/parse.js +16 -22
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +1273 -0
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +171 -0
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +31 -0
- package/dist/heatmap-matrix/index.d.ts +53 -0
- package/dist/heatmap-matrix/index.js +100 -0
- package/dist/heatmap-matrix/shared.d.ts +2 -0
- package/dist/heatmap-matrix/shared.js +4 -0
- package/dist/icons.d.ts +111 -0
- package/dist/icons.js +111 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/io/export.js +15 -3
- package/dist/io/file-drop.d.ts +7 -0
- package/dist/io/file-drop.js +43 -0
- package/dist/io/index.d.ts +2 -2
- package/dist/io/index.js +2 -112
- package/dist/io/types.d.ts +1 -0
- package/dist/io/url-drop.d.ts +2 -0
- package/dist/io/url-drop.js +118 -0
- package/dist/isosurface/Isosurface.svelte +101 -45
- package/dist/isosurface/IsosurfaceControls.svelte +19 -0
- package/dist/isosurface/parse.js +73 -30
- package/dist/isosurface/slice.d.ts +2 -1
- package/dist/isosurface/slice.js +3 -3
- package/dist/isosurface/types.d.ts +13 -1
- package/dist/isosurface/types.js +98 -0
- package/dist/labels.d.ts +2 -1
- package/dist/labels.js +1 -0
- package/dist/layout/InfoTag.svelte +62 -62
- package/dist/layout/SubpageGrid.svelte +74 -0
- package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
- package/dist/layout/index.d.ts +1 -0
- package/dist/layout/index.js +1 -0
- package/dist/layout/json-tree/JsonNode.svelte +83 -85
- package/dist/layout/json-tree/JsonTree.svelte +20 -19
- package/dist/layout/json-tree/JsonTree.svelte.d.ts +1 -1
- package/dist/layout/json-tree/JsonValue.svelte +196 -116
- package/dist/layout/json-tree/types.d.ts +10 -2
- package/dist/layout/json-tree/utils.d.ts +2 -0
- package/dist/layout/json-tree/utils.js +33 -0
- package/dist/math.d.ts +7 -0
- package/dist/math.js +358 -7
- package/dist/overlays/ContextMenu.svelte +3 -2
- package/dist/overlays/DraggablePane.svelte +163 -58
- package/dist/overlays/DraggablePane.svelte.d.ts +2 -0
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +232 -77
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -2
- package/dist/phase-diagram/PhaseDiagramControls.svelte +32 -11
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +3 -2
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +103 -0
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +102 -95
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +7 -0
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +100 -26
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +6 -3
- package/dist/phase-diagram/index.d.ts +2 -0
- package/dist/phase-diagram/index.js +2 -0
- package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
- package/dist/phase-diagram/svg-to-diagram.js +865 -0
- package/dist/phase-diagram/types.d.ts +10 -0
- package/dist/phase-diagram/utils.d.ts +7 -4
- package/dist/phase-diagram/utils.js +149 -59
- package/dist/plot/AxisLabel.svelte +26 -0
- package/dist/plot/AxisLabel.svelte.d.ts +16 -0
- package/dist/plot/BarPlot.svelte +473 -228
- package/dist/plot/BarPlot.svelte.d.ts +3 -3
- package/dist/plot/BarPlotControls.svelte +3 -2
- package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ColorBar.svelte +54 -54
- package/dist/plot/ColorBar.svelte.d.ts +1 -1
- package/dist/plot/ColorScaleSelect.svelte +1 -1
- package/dist/plot/ElementScatter.svelte +3 -2
- package/dist/plot/FillArea.svelte +4 -1
- package/dist/plot/Histogram.svelte +320 -230
- package/dist/plot/Histogram.svelte.d.ts +2 -2
- package/dist/plot/HistogramControls.svelte +29 -10
- package/dist/plot/HistogramControls.svelte.d.ts +6 -2
- package/dist/plot/InteractiveAxisLabel.svelte.d.ts +2 -2
- package/dist/plot/PlotControls.svelte +109 -27
- package/dist/plot/PlotControls.svelte.d.ts +1 -1
- package/dist/plot/PlotLegend.svelte +1 -1
- package/dist/plot/PortalSelect.svelte +2 -1
- package/dist/plot/ReferenceLine.svelte +2 -1
- package/dist/plot/ReferenceLine.svelte.d.ts +1 -0
- package/dist/plot/ReferencePlane.svelte +1 -3
- package/dist/plot/ScatterPlot.svelte +343 -209
- package/dist/plot/ScatterPlot.svelte.d.ts +3 -3
- package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3DControls.svelte +203 -250
- package/dist/plot/ScatterPlot3DScene.svelte +4 -7
- package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlotControls.svelte +95 -55
- package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ZeroLines.svelte +44 -0
- package/dist/plot/ZeroLines.svelte.d.ts +32 -0
- package/dist/plot/ZoomRect.svelte +21 -0
- package/dist/plot/ZoomRect.svelte.d.ts +8 -0
- package/dist/plot/axis-utils.d.ts +1 -1
- package/dist/plot/index.d.ts +6 -2
- package/dist/plot/index.js +6 -2
- package/dist/plot/interactions.d.ts +8 -10
- package/dist/plot/interactions.js +2 -3
- package/dist/plot/layout.d.ts +7 -1
- package/dist/plot/layout.js +12 -4
- package/dist/plot/reference-line.d.ts +4 -21
- package/dist/plot/reference-line.js +7 -81
- package/dist/plot/types.d.ts +42 -17
- package/dist/plot/types.js +10 -0
- package/dist/plot/utils/label-placement.js +13 -10
- package/dist/plot/utils.d.ts +1 -0
- package/dist/plot/utils.js +14 -0
- package/dist/rdf/RdfPlot.svelte +55 -66
- package/dist/settings.d.ts +3 -0
- package/dist/settings.js +17 -3
- package/dist/spectral/Bands.svelte +515 -143
- package/dist/spectral/Bands.svelte.d.ts +22 -2
- package/dist/spectral/helpers.d.ts +23 -1
- package/dist/spectral/helpers.js +65 -9
- package/dist/spectral/types.d.ts +2 -0
- package/dist/structure/AtomLegend.svelte +29 -8
- package/dist/structure/AtomLegend.svelte.d.ts +1 -1
- package/dist/structure/CellSelect.svelte +92 -22
- package/dist/structure/Structure.svelte +108 -118
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureControls.svelte +25 -22
- package/dist/structure/StructureControls.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +7 -1
- package/dist/structure/StructureScene.svelte +104 -66
- package/dist/structure/StructureScene.svelte.d.ts +2 -1
- package/dist/structure/atom-properties.d.ts +6 -2
- package/dist/structure/atom-properties.js +38 -25
- package/dist/structure/export.js +10 -7
- package/dist/structure/ferrox-wasm-types.d.ts +3 -2
- package/dist/structure/ferrox-wasm-types.js +0 -3
- package/dist/structure/ferrox-wasm.d.ts +3 -2
- package/dist/structure/ferrox-wasm.js +1 -2
- package/dist/structure/index.d.ts +6 -0
- package/dist/structure/index.js +22 -0
- package/dist/structure/parse.js +19 -16
- package/dist/structure/partial-occupancy.d.ts +25 -0
- package/dist/structure/partial-occupancy.js +102 -0
- package/dist/structure/validation.js +6 -3
- package/dist/symmetry/SymmetryStats.svelte +18 -4
- package/dist/symmetry/WyckoffTable.svelte +18 -10
- package/dist/symmetry/index.d.ts +7 -4
- package/dist/symmetry/index.js +83 -18
- package/dist/table/HeatmapTable.svelte +425 -65
- package/dist/table/HeatmapTable.svelte.d.ts +12 -1
- package/dist/table/ToggleMenu.svelte +2 -0
- package/dist/table/index.d.ts +2 -0
- package/dist/trajectory/Trajectory.svelte +147 -145
- package/dist/trajectory/TrajectoryExportPane.svelte +13 -9
- package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +1 -1
- package/dist/trajectory/constants.d.ts +6 -0
- package/dist/trajectory/constants.js +7 -0
- package/dist/trajectory/extract.js +3 -5
- package/dist/trajectory/format-detect.d.ts +9 -0
- package/dist/trajectory/format-detect.js +76 -0
- package/dist/trajectory/frame-reader.d.ts +17 -0
- package/dist/trajectory/frame-reader.js +339 -0
- package/dist/trajectory/helpers.d.ts +15 -0
- package/dist/trajectory/helpers.js +187 -0
- package/dist/trajectory/index.d.ts +1 -0
- package/dist/trajectory/index.js +11 -4
- package/dist/trajectory/parse/ase.d.ts +2 -0
- package/dist/trajectory/parse/ase.js +76 -0
- package/dist/trajectory/parse/hdf5.d.ts +2 -0
- package/dist/trajectory/parse/hdf5.js +121 -0
- package/dist/trajectory/parse/index.d.ts +12 -0
- package/dist/trajectory/parse/index.js +304 -0
- package/dist/trajectory/parse/lammps.d.ts +5 -0
- package/dist/trajectory/parse/lammps.js +169 -0
- package/dist/trajectory/parse/vasp.d.ts +2 -0
- package/dist/trajectory/parse/vasp.js +65 -0
- package/dist/trajectory/parse/xyz.d.ts +2 -0
- package/dist/trajectory/parse/xyz.js +109 -0
- package/dist/trajectory/types.d.ts +11 -0
- package/dist/trajectory/types.js +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +4 -0
- package/dist/xrd/XrdPlot.svelte +6 -4
- package/dist/xrd/calc-xrd.js +0 -1
- package/package.json +30 -24
- package/readme.md +4 -4
- package/dist/trajectory/parse.d.ts +0 -42
- package/dist/trajectory/parse.js +0 -1267
- /package/dist/element/{data.json.d.ts → data.json.gz.d.ts} +0 -0
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
<script lang="ts">import { PLOT_COLORS } from '../colors';
|
|
2
|
+
import EmptyState from '../EmptyState.svelte';
|
|
3
|
+
import { format_num } from '../labels';
|
|
4
|
+
import { SettingsSection } from '../layout';
|
|
2
5
|
import ScatterPlot from '../plot/ScatterPlot.svelte';
|
|
3
6
|
import * as helpers from './helpers';
|
|
4
7
|
import { SvelteMap } from 'svelte/reactivity';
|
|
5
|
-
let { band_structs, line_kwargs = {}, path_mode = `strict`, band_type = undefined, show_legend = true, x_axis = {}, y_axis = $bindable({}), x_positions = $bindable(), reference_frequency = null, ribbon_config = {}, fermi_level = undefined, ...rest } = $props();
|
|
8
|
+
let { band_structs, line_kwargs = {}, path_mode = `strict`, band_type = undefined, show_legend = true, x_axis = {}, y_axis = $bindable({}), x_positions = $bindable(), reference_frequency = null, ribbon_config = {}, fermi_level = undefined, units = $bindable(`THz`), band_spin_mode = $bindable(`overlay`), highlight_regions = [], shade_imaginary_modes = true, show_gap_annotation = true, show_controls = true, show_path_mode_control = true, show_units_control = true, show_spin_control = true, show_annotation_controls = true, id = undefined, class: class_name = undefined, style = undefined, 'data-testid': data_testid = undefined, ...rest } = $props();
|
|
9
|
+
const is_dom_attr_value = (attr_value) => typeof attr_value === `string` ||
|
|
10
|
+
typeof attr_value === `number` ||
|
|
11
|
+
typeof attr_value === `boolean`;
|
|
6
12
|
// Helper function to get line styling for a band
|
|
7
|
-
function get_line_style(color, is_acoustic,
|
|
13
|
+
function get_line_style(color, is_acoustic, frequencies, band_idx) {
|
|
8
14
|
const defaults = { stroke: color, stroke_width: is_acoustic ? 1.5 : 1 };
|
|
9
15
|
if (typeof line_kwargs === `function`) {
|
|
10
16
|
const custom = line_kwargs(frequencies, band_idx);
|
|
@@ -14,7 +20,8 @@ function get_line_style(color, is_acoustic, mode_type, frequencies, band_idx) {
|
|
|
14
20
|
};
|
|
15
21
|
}
|
|
16
22
|
if (typeof line_kwargs === `object` && line_kwargs !== null) {
|
|
17
|
-
const
|
|
23
|
+
const mode_key = is_acoustic ? `acoustic` : `optical`;
|
|
24
|
+
const mode_kwargs = line_kwargs[mode_key];
|
|
18
25
|
const source = (mode_kwargs ?? line_kwargs);
|
|
19
26
|
return {
|
|
20
27
|
stroke: source.stroke ?? defaults.stroke,
|
|
@@ -103,10 +110,23 @@ let effective_fermi_level = $derived.by(() => {
|
|
|
103
110
|
const efermi = source?.efermi;
|
|
104
111
|
return typeof efermi === `number` ? efermi : undefined;
|
|
105
112
|
});
|
|
106
|
-
|
|
107
|
-
|
|
113
|
+
let effective_spin_mode = $derived.by(() => {
|
|
114
|
+
if (detected_band_type !== `electronic`)
|
|
115
|
+
return null;
|
|
116
|
+
return (band_spin_mode === `up_only` || band_spin_mode === `down_only`)
|
|
117
|
+
? band_spin_mode
|
|
118
|
+
: `overlay`;
|
|
119
|
+
});
|
|
120
|
+
const convert_band_values = (values) => {
|
|
121
|
+
if (detected_band_type !== `phonon`)
|
|
122
|
+
return values;
|
|
123
|
+
if (units === `THz`)
|
|
124
|
+
return values;
|
|
125
|
+
return helpers.convert_frequencies(values, units);
|
|
126
|
+
};
|
|
127
|
+
// Collect all path segments across structures once (shared by strict checks and plotting)
|
|
128
|
+
let all_segments = $derived.by(() => {
|
|
108
129
|
const all_segments = {};
|
|
109
|
-
// Collect all segments from all structures
|
|
110
130
|
for (const [label, bs] of Object.entries(band_structs_dict)) {
|
|
111
131
|
for (const branch of bs.branches) {
|
|
112
132
|
const start_label = bs.qpoints[branch.start_index]?.label ?? undefined;
|
|
@@ -116,27 +136,49 @@ let segments_to_plot = $derived.by(() => {
|
|
|
116
136
|
all_segments[segment_key].push([label, bs]);
|
|
117
137
|
}
|
|
118
138
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
139
|
+
return all_segments;
|
|
140
|
+
});
|
|
141
|
+
let num_structures = $derived(Object.keys(band_structs_dict).length);
|
|
142
|
+
let all_segment_keys = $derived(Object.keys(all_segments));
|
|
143
|
+
let common_segment_keys = $derived.by(() => all_segment_keys.filter((segment_key) => all_segments[segment_key].length === num_structures));
|
|
144
|
+
let empty_state_attrs = $derived.by(() => {
|
|
145
|
+
const attrs = {};
|
|
146
|
+
for (const [attr_name, attr_value] of Object.entries(rest)) {
|
|
147
|
+
if ((attr_name === `role` || attr_name.startsWith(`aria-`)) &&
|
|
148
|
+
is_dom_attr_value(attr_value)) {
|
|
149
|
+
attrs[attr_name] = attr_value;
|
|
128
150
|
}
|
|
129
|
-
return new Set(common_segments);
|
|
130
151
|
}
|
|
131
|
-
|
|
132
|
-
|
|
152
|
+
return attrs;
|
|
153
|
+
});
|
|
154
|
+
// Compute path mismatch details for strict mode handling
|
|
155
|
+
let strict_path_error = $derived.by(() => {
|
|
156
|
+
if (path_mode !== `strict`)
|
|
157
|
+
return null;
|
|
158
|
+
return common_segment_keys.length === all_segment_keys.length
|
|
159
|
+
? null
|
|
160
|
+
: `Band structures have different q-point paths. Switch to path_mode="union" or "intersection" to compare non-identical paths.`;
|
|
161
|
+
});
|
|
162
|
+
// Determine which segments to plot based on path_mode
|
|
163
|
+
let segments_to_plot = $derived.by(() => {
|
|
164
|
+
if (path_mode === `union`)
|
|
165
|
+
return new Set(all_segment_keys);
|
|
166
|
+
return new Set(common_segment_keys);
|
|
133
167
|
});
|
|
134
168
|
// Map segments to x-axis positions
|
|
135
169
|
$effect(() => {
|
|
170
|
+
if (Object.keys(band_structs_dict).length === 0 || segments_to_plot.size === 0) {
|
|
171
|
+
x_positions = {};
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
136
174
|
const positions = {};
|
|
137
175
|
let current_x = 0;
|
|
138
176
|
// Preserve physical path order using the first available structure
|
|
139
177
|
const canonical = Object.values(band_structs_dict)[0];
|
|
178
|
+
if (!canonical) {
|
|
179
|
+
x_positions = {};
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
140
182
|
const ordered_segments = helpers.get_ordered_segments(canonical, segments_to_plot);
|
|
141
183
|
for (let seg_idx = 0; seg_idx < ordered_segments.length; seg_idx++) {
|
|
142
184
|
const segment_key = ordered_segments[seg_idx];
|
|
@@ -169,15 +211,19 @@ $effect(() => {
|
|
|
169
211
|
}
|
|
170
212
|
x_positions = positions;
|
|
171
213
|
});
|
|
172
|
-
// Convert band structures to scatter plot series
|
|
173
|
-
let series_data = $derived.by(() => {
|
|
214
|
+
// Convert band structures to scatter plot series + track max slope in one pass
|
|
215
|
+
let { series_data, max_abs_slope } = $derived.by(() => {
|
|
174
216
|
if (Object.keys(band_structs_dict).length === 0 || segments_to_plot.size === 0) {
|
|
175
|
-
return [];
|
|
217
|
+
return { series_data: [], max_abs_slope: 1 };
|
|
176
218
|
}
|
|
177
219
|
const all_series = [];
|
|
220
|
+
let max_slope = 0;
|
|
178
221
|
for (const [bs_idx, [label, bs]] of Object.entries(band_structs_dict).entries()) {
|
|
179
222
|
const color = PLOT_COLORS[bs_idx % PLOT_COLORS.length];
|
|
180
223
|
const structure_label = label || `Structure ${bs_idx + 1}`;
|
|
224
|
+
const gamma_indices = detected_band_type === `phonon`
|
|
225
|
+
? helpers.find_gamma_indices(bs)
|
|
226
|
+
: [];
|
|
181
227
|
for (const branch of bs.branches) {
|
|
182
228
|
const start_idx = branch.start_index;
|
|
183
229
|
const end_idx = branch.end_index + 1;
|
|
@@ -194,25 +240,73 @@ let series_data = $derived.by(() => {
|
|
|
194
240
|
// Scale distances for this segment
|
|
195
241
|
const segment_distances = bs.distance.slice(start_idx, end_idx);
|
|
196
242
|
const scaled_distances = helpers.scale_segment_distances(segment_distances, x_start, x_end);
|
|
197
|
-
// Create series for each band
|
|
243
|
+
// Create series for each band (and spin channel for electronic structures)
|
|
198
244
|
for (let band_idx = 0; band_idx < bs.nb_bands; band_idx++) {
|
|
199
|
-
const frequencies = bs.bands[band_idx].slice(start_idx, end_idx);
|
|
200
|
-
const is_acoustic =
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
245
|
+
const frequencies = convert_band_values(bs.bands[band_idx].slice(start_idx, end_idx));
|
|
246
|
+
const is_acoustic = helpers.classify_acoustic(bs, band_idx, gamma_indices);
|
|
247
|
+
const line_style_up = get_line_style(color, is_acoustic === true, frequencies, band_idx);
|
|
248
|
+
const spin_down_band = bs.spin_down_bands?.[band_idx];
|
|
249
|
+
const has_spin_down_channel = detected_band_type === `electronic` &&
|
|
250
|
+
Array.isArray(spin_down_band) &&
|
|
251
|
+
spin_down_band.length >= end_idx;
|
|
252
|
+
const track_max_slope = (meta) => {
|
|
253
|
+
for (const pt of meta) {
|
|
254
|
+
if (typeof pt.slope === `number` && Number.isFinite(pt.slope)) {
|
|
255
|
+
max_slope = Math.max(max_slope, Math.abs(pt.slope));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
if (effective_spin_mode !== `down_only`) {
|
|
260
|
+
const meta = helpers.build_point_metadata({
|
|
261
|
+
x_vals: scaled_distances,
|
|
262
|
+
y_vals: frequencies,
|
|
263
|
+
band_idx,
|
|
264
|
+
spin: `up`,
|
|
265
|
+
is_acoustic,
|
|
266
|
+
bs,
|
|
267
|
+
start_idx,
|
|
268
|
+
});
|
|
269
|
+
track_max_slope(meta);
|
|
270
|
+
all_series.push({
|
|
271
|
+
x: scaled_distances,
|
|
272
|
+
y: frequencies,
|
|
273
|
+
markers: `line`,
|
|
274
|
+
label: has_spin_down_channel
|
|
275
|
+
? `${structure_label} (↑)`
|
|
276
|
+
: structure_label,
|
|
277
|
+
line_style: line_style_up,
|
|
278
|
+
metadata: meta,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
if (has_spin_down_channel && effective_spin_mode !== `up_only`) {
|
|
282
|
+
const spin_down_frequencies = convert_band_values(spin_down_band.slice(start_idx, end_idx));
|
|
283
|
+
const meta = helpers.build_point_metadata({
|
|
284
|
+
x_vals: scaled_distances,
|
|
285
|
+
y_vals: spin_down_frequencies,
|
|
286
|
+
band_idx,
|
|
287
|
+
spin: `down`,
|
|
288
|
+
is_acoustic,
|
|
289
|
+
bs,
|
|
290
|
+
start_idx,
|
|
291
|
+
});
|
|
292
|
+
track_max_slope(meta);
|
|
293
|
+
all_series.push({
|
|
294
|
+
x: scaled_distances,
|
|
295
|
+
y: spin_down_frequencies,
|
|
296
|
+
markers: `line`,
|
|
297
|
+
label: `${structure_label} (↓)`,
|
|
298
|
+
line_style: {
|
|
299
|
+
...line_style_up,
|
|
300
|
+
line_dash: `4,2`,
|
|
301
|
+
stroke_width: Math.max(1, line_style_up.stroke_width - 0.1),
|
|
302
|
+
},
|
|
303
|
+
metadata: meta,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
212
306
|
}
|
|
213
307
|
}
|
|
214
308
|
}
|
|
215
|
-
return all_series;
|
|
309
|
+
return { series_data: all_series, max_abs_slope: max_slope || 1 };
|
|
216
310
|
});
|
|
217
311
|
// Compute ribbon data for bands with width information
|
|
218
312
|
let ribbon_data = $derived.by(() => {
|
|
@@ -252,7 +346,7 @@ let ribbon_data = $derived.by(() => {
|
|
|
252
346
|
// Skip if all widths are zero or missing
|
|
253
347
|
if (width_values.every((wv) => !wv || wv <= 0))
|
|
254
348
|
continue;
|
|
255
|
-
const y_values = bs.bands[band_idx].slice(start_idx, end_idx);
|
|
349
|
+
const y_values = convert_band_values(bs.bands[band_idx].slice(start_idx, end_idx));
|
|
256
350
|
all_ribbons.push({
|
|
257
351
|
x_values: scaled_distances,
|
|
258
352
|
y_values,
|
|
@@ -323,8 +417,17 @@ let x_range = $derived.by(() => {
|
|
|
323
417
|
});
|
|
324
418
|
// Calculate y-range, enforcing 0 minimum for phonon bands without imaginary modes
|
|
325
419
|
let y_range = $derived.by(() => {
|
|
326
|
-
const all_freqs = Object.values(band_structs_dict).flatMap((bs) =>
|
|
327
|
-
|
|
420
|
+
const all_freqs = Object.values(band_structs_dict).flatMap((bs) => [
|
|
421
|
+
...bs.bands.flat(),
|
|
422
|
+
...(bs.spin_down_bands?.flat() ?? []),
|
|
423
|
+
]);
|
|
424
|
+
// Keep electronic y-range independent of phonon unit conversion options.
|
|
425
|
+
const display_values = detected_band_type === `phonon`
|
|
426
|
+
? convert_band_values(all_freqs)
|
|
427
|
+
: all_freqs;
|
|
428
|
+
if (!display_values.length)
|
|
429
|
+
return undefined;
|
|
430
|
+
const finite = display_values.filter(Number.isFinite);
|
|
328
431
|
if (!finite.length)
|
|
329
432
|
return undefined;
|
|
330
433
|
let min_val = Math.min(...finite), max_val = Math.max(...finite);
|
|
@@ -339,7 +442,7 @@ let y_range = $derived.by(() => {
|
|
|
339
442
|
});
|
|
340
443
|
// Internal y_axis that ScatterPlot binds to - syncs zoom changes back to parent
|
|
341
444
|
let internal_y_axis = $derived({
|
|
342
|
-
label: detected_band_type === `phonon` ? `Frequency (
|
|
445
|
+
label: detected_band_type === `phonon` ? `Frequency (${units})` : `Energy (eV)`,
|
|
343
446
|
format: `.2f`,
|
|
344
447
|
label_shift: { y: 15 },
|
|
345
448
|
range: y_range,
|
|
@@ -361,49 +464,226 @@ $effect(() => {
|
|
|
361
464
|
y_axis = rest;
|
|
362
465
|
}
|
|
363
466
|
});
|
|
467
|
+
let has_series = $derived(series_data.length > 0);
|
|
468
|
+
let is_strict_path_error = $derived(path_mode === `strict` && !!strict_path_error);
|
|
469
|
+
let imaginary_mode_region = $derived.by(() => {
|
|
470
|
+
if (detected_band_type !== `phonon` ||
|
|
471
|
+
!shade_imaginary_modes ||
|
|
472
|
+
!y_range ||
|
|
473
|
+
y_range[0] >= 0)
|
|
474
|
+
return [];
|
|
475
|
+
return [{
|
|
476
|
+
lower: y_range[0],
|
|
477
|
+
upper: 0,
|
|
478
|
+
fill: `var(--bands-imaginary-region-color, light-dark(#f8d7da, #5a1a1f))`,
|
|
479
|
+
fill_opacity: 0.2,
|
|
480
|
+
label: `Imaginary modes`,
|
|
481
|
+
show_in_legend: false,
|
|
482
|
+
z_index: `below-lines`,
|
|
483
|
+
}];
|
|
484
|
+
});
|
|
485
|
+
let custom_highlight_regions = $derived.by(() => (highlight_regions ?? [])
|
|
486
|
+
.filter((region) => Number.isFinite(region.y_min) && Number.isFinite(region.y_max))
|
|
487
|
+
.map((region) => ({
|
|
488
|
+
lower: Math.min(region.y_min, region.y_max),
|
|
489
|
+
upper: Math.max(region.y_min, region.y_max),
|
|
490
|
+
fill: region.color ??
|
|
491
|
+
`var(--bands-highlight-region-color, light-dark(#f6e8c3, #4d3f20))`,
|
|
492
|
+
fill_opacity: region.opacity ?? 0.2,
|
|
493
|
+
label: region.label,
|
|
494
|
+
show_in_legend: Boolean(region.label),
|
|
495
|
+
z_index: `below-lines`,
|
|
496
|
+
})));
|
|
497
|
+
let fill_regions = $derived([
|
|
498
|
+
...imaginary_mode_region,
|
|
499
|
+
...custom_highlight_regions,
|
|
500
|
+
]);
|
|
501
|
+
let electronic_gap_annotation = $derived.by(() => {
|
|
502
|
+
if (!show_gap_annotation ||
|
|
503
|
+
detected_band_type !== `electronic` ||
|
|
504
|
+
effective_fermi_level === undefined)
|
|
505
|
+
return null;
|
|
506
|
+
const all_energies = series_data.flatMap((series_item) => series_item.y.filter(Number.isFinite));
|
|
507
|
+
const occupied = all_energies.filter((energy) => energy <= effective_fermi_level);
|
|
508
|
+
const unoccupied = all_energies.filter((energy) => energy > effective_fermi_level);
|
|
509
|
+
if (!occupied.length || !unoccupied.length)
|
|
510
|
+
return null;
|
|
511
|
+
const vbm = Math.max(...occupied);
|
|
512
|
+
const cbm = Math.min(...unoccupied);
|
|
513
|
+
const gap = cbm - vbm;
|
|
514
|
+
if (!(gap > 0))
|
|
515
|
+
return null;
|
|
516
|
+
return { vbm, cbm, gap };
|
|
517
|
+
});
|
|
518
|
+
let empty_state_message = $derived.by(() => {
|
|
519
|
+
if (is_strict_path_error) {
|
|
520
|
+
return strict_path_error ?? `Path mismatch in strict mode.`;
|
|
521
|
+
}
|
|
522
|
+
if (!band_structs || Object.keys(band_structs_dict).length === 0) {
|
|
523
|
+
return `No valid band structure data to display.`;
|
|
524
|
+
}
|
|
525
|
+
if (!has_series) {
|
|
526
|
+
return `No plottable band segments were found in the provided data.`;
|
|
527
|
+
}
|
|
528
|
+
return `No valid band structure data to display.`;
|
|
529
|
+
});
|
|
364
530
|
let display = $state({ x_grid: false, y_grid: true, y_zero_line: true });
|
|
365
531
|
</script>
|
|
366
|
-
|
|
367
|
-
<ScatterPlot
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
{
|
|
384
|
-
{
|
|
532
|
+
{#if has_series && !is_strict_path_error}
|
|
533
|
+
<ScatterPlot
|
|
534
|
+
{id}
|
|
535
|
+
class={class_name}
|
|
536
|
+
{style}
|
|
537
|
+
data-testid={data_testid}
|
|
538
|
+
series={series_data}
|
|
539
|
+
{fill_regions}
|
|
540
|
+
x_axis={{
|
|
541
|
+
label: `Wave Vector`,
|
|
542
|
+
ticks: Object.keys(x_axis_ticks).length > 0 ? x_axis_ticks : undefined,
|
|
543
|
+
format: ``,
|
|
544
|
+
range: x_range,
|
|
545
|
+
...x_axis,
|
|
546
|
+
}}
|
|
547
|
+
bind:y_axis={internal_y_axis}
|
|
548
|
+
bind:display
|
|
549
|
+
legend={show_legend && Object.keys(band_structs_dict).length > 1 ? {} : null}
|
|
550
|
+
hover_config={{ threshold_px: 50 }}
|
|
551
|
+
controls={{ show: show_controls }}
|
|
552
|
+
{...rest}
|
|
553
|
+
>
|
|
554
|
+
{#snippet tooltip({ x, y, y_formatted, label, metadata })}
|
|
555
|
+
{@const y_label_full = internal_y_axis.label ?? ``}
|
|
556
|
+
{@const [, y_label, y_unit] = y_label_full.match(/^(.+?)\s*\(([^)]+)\)$/) ??
|
|
385
557
|
[, y_label_full, ``]}
|
|
386
|
-
|
|
558
|
+
{@const segment = Object.entries(x_positions ?? {}).find(([, [start, end]]) =>
|
|
387
559
|
x >= start && x <= end
|
|
388
560
|
)}
|
|
389
|
-
|
|
561
|
+
{@const path = segment?.[0].split(`_`).map((lbl) =>
|
|
390
562
|
lbl !== `null` ? helpers.pretty_sym_point(lbl) : ``
|
|
391
563
|
).filter(Boolean).join(` → `) || null}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
564
|
+
{@const {
|
|
565
|
+
band_idx,
|
|
566
|
+
spin,
|
|
567
|
+
is_acoustic,
|
|
568
|
+
nb_bands,
|
|
569
|
+
frac_coords,
|
|
570
|
+
qpoint_label,
|
|
571
|
+
band_width,
|
|
572
|
+
slope,
|
|
573
|
+
} = (metadata ?? {}) as Partial<helpers.BandPointMeta>}
|
|
574
|
+
{@const num_structs = Object.keys(band_structs_dict).length}
|
|
575
|
+
{#if num_structs > 1 && label}<strong>{label}</strong><br />{/if}
|
|
576
|
+
{@html y_label || `Value`}: {y_formatted}{y_unit ? ` ${y_unit}` : ``}<br />
|
|
577
|
+
{#if path}Path: {path}<br />{/if}
|
|
578
|
+
{#if typeof band_idx === `number`}
|
|
579
|
+
Band: {band_idx + 1}{#if typeof nb_bands === `number`} / {
|
|
580
|
+
nb_bands
|
|
581
|
+
}{/if}
|
|
582
|
+
{#if typeof is_acoustic === `boolean`}
|
|
583
|
+
({is_acoustic ? `acoustic` : `optical`})
|
|
584
|
+
{:else if detected_band_type === `electronic` && effective_fermi_level !== undefined}
|
|
585
|
+
({y <= effective_fermi_level ? `valence` : `conduction`})
|
|
586
|
+
{/if}
|
|
587
|
+
{#if spin === `up` || spin === `down`}
|
|
588
|
+
{spin === `up` ? `↑` : `↓`}
|
|
589
|
+
{/if}
|
|
590
|
+
{/if}
|
|
591
|
+
{#if typeof qpoint_label === `string` && qpoint_label}
|
|
592
|
+
<br />At: {helpers.pretty_sym_point(qpoint_label)}
|
|
593
|
+
{/if}
|
|
594
|
+
{#if Array.isArray(frac_coords)}
|
|
595
|
+
<br />{detected_band_type === `electronic` ? `k` : `q`}: [{
|
|
596
|
+
frac_coords.map((coord: number) => format_num(coord, `.3f`)).join(`, `)
|
|
597
|
+
}]
|
|
598
|
+
{/if}
|
|
599
|
+
{#if typeof band_width === `number` && band_width > 0}
|
|
600
|
+
<br />Projection: {format_num(band_width, `.3~g`)}
|
|
601
|
+
{/if}
|
|
602
|
+
{#if typeof slope === `number` && Number.isFinite(slope)}
|
|
603
|
+
{@const rel = Math.abs(slope) / max_abs_slope}
|
|
604
|
+
<br />Dispersion: {rel < 0.15 ? `flat` : rel < 0.5 ? `moderate` : `steep`}
|
|
605
|
+
{/if}
|
|
606
|
+
{/snippet}
|
|
399
607
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
608
|
+
{#snippet controls_extra()}
|
|
609
|
+
{#if show_path_mode_control}
|
|
610
|
+
<SettingsSection
|
|
611
|
+
title="Path Mode"
|
|
612
|
+
current_values={{ path_mode }}
|
|
613
|
+
on_reset={() => (path_mode = `strict`)}
|
|
614
|
+
>
|
|
615
|
+
<div class="pane-row">
|
|
616
|
+
<label for="bands-path-mode">Mode:</label>
|
|
617
|
+
<select id="bands-path-mode" bind:value={path_mode}>
|
|
618
|
+
<option value="strict">strict</option>
|
|
619
|
+
<option value="intersection">intersection</option>
|
|
620
|
+
<option value="union">union</option>
|
|
621
|
+
</select>
|
|
622
|
+
</div>
|
|
623
|
+
</SettingsSection>
|
|
624
|
+
{/if}
|
|
625
|
+
|
|
626
|
+
{#if show_units_control && detected_band_type === `phonon`}
|
|
627
|
+
<SettingsSection
|
|
628
|
+
title="Units"
|
|
629
|
+
current_values={{ units }}
|
|
630
|
+
on_reset={() => (units = `THz`)}
|
|
631
|
+
>
|
|
632
|
+
<div class="pane-row">
|
|
633
|
+
<label for="bands-units">Frequency:</label>
|
|
634
|
+
<select id="bands-units" bind:value={units}>
|
|
635
|
+
<option value="THz">THz</option>
|
|
636
|
+
<option value="eV">eV</option>
|
|
637
|
+
<option value="meV">meV</option>
|
|
638
|
+
<option value="cm-1">cm-1</option>
|
|
639
|
+
<option value="Ha">Ha</option>
|
|
640
|
+
</select>
|
|
641
|
+
</div>
|
|
642
|
+
</SettingsSection>
|
|
643
|
+
{/if}
|
|
644
|
+
|
|
645
|
+
{#if show_spin_control && detected_band_type === `electronic`}
|
|
646
|
+
<SettingsSection
|
|
647
|
+
title="Spin Display"
|
|
648
|
+
current_values={{ band_spin_mode }}
|
|
649
|
+
on_reset={() => (band_spin_mode = `overlay`)}
|
|
650
|
+
>
|
|
651
|
+
<div class="pane-row">
|
|
652
|
+
<label for="bands-spin-mode">Mode:</label>
|
|
653
|
+
<select id="bands-spin-mode" bind:value={band_spin_mode}>
|
|
654
|
+
<option value="overlay">overlay</option>
|
|
655
|
+
<option value="up_only">up only</option>
|
|
656
|
+
<option value="down_only">down only</option>
|
|
657
|
+
</select>
|
|
658
|
+
</div>
|
|
659
|
+
</SettingsSection>
|
|
660
|
+
{/if}
|
|
661
|
+
|
|
662
|
+
{#if show_annotation_controls && detected_band_type === `electronic`}
|
|
663
|
+
<SettingsSection
|
|
664
|
+
title="Annotations"
|
|
665
|
+
current_values={{ show_gap_annotation }}
|
|
666
|
+
on_reset={() => (show_gap_annotation = true)}
|
|
667
|
+
>
|
|
668
|
+
<div class="pane-row pane-checkbox">
|
|
669
|
+
<input
|
|
670
|
+
id="bands-gap-annotation"
|
|
671
|
+
type="checkbox"
|
|
672
|
+
bind:checked={show_gap_annotation}
|
|
673
|
+
/>
|
|
674
|
+
<label for="bands-gap-annotation">Show band gap annotation</label>
|
|
675
|
+
</div>
|
|
676
|
+
</SettingsSection>
|
|
677
|
+
{/if}
|
|
678
|
+
{/snippet}
|
|
679
|
+
|
|
680
|
+
{#snippet user_content({ height, x_scale_fn, y_scale_fn, pad })}
|
|
681
|
+
<!-- Fat band ribbons (rendered behind band lines) -->
|
|
682
|
+
{#each ribbon_data as
|
|
683
|
+
ribbon
|
|
684
|
+
(`${ribbon.structure_label}-${ribbon.segment_key}-${ribbon.band_idx}`)
|
|
685
|
+
}
|
|
686
|
+
{@const path_d = helpers.generate_ribbon_path(
|
|
407
687
|
ribbon.x_values,
|
|
408
688
|
ribbon.y_values,
|
|
409
689
|
ribbon.width_values,
|
|
@@ -412,78 +692,170 @@ let display = $state({ x_grid: false, y_grid: true, y_zero_line: true });
|
|
|
412
692
|
ribbon.max_width,
|
|
413
693
|
ribbon.scale,
|
|
414
694
|
)}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
695
|
+
{#if path_d}
|
|
696
|
+
<path
|
|
697
|
+
d={path_d}
|
|
698
|
+
fill={ribbon.color}
|
|
699
|
+
opacity={ribbon.opacity}
|
|
700
|
+
stroke="none"
|
|
701
|
+
class="fat-band-ribbon"
|
|
702
|
+
/>
|
|
703
|
+
{/if}
|
|
704
|
+
{/each}
|
|
425
705
|
|
|
426
|
-
|
|
427
|
-
|
|
706
|
+
<!-- Symmetry point vertical lines (filter NaN from scale) -->
|
|
707
|
+
{#each Object.keys(x_axis_ticks).map(Number).map((x) => x_scale_fn(x)).filter(
|
|
428
708
|
Number.isFinite,
|
|
429
709
|
) as
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
710
|
+
scaled_x
|
|
711
|
+
(scaled_x)
|
|
712
|
+
}
|
|
713
|
+
<line
|
|
714
|
+
x1={scaled_x}
|
|
715
|
+
x2={scaled_x}
|
|
716
|
+
y1={pad.t}
|
|
717
|
+
y2={height - pad.b}
|
|
718
|
+
stroke="var(--bands-symmetry-line-color, light-dark(black, white))"
|
|
719
|
+
stroke-width="var(--bands-symmetry-line-width, 1)"
|
|
720
|
+
opacity="var(--bands-symmetry-line-opacity, 0.5)"
|
|
721
|
+
/>
|
|
722
|
+
{/each}
|
|
443
723
|
|
|
444
|
-
|
|
445
|
-
|
|
724
|
+
<!-- Shared geometry for Fermi level and gap annotations -->
|
|
725
|
+
{@const fermi_y = effective_fermi_level !== undefined
|
|
446
726
|
? y_scale_fn(effective_fermi_level)
|
|
447
727
|
: NaN}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
728
|
+
{@const bands_x_end = x_scale_fn(Object.values(x_positions ?? {}).flat().at(-1) ?? 1)}
|
|
729
|
+
{@const gap_data = electronic_gap_annotation}
|
|
730
|
+
{@const vbm_y = gap_data ? y_scale_fn(gap_data.vbm) : NaN}
|
|
731
|
+
{@const cbm_y = gap_data ? y_scale_fn(gap_data.cbm) : NaN}
|
|
732
|
+
{@const gap_mid_y = (vbm_y + cbm_y) / 2}
|
|
733
|
+
{@const ef_needs_offset = Number.isFinite(gap_mid_y) &&
|
|
734
|
+
Math.abs(fermi_y - gap_mid_y) < 16}
|
|
735
|
+
{@const ef_label_y = ef_needs_offset
|
|
736
|
+
? gap_mid_y + (fermi_y >= gap_mid_y ? 16 : -16)
|
|
737
|
+
: fermi_y}
|
|
738
|
+
|
|
739
|
+
<!-- Fermi level line for electronic bands -->
|
|
740
|
+
{#if Number.isFinite(fermi_y) && Number.isFinite(bands_x_end)}
|
|
741
|
+
<line
|
|
742
|
+
class="fermi-level-line"
|
|
743
|
+
x1={pad.l}
|
|
744
|
+
x2={bands_x_end}
|
|
745
|
+
y1={fermi_y}
|
|
746
|
+
y2={fermi_y}
|
|
747
|
+
stroke="var(--bands-fermi-line-color, light-dark(#e74c3c, #ff6b6b))"
|
|
748
|
+
stroke-width="var(--bands-fermi-line-width, 1.5)"
|
|
749
|
+
stroke-dasharray="var(--bands-fermi-line-dash, 6,3)"
|
|
750
|
+
opacity="var(--bands-fermi-line-opacity, 0.8)"
|
|
751
|
+
/>
|
|
752
|
+
{#if ef_needs_offset}
|
|
753
|
+
<line
|
|
754
|
+
x1={bands_x_end}
|
|
755
|
+
y1={fermi_y}
|
|
756
|
+
x2={bands_x_end + 3}
|
|
757
|
+
y2={ef_label_y}
|
|
758
|
+
stroke="var(--bands-fermi-line-color, light-dark(#e74c3c, #ff6b6b))"
|
|
759
|
+
stroke-width="0.7"
|
|
760
|
+
opacity="0.5"
|
|
761
|
+
/>
|
|
762
|
+
{/if}
|
|
763
|
+
<text
|
|
764
|
+
class="fermi-level-label"
|
|
765
|
+
x={bands_x_end + 4}
|
|
766
|
+
y={ef_label_y}
|
|
767
|
+
dy="0.35em"
|
|
768
|
+
font-size="10"
|
|
769
|
+
fill="var(--bands-fermi-line-color, light-dark(#e74c3c, #ff6b6b))"
|
|
770
|
+
opacity="0.9"
|
|
771
|
+
>
|
|
772
|
+
E<tspan dy="2" font-size="8">F</tspan>
|
|
773
|
+
</text>
|
|
774
|
+
{/if}
|
|
775
|
+
|
|
776
|
+
<!-- Reference frequency horizontal line -->
|
|
777
|
+
{@const ref_freq = reference_frequency !== null && reference_frequency !== undefined
|
|
778
|
+
? convert_band_values([reference_frequency])[0]
|
|
779
|
+
: NaN}
|
|
780
|
+
{@const ref_y = Number.isFinite(ref_freq) ? y_scale_fn(ref_freq) : NaN}
|
|
781
|
+
{#if Number.isFinite(ref_y) && Number.isFinite(bands_x_end)}
|
|
782
|
+
<line
|
|
783
|
+
x1={pad.l}
|
|
784
|
+
x2={bands_x_end}
|
|
785
|
+
y1={ref_y}
|
|
786
|
+
y2={ref_y}
|
|
787
|
+
stroke="var(--bands-reference-line-color, light-dark(#d48860, #c47850))"
|
|
788
|
+
stroke-width="var(--bands-reference-line-width, 1)"
|
|
789
|
+
stroke-dasharray="var(--bands-reference-line-dash, 4,3)"
|
|
790
|
+
opacity="var(--bands-reference-line-opacity, 0.5)"
|
|
791
|
+
/>
|
|
792
|
+
{/if}
|
|
793
|
+
|
|
794
|
+
<!-- Electronic band edge and gap annotation -->
|
|
795
|
+
{#if gap_data && Number.isFinite(vbm_y) && Number.isFinite(cbm_y) &&
|
|
796
|
+
Number.isFinite(bands_x_end)}
|
|
797
|
+
{#each [
|
|
798
|
+
[vbm_y, `var(--bands-gap-vbm-color, light-dark(#1f77b4, #7db7ff))`],
|
|
799
|
+
[cbm_y, `var(--bands-gap-cbm-color, light-dark(#2ca02c, #7ddc7d))`],
|
|
800
|
+
] as [number, string][] as
|
|
801
|
+
[edge_y, color]
|
|
802
|
+
(edge_y)
|
|
803
|
+
}
|
|
804
|
+
<line
|
|
805
|
+
x1={pad.l}
|
|
806
|
+
x2={bands_x_end + 3}
|
|
807
|
+
y1={edge_y}
|
|
808
|
+
y2={edge_y}
|
|
809
|
+
stroke={color}
|
|
810
|
+
stroke-width="var(--bands-gap-line-width, 1)"
|
|
811
|
+
stroke-dasharray="var(--bands-gap-line-dash, 2,2)"
|
|
812
|
+
opacity="0.7"
|
|
813
|
+
/>
|
|
814
|
+
{/each}
|
|
815
|
+
<text
|
|
816
|
+
x={bands_x_end + 4}
|
|
817
|
+
y={gap_mid_y}
|
|
818
|
+
dy="0.35em"
|
|
819
|
+
font-size="10"
|
|
820
|
+
fill="var(--text-color)"
|
|
821
|
+
>
|
|
822
|
+
E<tspan dy="2" font-size="8">g:</tspan>
|
|
823
|
+
<tspan dy="-2">{Number(gap_data.gap.toPrecision(4))} eV</tspan>
|
|
824
|
+
</text>
|
|
825
|
+
{/if}
|
|
826
|
+
{/snippet}
|
|
827
|
+
</ScatterPlot>
|
|
828
|
+
{:else}
|
|
829
|
+
<EmptyState
|
|
830
|
+
{id}
|
|
831
|
+
class={class_name}
|
|
832
|
+
{style}
|
|
833
|
+
data-testid={data_testid}
|
|
834
|
+
{...empty_state_attrs}
|
|
835
|
+
message={empty_state_message}
|
|
836
|
+
/>
|
|
837
|
+
{/if}
|
|
473
838
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
839
|
+
<style>
|
|
840
|
+
.pane-row {
|
|
841
|
+
display: flex;
|
|
842
|
+
align-items: center;
|
|
843
|
+
gap: 0.5em;
|
|
844
|
+
margin: 0.3em 0;
|
|
845
|
+
font-size: 0.9em;
|
|
846
|
+
}
|
|
847
|
+
.pane-row label {
|
|
848
|
+
min-width: 4.5em;
|
|
849
|
+
flex-shrink: 0;
|
|
850
|
+
}
|
|
851
|
+
.pane-row select {
|
|
852
|
+
flex: 1;
|
|
853
|
+
min-width: 0;
|
|
854
|
+
}
|
|
855
|
+
.pane-checkbox {
|
|
856
|
+
gap: 0.4em;
|
|
857
|
+
}
|
|
858
|
+
.pane-checkbox label {
|
|
859
|
+
min-width: 0;
|
|
860
|
+
}
|
|
861
|
+
</style>
|