matterviz 0.3.0 → 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.
Files changed (286) hide show
  1. package/dist/FilePicker.svelte +37 -20
  2. package/dist/Icon.svelte +2 -2
  3. package/dist/MillerIndexInput.svelte +60 -0
  4. package/dist/MillerIndexInput.svelte.d.ts +7 -0
  5. package/dist/app.css +38 -2
  6. package/dist/brillouin/BrillouinZone.svelte +20 -62
  7. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  8. package/dist/brillouin/BrillouinZoneExportPane.svelte +12 -20
  9. package/dist/brillouin/BrillouinZoneScene.svelte +2 -2
  10. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  11. package/dist/chempot-diagram/ChemPotDiagram.svelte +192 -0
  12. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
  13. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +677 -0
  14. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
  15. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2688 -0
  16. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
  17. package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -0
  18. package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
  19. package/dist/chempot-diagram/color.d.ts +10 -0
  20. package/dist/chempot-diagram/color.js +33 -0
  21. package/dist/chempot-diagram/compute.d.ts +38 -0
  22. package/dist/chempot-diagram/compute.js +650 -0
  23. package/dist/chempot-diagram/index.d.ts +5 -0
  24. package/dist/chempot-diagram/index.js +5 -0
  25. package/dist/chempot-diagram/pointer.d.ts +16 -0
  26. package/dist/chempot-diagram/pointer.js +40 -0
  27. package/dist/chempot-diagram/temperature.d.ts +15 -0
  28. package/dist/chempot-diagram/temperature.js +37 -0
  29. package/dist/chempot-diagram/types.d.ts +83 -0
  30. package/dist/chempot-diagram/types.js +27 -0
  31. package/dist/colors/index.d.ts +3 -1
  32. package/dist/colors/index.js +4 -0
  33. package/dist/composition/BarChart.svelte +13 -22
  34. package/dist/composition/BubbleChart.svelte +5 -3
  35. package/dist/composition/FormulaFilter.svelte +770 -90
  36. package/dist/composition/FormulaFilter.svelte.d.ts +37 -1
  37. package/dist/composition/PieChart.svelte +43 -18
  38. package/dist/composition/PieChart.svelte.d.ts +1 -1
  39. package/dist/constants.d.ts +1 -0
  40. package/dist/constants.js +2 -0
  41. package/dist/convex-hull/ConvexHull.svelte +14 -1
  42. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  43. package/dist/convex-hull/ConvexHull2D.svelte +14 -45
  44. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  45. package/dist/convex-hull/ConvexHull3D.svelte +396 -134
  46. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  47. package/dist/convex-hull/ConvexHull4D.svelte +93 -42
  48. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  49. package/dist/convex-hull/ConvexHullControls.svelte +94 -31
  50. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +4 -2
  51. package/dist/convex-hull/ConvexHullStats.svelte +697 -128
  52. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +6 -1
  53. package/dist/convex-hull/ConvexHullTooltip.svelte +1 -0
  54. package/dist/convex-hull/GasPressureControls.svelte +72 -38
  55. package/dist/convex-hull/GasPressureControls.svelte.d.ts +2 -1
  56. package/dist/convex-hull/TemperatureSlider.svelte +46 -19
  57. package/dist/convex-hull/TemperatureSlider.svelte.d.ts +2 -1
  58. package/dist/convex-hull/demo-temperature.d.ts +6 -0
  59. package/dist/convex-hull/demo-temperature.js +36 -0
  60. package/dist/convex-hull/gas-thermodynamics.js +16 -5
  61. package/dist/convex-hull/helpers.d.ts +7 -1
  62. package/dist/convex-hull/helpers.js +45 -15
  63. package/dist/convex-hull/index.d.ts +15 -1
  64. package/dist/convex-hull/index.js +1 -0
  65. package/dist/convex-hull/thermodynamics.d.ts +8 -21
  66. package/dist/convex-hull/thermodynamics.js +106 -17
  67. package/dist/convex-hull/types.d.ts +7 -0
  68. package/dist/convex-hull/types.js +11 -0
  69. package/dist/coordination/CoordinationBarPlot.svelte +29 -46
  70. package/dist/element/BohrAtom.svelte +1 -1
  71. package/dist/element/data.js +2 -14
  72. package/dist/element/data.json.gz +0 -0
  73. package/dist/element/index.d.ts +1 -1
  74. package/dist/element/index.js +1 -0
  75. package/dist/element/types.d.ts +1 -0
  76. package/dist/fermi-surface/FermiSurface.svelte +21 -65
  77. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  78. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  79. package/dist/fermi-surface/FermiSurfaceScene.svelte +1 -1
  80. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  81. package/dist/fermi-surface/compute.js +1 -21
  82. package/dist/fermi-surface/marching-cubes.d.ts +2 -13
  83. package/dist/fermi-surface/marching-cubes.js +2 -519
  84. package/dist/fermi-surface/parse.js +17 -23
  85. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1273 -0
  86. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
  87. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +171 -0
  88. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +31 -0
  89. package/dist/heatmap-matrix/index.d.ts +53 -0
  90. package/dist/heatmap-matrix/index.js +100 -0
  91. package/dist/heatmap-matrix/shared.d.ts +2 -0
  92. package/dist/heatmap-matrix/shared.js +4 -0
  93. package/dist/icons.d.ts +119 -0
  94. package/dist/icons.js +119 -0
  95. package/dist/index.d.ts +6 -1
  96. package/dist/index.js +6 -1
  97. package/dist/io/export.js +15 -3
  98. package/dist/io/file-drop.d.ts +7 -0
  99. package/dist/io/file-drop.js +43 -0
  100. package/dist/io/index.d.ts +2 -2
  101. package/dist/io/index.js +2 -112
  102. package/dist/io/types.d.ts +1 -0
  103. package/dist/io/url-drop.d.ts +2 -0
  104. package/dist/io/url-drop.js +118 -0
  105. package/dist/isosurface/Isosurface.svelte +231 -0
  106. package/dist/isosurface/Isosurface.svelte.d.ts +8 -0
  107. package/dist/isosurface/IsosurfaceControls.svelte +273 -0
  108. package/dist/isosurface/IsosurfaceControls.svelte.d.ts +9 -0
  109. package/dist/isosurface/index.d.ts +5 -0
  110. package/dist/isosurface/index.js +6 -0
  111. package/dist/isosurface/parse.d.ts +6 -0
  112. package/dist/isosurface/parse.js +548 -0
  113. package/dist/isosurface/slice.d.ts +11 -0
  114. package/dist/isosurface/slice.js +145 -0
  115. package/dist/isosurface/types.d.ts +55 -0
  116. package/dist/isosurface/types.js +178 -0
  117. package/dist/labels.d.ts +2 -1
  118. package/dist/labels.js +1 -0
  119. package/dist/layout/InfoTag.svelte +62 -62
  120. package/dist/layout/SubpageGrid.svelte +74 -0
  121. package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
  122. package/dist/layout/index.d.ts +1 -0
  123. package/dist/layout/index.js +1 -0
  124. package/dist/layout/json-tree/JsonNode.svelte +226 -53
  125. package/dist/layout/json-tree/JsonTree.svelte +425 -51
  126. package/dist/layout/json-tree/JsonTree.svelte.d.ts +1 -1
  127. package/dist/layout/json-tree/JsonValue.svelte +218 -97
  128. package/dist/layout/json-tree/types.d.ts +27 -2
  129. package/dist/layout/json-tree/utils.d.ts +14 -1
  130. package/dist/layout/json-tree/utils.js +254 -0
  131. package/dist/marching-cubes.d.ts +14 -0
  132. package/dist/marching-cubes.js +519 -0
  133. package/dist/math.d.ts +8 -0
  134. package/dist/math.js +374 -7
  135. package/dist/overlays/ContextMenu.svelte +3 -2
  136. package/dist/overlays/DraggablePane.svelte +163 -58
  137. package/dist/overlays/DraggablePane.svelte.d.ts +2 -0
  138. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +232 -77
  139. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -2
  140. package/dist/phase-diagram/PhaseDiagramControls.svelte +32 -11
  141. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +3 -2
  142. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +103 -0
  143. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
  144. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +102 -95
  145. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +7 -0
  146. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +100 -26
  147. package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +6 -3
  148. package/dist/phase-diagram/index.d.ts +2 -0
  149. package/dist/phase-diagram/index.js +2 -0
  150. package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
  151. package/dist/phase-diagram/svg-to-diagram.js +865 -0
  152. package/dist/phase-diagram/types.d.ts +10 -0
  153. package/dist/phase-diagram/utils.d.ts +7 -4
  154. package/dist/phase-diagram/utils.js +149 -59
  155. package/dist/plot/AxisLabel.svelte +26 -0
  156. package/dist/plot/AxisLabel.svelte.d.ts +16 -0
  157. package/dist/plot/BarPlot.svelte +473 -228
  158. package/dist/plot/BarPlot.svelte.d.ts +3 -3
  159. package/dist/plot/BarPlotControls.svelte +3 -2
  160. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  161. package/dist/plot/ColorBar.svelte +54 -54
  162. package/dist/plot/ColorBar.svelte.d.ts +1 -1
  163. package/dist/plot/ElementScatter.svelte +4 -3
  164. package/dist/plot/FillArea.svelte +4 -1
  165. package/dist/plot/Histogram.svelte +320 -230
  166. package/dist/plot/Histogram.svelte.d.ts +2 -2
  167. package/dist/plot/HistogramControls.svelte +29 -10
  168. package/dist/plot/HistogramControls.svelte.d.ts +6 -2
  169. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +2 -2
  170. package/dist/plot/PlotControls.svelte +109 -27
  171. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  172. package/dist/plot/PlotLegend.svelte +1 -1
  173. package/dist/plot/PortalSelect.svelte +2 -1
  174. package/dist/plot/ReferenceLine.svelte +2 -1
  175. package/dist/plot/ReferenceLine.svelte.d.ts +1 -0
  176. package/dist/plot/ReferencePlane.svelte +1 -3
  177. package/dist/plot/ScatterPlot.svelte +343 -209
  178. package/dist/plot/ScatterPlot.svelte.d.ts +3 -3
  179. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  180. package/dist/plot/ScatterPlot3DControls.svelte +203 -250
  181. package/dist/plot/ScatterPlot3DScene.svelte +4 -7
  182. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  183. package/dist/plot/ScatterPlotControls.svelte +95 -55
  184. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  185. package/dist/plot/ZeroLines.svelte +44 -0
  186. package/dist/plot/ZeroLines.svelte.d.ts +32 -0
  187. package/dist/plot/ZoomRect.svelte +21 -0
  188. package/dist/plot/ZoomRect.svelte.d.ts +8 -0
  189. package/dist/plot/axis-utils.d.ts +1 -1
  190. package/dist/plot/data-cleaning.js +1 -5
  191. package/dist/plot/index.d.ts +6 -2
  192. package/dist/plot/index.js +6 -2
  193. package/dist/plot/interactions.d.ts +8 -10
  194. package/dist/plot/interactions.js +10 -19
  195. package/dist/plot/layout.d.ts +7 -1
  196. package/dist/plot/layout.js +12 -4
  197. package/dist/plot/reference-line.d.ts +4 -21
  198. package/dist/plot/reference-line.js +7 -81
  199. package/dist/plot/types.d.ts +42 -17
  200. package/dist/plot/types.js +10 -0
  201. package/dist/plot/utils/label-placement.js +14 -11
  202. package/dist/plot/utils.d.ts +1 -0
  203. package/dist/plot/utils.js +14 -0
  204. package/dist/rdf/RdfPlot.svelte +55 -66
  205. package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
  206. package/dist/rdf/index.d.ts +1 -1
  207. package/dist/rdf/index.js +1 -1
  208. package/dist/settings.d.ts +5 -0
  209. package/dist/settings.js +37 -3
  210. package/dist/spectral/Bands.svelte +515 -143
  211. package/dist/spectral/Bands.svelte.d.ts +22 -2
  212. package/dist/spectral/helpers.d.ts +23 -1
  213. package/dist/spectral/helpers.js +65 -9
  214. package/dist/spectral/types.d.ts +2 -0
  215. package/dist/structure/AtomLegend.svelte +31 -10
  216. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  217. package/dist/structure/CellSelect.svelte +92 -22
  218. package/dist/structure/Lattice.svelte +2 -0
  219. package/dist/structure/Structure.svelte +716 -173
  220. package/dist/structure/Structure.svelte.d.ts +7 -2
  221. package/dist/structure/StructureControls.svelte +26 -14
  222. package/dist/structure/StructureControls.svelte.d.ts +5 -1
  223. package/dist/structure/StructureInfoPane.svelte +7 -1
  224. package/dist/structure/StructureScene.svelte +386 -95
  225. package/dist/structure/StructureScene.svelte.d.ts +15 -4
  226. package/dist/structure/atom-properties.d.ts +6 -2
  227. package/dist/structure/atom-properties.js +38 -25
  228. package/dist/structure/export.js +10 -7
  229. package/dist/structure/ferrox-wasm-types.d.ts +3 -2
  230. package/dist/structure/ferrox-wasm-types.js +0 -3
  231. package/dist/structure/ferrox-wasm.d.ts +3 -2
  232. package/dist/structure/ferrox-wasm.js +1 -2
  233. package/dist/structure/index.d.ts +7 -0
  234. package/dist/structure/index.js +22 -0
  235. package/dist/structure/parse.js +19 -16
  236. package/dist/structure/partial-occupancy.d.ts +25 -0
  237. package/dist/structure/partial-occupancy.js +102 -0
  238. package/dist/structure/validation.js +6 -3
  239. package/dist/symmetry/SymmetryStats.svelte +18 -4
  240. package/dist/symmetry/WyckoffTable.svelte +18 -10
  241. package/dist/symmetry/index.d.ts +7 -4
  242. package/dist/symmetry/index.js +83 -18
  243. package/dist/table/HeatmapTable.svelte +468 -69
  244. package/dist/table/HeatmapTable.svelte.d.ts +13 -1
  245. package/dist/table/ToggleMenu.svelte +291 -44
  246. package/dist/table/ToggleMenu.svelte.d.ts +4 -1
  247. package/dist/table/index.d.ts +3 -0
  248. package/dist/tooltip/index.d.ts +1 -1
  249. package/dist/tooltip/index.js +1 -0
  250. package/dist/trajectory/Trajectory.svelte +147 -145
  251. package/dist/trajectory/TrajectoryExportPane.svelte +13 -9
  252. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +1 -1
  253. package/dist/trajectory/constants.d.ts +6 -0
  254. package/dist/trajectory/constants.js +7 -0
  255. package/dist/trajectory/extract.js +3 -5
  256. package/dist/trajectory/format-detect.d.ts +9 -0
  257. package/dist/trajectory/format-detect.js +76 -0
  258. package/dist/trajectory/frame-reader.d.ts +17 -0
  259. package/dist/trajectory/frame-reader.js +339 -0
  260. package/dist/trajectory/helpers.d.ts +15 -0
  261. package/dist/trajectory/helpers.js +187 -0
  262. package/dist/trajectory/index.d.ts +1 -0
  263. package/dist/trajectory/index.js +11 -4
  264. package/dist/trajectory/parse/ase.d.ts +2 -0
  265. package/dist/trajectory/parse/ase.js +76 -0
  266. package/dist/trajectory/parse/hdf5.d.ts +2 -0
  267. package/dist/trajectory/parse/hdf5.js +121 -0
  268. package/dist/trajectory/parse/index.d.ts +12 -0
  269. package/dist/trajectory/parse/index.js +304 -0
  270. package/dist/trajectory/parse/lammps.d.ts +5 -0
  271. package/dist/trajectory/parse/lammps.js +169 -0
  272. package/dist/trajectory/parse/vasp.d.ts +2 -0
  273. package/dist/trajectory/parse/vasp.js +65 -0
  274. package/dist/trajectory/parse/xyz.d.ts +2 -0
  275. package/dist/trajectory/parse/xyz.js +109 -0
  276. package/dist/trajectory/types.d.ts +11 -0
  277. package/dist/trajectory/types.js +1 -0
  278. package/dist/utils.d.ts +2 -0
  279. package/dist/utils.js +4 -0
  280. package/dist/xrd/XrdPlot.svelte +6 -4
  281. package/dist/xrd/calc-xrd.js +0 -1
  282. package/package.json +33 -23
  283. package/readme.md +4 -4
  284. package/dist/trajectory/parse.d.ts +0 -42
  285. package/dist/trajectory/parse.js +0 -1267
  286. /package/dist/element/{data.json.d.ts → data.json.gz.d.ts} +0 -0
@@ -1,6 +1,6 @@
1
1
  import ScatterPlot from '../plot/ScatterPlot.svelte';
2
2
  import type { AxisConfig } from '../plot/types';
3
- import type { BandStructureType, BaseBandStructure, LineKwargs, PathMode, RibbonConfig } from './types';
3
+ import type { BandsSpinMode, BandStructureType, BaseBandStructure, FrequencyUnit, LineKwargs, PathMode, RibbonConfig } from './types';
4
4
  import type { ComponentProps } from 'svelte';
5
5
  type $$ComponentProps = ComponentProps<typeof ScatterPlot> & {
6
6
  band_structs: BaseBandStructure | Record<string, BaseBandStructure>;
@@ -14,7 +14,27 @@ type $$ComponentProps = ComponentProps<typeof ScatterPlot> & {
14
14
  reference_frequency?: number | null;
15
15
  ribbon_config?: RibbonConfig | Record<string, RibbonConfig>;
16
16
  fermi_level?: number;
17
+ units?: FrequencyUnit;
18
+ band_spin_mode?: BandsSpinMode;
19
+ highlight_regions?: {
20
+ y_min: number;
21
+ y_max: number;
22
+ color?: string;
23
+ opacity?: number;
24
+ label?: string;
25
+ }[];
26
+ shade_imaginary_modes?: boolean;
27
+ show_gap_annotation?: boolean;
28
+ show_controls?: boolean;
29
+ show_path_mode_control?: boolean;
30
+ show_units_control?: boolean;
31
+ show_spin_control?: boolean;
32
+ show_annotation_controls?: boolean;
33
+ id?: string;
34
+ class?: string;
35
+ style?: string;
36
+ 'data-testid'?: string;
17
37
  };
18
- declare const Bands: import("svelte").Component<$$ComponentProps, {}, "y_axis" | "x_positions">;
38
+ declare const Bands: import("svelte").Component<$$ComponentProps, {}, "y_axis" | "units" | "x_positions" | "band_spin_mode">;
19
39
  type Bands = ReturnType<typeof Bands>;
20
40
  export default Bands;
@@ -4,7 +4,6 @@ import type { RibbonConfig } from './types';
4
4
  export declare const is_valid_range: (range: unknown) => range is Vec2;
5
5
  export declare const ranges_equal: (a: Vec2 | undefined | null, b: Vec2 | undefined | null, tol?: number) => boolean;
6
6
  export declare function detect_zoom_change(bands_range: unknown, dos_range: unknown, shared_range: Vec2, current_synced: Vec2 | null, dos_enabled?: boolean): Vec2 | null | undefined;
7
- export declare const N_ACOUSTIC_MODES = 3;
8
7
  export declare const IMAGINARY_MODE_NOISE_THRESHOLD = 0.005;
9
8
  export declare function pretty_sym_point(symbol: string): string;
10
9
  export declare const get_segment_key: (start_label?: string, end_label?: string) => string;
@@ -92,4 +91,27 @@ export declare const DEFAULT_UNITS: types.FrequencyUnit;
92
91
  export declare function format_sigma(val: number): string;
93
92
  export declare function validate_sigma_range([min, max]: [number, number]): [number, number];
94
93
  export declare function calculate_sigma_step(range: [number, number]): number;
94
+ export interface BandPointMeta extends Record<string, unknown> {
95
+ band_idx: number;
96
+ spin: `up` | `down`;
97
+ is_acoustic: boolean | null;
98
+ nb_bands: number;
99
+ frac_coords: Vec3 | null;
100
+ qpoint_label: string | null;
101
+ band_width: number | null;
102
+ slope: number | null;
103
+ }
104
+ export declare function compute_slope(x_vals: number[], y_vals: number[], idx: number): number | null;
105
+ export declare function find_gamma_indices(bs: types.BaseBandStructure): number[];
106
+ export declare const ACOUSTIC_FREQ_THRESHOLD = 0.5;
107
+ export declare function classify_acoustic(bs: types.BaseBandStructure, band_idx: number, gamma_indices: number[], threshold?: number): boolean | null;
108
+ export declare function build_point_metadata(opts: {
109
+ x_vals: number[];
110
+ y_vals: number[];
111
+ band_idx: number;
112
+ spin: `up` | `down`;
113
+ is_acoustic: boolean | null;
114
+ bs: types.BaseBandStructure;
115
+ start_idx: number;
116
+ }): BandPointMeta[];
95
117
  export {};
@@ -52,7 +52,6 @@ const THz_TO_MEV = THz_TO_EV * 1000;
52
52
  const THz_TO_HA = THz_TO_EV / 27.211386245988; // Hartree
53
53
  const THz_TO_CM = THz_TO_HZ / (C_LIGHT * 100); // cm^-1 (c in cm/s)
54
54
  // Band structure constants
55
- export const N_ACOUSTIC_MODES = 3; // Number of acoustic modes in typical 3D crystals
56
55
  export const IMAGINARY_MODE_NOISE_THRESHOLD = 0.005; // Clamp negatives < 0.5% as noise
57
56
  // Convert symmetry point symbols to pretty-printed versions.
58
57
  // Handles Greek letters (both plain and LaTeX backslash-prefixed) and subscripts.
@@ -311,11 +310,6 @@ const CM_TO_THZ = 1 / THz_TO_CM;
311
310
  // Spin key constants for pymatgen spin-polarized data
312
311
  const SPIN_UP_KEYS = [`1`, `Spin.up`];
313
312
  const SPIN_DOWN_KEYS = [`-1`, `Spin.down`];
314
- // Extract first spin channel from pymatgen spin-keyed data.
315
- // Thin wrapper around extract_spin_channels for backwards compatibility.
316
- function extract_first_spin_channel(data) {
317
- return extract_spin_channels(data)?.up ?? null;
318
- }
319
313
  // Extract both spin channels from pymatgen spin-keyed data.
320
314
  // Returns { up: T, down: T | null } where down is null for non-spin-polarized data.
321
315
  export function extract_spin_channels(data) {
@@ -359,7 +353,9 @@ function convert_pymatgen_band_structure(pmg) {
359
353
  // 1. Standard pymatgen: bands as dict with spin keys {1: [[...], ...]}
360
354
  // 2. Custom phonon format: frequencies_cm as 2D array [[...], ...]
361
355
  // 3. Already normalized: bands as 2D array [[...], ...]
362
- let raw_bands = extract_first_spin_channel(pmg.bands);
356
+ const spin_channels = extract_spin_channels(pmg.bands);
357
+ let raw_bands = spin_channels?.up ?? null;
358
+ let raw_spin_down_bands = spin_channels?.down ?? null;
363
359
  const has_frequencies_cm = Array.isArray(pmg.frequencies_cm);
364
360
  if (!raw_bands && has_frequencies_cm) {
365
361
  // Phonon format: frequencies_cm is [n_qpoints x n_branches] - needs transpose
@@ -367,6 +363,7 @@ function convert_pymatgen_band_structure(pmg) {
367
363
  if (freqs.length > 0 && Array.isArray(freqs[0])) {
368
364
  // Transpose: [n_qpoints x n_branches] -> [n_branches x n_qpoints]
369
365
  raw_bands = Array.from({ length: freqs[0].length }, (_, band_idx) => freqs.map((qpt_freqs) => qpt_freqs[band_idx]));
366
+ raw_spin_down_bands = null;
370
367
  }
371
368
  }
372
369
  const labels_dict = pmg.labels_dict;
@@ -377,7 +374,7 @@ function convert_pymatgen_band_structure(pmg) {
377
374
  if (!Array.isArray(raw_qpts) || !Array.isArray(raw_bands) ||
378
375
  !raw_qpts.length || !raw_bands.length)
379
376
  return null;
380
- const qpoints = raw_qpts.map((q) => parse_qpoint(q, labels_dict)).filter(Boolean);
377
+ const qpoints = raw_qpts.map((q) => parse_qpoint(q, labels_dict)).filter((q) => q !== null);
381
378
  if (!qpoints.length)
382
379
  return null;
383
380
  // Step distances and discontinuity detection (5x median threshold)
@@ -432,11 +429,19 @@ function convert_pymatgen_band_structure(pmg) {
432
429
  return val * CM_TO_THZ;
433
430
  return val; // THz (default) - no conversion
434
431
  };
432
+ const converted_bands = raw_bands.map((band) => band.map(convert_to_thz));
433
+ const valid_spin_down_bands = Array.isArray(raw_spin_down_bands) &&
434
+ raw_spin_down_bands.length === raw_bands.length &&
435
+ raw_spin_down_bands.every((band, band_idx) => Array.isArray(band) && band.length === raw_bands[band_idx]?.length)
436
+ ? raw_spin_down_bands
437
+ : null;
438
+ const converted_spin_down_bands = valid_spin_down_bands?.map((band) => band.map(convert_to_thz));
435
439
  return {
436
440
  qpoints,
437
441
  branches,
438
442
  distance,
439
- bands: raw_bands.map((band) => band.map(convert_to_thz)),
443
+ bands: converted_bands,
444
+ spin_down_bands: converted_spin_down_bands,
440
445
  nb_bands: raw_bands.length,
441
446
  labels_dict: labels_dict ?? {},
442
447
  recip_lattice: { matrix: lattice_rec?.matrix ?? [[1, 0, 0], [0, 1, 0], [0, 0, 1]] },
@@ -953,3 +958,54 @@ export function calculate_sigma_step(range) {
953
958
  const [min, max] = validate_sigma_range(range);
954
959
  return (max - min) / 100 || 0.01;
955
960
  }
961
+ // Central difference for local slope (dω/dk or dE/dk).
962
+ // Uses forward/backward difference at endpoints, central difference for interior points.
963
+ export function compute_slope(x_vals, y_vals, idx) {
964
+ const len = Math.min(x_vals.length, y_vals.length);
965
+ if (len < 2 || idx < 0 || idx >= len)
966
+ return null;
967
+ const lo = idx === 0 ? 0 : idx - 1;
968
+ const hi = idx >= len - 1 ? len - 1 : idx + 1;
969
+ const dx = x_vals[hi] - x_vals[lo];
970
+ return dx ? (y_vals[hi] - y_vals[lo]) / dx : null;
971
+ }
972
+ // Find Gamma-point indices (q ≈ integer lattice point) in a band structure.
973
+ // Returns indices of q-points whose fractional coordinates are all within 0.01 of integers.
974
+ export function find_gamma_indices(bs) {
975
+ const indices = [];
976
+ for (let q_idx = 0; q_idx < bs.qpoints.length; q_idx++) {
977
+ const coords = bs.qpoints[q_idx]?.frac_coords;
978
+ if (coords?.every((coord) => Math.abs(coord - Math.round(coord)) < 0.01)) {
979
+ indices.push(q_idx);
980
+ }
981
+ }
982
+ return indices;
983
+ }
984
+ // Threshold below which a band's frequency at Gamma is considered acoustic (THz).
985
+ // Assumes bands are stored in THz (normalize_band_structure converts to THz).
986
+ export const ACOUSTIC_FREQ_THRESHOLD = 0.5;
987
+ // Classify a band as acoustic based on near-zero frequency at Gamma points.
988
+ // Returns true (acoustic), false (optical), or null (no Gamma points → can't determine).
989
+ export function classify_acoustic(bs, band_idx, gamma_indices, threshold = ACOUSTIC_FREQ_THRESHOLD) {
990
+ if (gamma_indices.length === 0)
991
+ return null;
992
+ return gamma_indices.some((gamma_idx) => Math.abs(bs.bands[band_idx]?.[gamma_idx] ?? Infinity) < threshold);
993
+ }
994
+ // Build per-point metadata array for a band series in the tooltip.
995
+ export function build_point_metadata(opts) {
996
+ const { x_vals, y_vals, band_idx, spin, is_acoustic, bs, start_idx } = opts;
997
+ return x_vals.map((_, pt_idx) => {
998
+ const global_idx = start_idx + pt_idx;
999
+ const qpoint = bs.qpoints[global_idx];
1000
+ return {
1001
+ band_idx,
1002
+ spin,
1003
+ is_acoustic,
1004
+ nb_bands: bs.nb_bands,
1005
+ frac_coords: qpoint?.frac_coords ?? null,
1006
+ qpoint_label: qpoint?.label ?? null,
1007
+ band_width: bs.band_widths?.[band_idx]?.[global_idx] ?? null,
1008
+ slope: compute_slope(x_vals, y_vals, pt_idx),
1009
+ };
1010
+ });
1011
+ }
@@ -25,6 +25,7 @@ export interface BaseBandStructure {
25
25
  distance: number[];
26
26
  nb_bands: number;
27
27
  bands: number[][];
28
+ spin_down_bands?: number[][];
28
29
  band_widths?: number[][];
29
30
  }
30
31
  export interface RibbonConfig {
@@ -62,6 +63,7 @@ export interface ElectronicDos {
62
63
  dos_at_e_fermi?: number;
63
64
  }
64
65
  export type SpinMode = `mirror` | `overlay` | `up_only` | `down_only` | null;
66
+ export type BandsSpinMode = Exclude<SpinMode, `mirror`>;
65
67
  export type PdosType = `atom` | `orbital`;
66
68
  export interface StackedAreaData {
67
69
  x_values: number[];
@@ -12,7 +12,7 @@ let { atom_color_config = $bindable({
12
12
  mode: `element`,
13
13
  scale: undefined,
14
14
  scale_type: `continuous`,
15
- }), property_colors = null, elements, elem_color_picker_title = `Double click to reset color`, labels = $bindable([]), amount_format = `.3~f`, show_amounts = true, get_element_label, hidden_elements = $bindable(new Set()), hidden_prop_vals = $bindable(new Set()),
15
+ }), property_colors = null, elements, elem_color_picker_title = `Double click to reset color`, labels = $bindable([]), amount_format = `.3~f`, show_amounts = true, get_element_label, hidden_elements = $bindable(new SvelteSet()), hidden_prop_vals = $bindable(new SvelteSet()),
16
16
  // Element remapping: maps original element symbols to new ones
17
17
  element_mapping = $bindable(),
18
18
  // Per-element and per-site radius overrides
@@ -31,7 +31,7 @@ let mode_menu_open = $state(false);
31
31
  let previous_mode = $state(atom_color_config.mode);
32
32
  $effect(() => {
33
33
  if (atom_color_config.mode !== previous_mode) {
34
- hidden_prop_vals = new Set();
34
+ hidden_prop_vals.clear();
35
35
  previous_mode = atom_color_config.mode;
36
36
  }
37
37
  });
@@ -85,6 +85,21 @@ let filtered_elements = $derived.by(() => {
85
85
  data?.name?.toLowerCase().includes(query);
86
86
  });
87
87
  });
88
+ const known_element_symbols = new Set(ELEM_SYMBOLS);
89
+ let sorted_element_entries = $derived.by(() => {
90
+ if (!elements)
91
+ return [];
92
+ const element_amounts = elements;
93
+ const ordered_known_entries = ELEM_SYMBOLS
94
+ .flatMap((element_symbol) => {
95
+ const amount = element_amounts[element_symbol];
96
+ return amount === undefined ? [] : [[element_symbol, amount]];
97
+ });
98
+ const unknown_entries = Object.entries(element_amounts)
99
+ .filter(([element_symbol]) => !known_element_symbols.has(element_symbol))
100
+ .sort(([element_a], [element_b]) => element_a.localeCompare(element_b));
101
+ return [...ordered_known_entries, ...unknown_entries];
102
+ });
88
103
  function remap_element(from, to) {
89
104
  if (from === to && element_mapping?.[from]) {
90
105
  // Remove mapping if mapping back to original element
@@ -195,8 +210,11 @@ const get_site_radius = (site_idx) => {
195
210
  max={MAX_RADIUS}
196
211
  step={0.05}
197
212
  value={get_site_radius(site_idx)}
198
- oninput={(event) =>
199
- update_site_radius(site_idx, (event.target as HTMLInputElement).value)}
213
+ oninput={(event) => {
214
+ if (event.target instanceof HTMLInputElement) {
215
+ update_site_radius(site_idx, event.target.value)
216
+ }
217
+ }}
200
218
  />
201
219
  <span class="unit">Å</span>
202
220
  </label>
@@ -217,7 +235,7 @@ const get_site_radius = (site_idx) => {
217
235
 
218
236
  {#if show_element_legend}
219
237
  <div {...rest} class="atom-legend element-legend {rest.class ?? ``}">
220
- {#each Object.entries(elements!) as [elem, amt], idx (elem + amt)}
238
+ {#each sorted_element_entries as [elem, amt], idx (elem)}
221
239
  {@const is_hidden = hidden_elements.has(elem as ElementSymbol)}
222
240
  {@const displayed_elem = element_mapping?.[elem as ElementSymbol] || elem}
223
241
  <div class="legend-item">
@@ -293,11 +311,14 @@ const get_site_radius = (site_idx) => {
293
311
  max={MAX_RADIUS}
294
312
  step={0.05}
295
313
  value={get_element_radius(elem as ElementSymbol)}
296
- oninput={(event) =>
297
- update_element_radius(
298
- elem as ElementSymbol,
299
- (event.target as HTMLInputElement).value,
300
- )}
314
+ oninput={(event) => {
315
+ if (event.target instanceof HTMLInputElement) {
316
+ update_element_radius(
317
+ elem as ElementSymbol,
318
+ event.target.value,
319
+ )
320
+ }
321
+ }}
301
322
  />
302
323
  <span class="unit">Å</span>
303
324
  </label>
@@ -29,6 +29,6 @@ type $$ComponentProps = Omit<HTMLAttributes<HTMLDivElement>, `children`> & {
29
29
  structure?: AnyStructure | null;
30
30
  }]>;
31
31
  };
32
- declare const AtomLegend: import("svelte").Component<$$ComponentProps, {}, "atom_color_config" | "labels" | "hidden_elements" | "hidden_prop_vals" | "element_mapping" | "element_radius_overrides" | "site_radius_overrides">;
32
+ declare const AtomLegend: import("svelte").Component<$$ComponentProps, {}, "labels" | "atom_color_config" | "hidden_elements" | "hidden_prop_vals" | "element_mapping" | "element_radius_overrides" | "site_radius_overrides">;
33
33
  type AtomLegend = ReturnType<typeof AtomLegend>;
34
34
  export default AtomLegend;
@@ -20,6 +20,8 @@ const cell_tooltips = {
20
20
  primitive: `Primitive cell (smallest repeating unit)`,
21
21
  conventional: `Conventional cell (standardized representation)`,
22
22
  };
23
+ const hair_space = `\u200A`;
24
+ const format_supercell_label = (supercell_value) => supercell_value.replaceAll(`x`, `${hair_space}x${hair_space}`);
23
25
  function apply_preset(preset) {
24
26
  supercell_scaling = preset;
25
27
  input_value = preset;
@@ -31,6 +33,20 @@ function handle_input_submit() {
31
33
  menu_open = false;
32
34
  }
33
35
  }
36
+ function handle_focus_out(event) {
37
+ const next_target = event.relatedTarget;
38
+ const current_target = event.currentTarget;
39
+ if (!(current_target instanceof Node) ||
40
+ !(next_target instanceof Node) ||
41
+ !current_target.contains(next_target))
42
+ menu_open = false;
43
+ }
44
+ function handle_key_down(event, submit_on_enter = false) {
45
+ if (event.key === `Escape`)
46
+ menu_open = false;
47
+ if (submit_on_enter && event.key === `Enter`)
48
+ handle_input_submit();
49
+ }
34
50
  // Sync input value when external prop changes
35
51
  $effect(() => {
36
52
  if (!menu_open && supercell_scaling && supercell_scaling !== input_value) {
@@ -46,10 +62,12 @@ $effect(() => {
46
62
  onmouseenter={() => (menu_open = true)}
47
63
  onmouseleave={() => (menu_open = false)}
48
64
  onfocusin={() => (menu_open = true)}
65
+ onfocusout={handle_focus_out}
49
66
  >
50
67
  <button
51
68
  type="button"
52
69
  onclick={() => (menu_open = !menu_open)}
70
+ onkeydown={handle_key_down}
53
71
  class="toggle-btn"
54
72
  class:active={menu_open}
55
73
  aria-expanded={menu_open}
@@ -60,7 +78,9 @@ $effect(() => {
60
78
  style="--spinner-border-width: 2px; --spinner-size: 1em; --spinner-margin: 0; display: inline-block; vertical-align: middle"
61
79
  />
62
80
  {:else}
63
- {cell_type !== `original` ? `${cell_labels[cell_type]} ` : ``}{supercell_scaling}
81
+ {cell_type !== `original` ? `${cell_labels[cell_type]} ` : ``}{
82
+ format_supercell_label(supercell_scaling)
83
+ }
64
84
  {/if}
65
85
  </button>
66
86
 
@@ -101,7 +121,7 @@ $effect(() => {
101
121
  class:selected={supercell_scaling === preset}
102
122
  onclick={() => apply_preset(preset)}
103
123
  >
104
- {preset}
124
+ {format_supercell_label(preset)}
105
125
  </button>
106
126
  {/each}
107
127
  </div>
@@ -113,7 +133,7 @@ $effect(() => {
113
133
  bind:value={input_value}
114
134
  placeholder="e.g. 2x2x2"
115
135
  class:invalid={!input_valid}
116
- onkeydown={(event) => event.key === `Enter` && handle_input_submit()}
136
+ onkeydown={(event) => handle_key_down(event, true)}
117
137
  />
118
138
  <button
119
139
  class="apply-btn"
@@ -131,27 +151,47 @@ $effect(() => {
131
151
  <style>
132
152
  .cell-select {
133
153
  position: relative;
134
- font-size: var(--struct-legend-font, clamp(9pt, 3.5cqmin, 12pt));
154
+ font-size: inherit;
155
+ align-self: center;
156
+ --cell-select-accent: var(--accent-color, light-dark(#2563eb, #60a5fa));
157
+ --cell-select-surface: var(--surface-bg, light-dark(rgba(255, 255, 255, 0.96), #222));
158
+ --cell-select-border: var(
159
+ --border-color,
160
+ light-dark(rgba(0, 0, 0, 0.2), rgba(255, 255, 255, 0.25))
161
+ );
135
162
  }
136
163
  .toggle-btn {
137
164
  padding: var(--struct-legend-padding, 0 4pt);
138
165
  line-height: var(--struct-legend-line-height, 1.3);
139
166
  vertical-align: middle;
167
+ color: var(--text-color);
168
+ background: var(--btn-bg, light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.1)));
169
+ border: 1px solid var(--border-color);
170
+ border-radius: var(--border-radius, 3pt);
171
+ transition: background 0.15s ease;
172
+ }
173
+ @media (hover: hover) {
174
+ .toggle-btn:hover {
175
+ background: var(
176
+ --btn-bg-hover,
177
+ light-dark(rgba(0, 0, 0, 0.12), rgba(255, 255, 255, 0.15))
178
+ );
179
+ }
140
180
  }
141
181
  .dropdown {
142
182
  position: absolute;
143
183
  top: 100%;
144
184
  right: 0;
145
185
  margin-top: 2px;
146
- background: var(--surface-bg, #222);
147
- padding: 5px;
186
+ background: var(--surface-bg, light-dark(rgba(255, 255, 255, 0.96), #222));
187
+ padding: 6px;
148
188
  border-radius: var(--struct-border-radius, var(--border-radius, 3pt));
149
189
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
150
190
  display: flex;
151
191
  flex-direction: column;
152
- gap: 4px;
192
+ gap: 5px;
153
193
  z-index: 100;
154
- min-width: 95px;
194
+ min-width: 118px;
155
195
  }
156
196
  /* Invisible bridge to prevent menu closing when moving mouse from toggle to dropdown */
157
197
  .dropdown::before {
@@ -180,26 +220,41 @@ $effect(() => {
180
220
  /* Cell type row - compact buttons with minimal padding */
181
221
  .cell-type-row {
182
222
  display: flex;
183
- gap: 1px;
184
- padding-bottom: 3px;
185
- border-bottom: 1px solid rgba(128, 128, 128, 0.3);
223
+ gap: 3px;
224
+ padding-bottom: 5px;
225
+ border-bottom: 1px solid var(--border-color, rgba(128, 128, 128, 0.3));
186
226
  }
187
227
  .cell-type-btn {
188
228
  flex: 1;
189
- padding: 1px 0;
229
+ padding: 2px 6px;
190
230
  font-size: 0.9em;
231
+ color: var(--text-color);
232
+ background: var(--btn-bg, light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.1)));
233
+ border: 1px solid var(--border-color);
191
234
  border-radius: var(--border-radius, 3pt);
192
235
  transition: background 0.15s ease;
193
236
  white-space: nowrap;
194
237
  }
195
238
  @media (hover: hover) {
196
239
  .cell-type-btn:hover:not(.disabled) {
197
- background: rgba(255, 255, 255, 0.15);
240
+ background: var(
241
+ --btn-bg-hover,
242
+ light-dark(rgba(0, 0, 0, 0.12), rgba(255, 255, 255, 0.15))
243
+ );
198
244
  }
199
245
  }
200
246
  .cell-type-btn.selected {
201
- background: rgba(0, 255, 255, 0.4);
202
- border-color: rgba(0, 255, 255, 0.5);
247
+ color: var(--cell-select-accent);
248
+ background: color-mix(
249
+ in srgb,
250
+ var(--cell-select-accent) 18%,
251
+ var(--cell-select-surface)
252
+ );
253
+ border-color: color-mix(
254
+ in srgb,
255
+ var(--cell-select-accent) 45%,
256
+ var(--cell-select-border)
257
+ );
203
258
  }
204
259
  .cell-type-btn.disabled {
205
260
  opacity: 0.4;
@@ -215,16 +270,31 @@ $effect(() => {
215
270
  .preset-btn {
216
271
  padding: 2px 4px;
217
272
  font-size: 0.9em;
273
+ color: var(--text-color);
274
+ background: var(--btn-bg, light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.1)));
275
+ border: 1px solid var(--border-color);
218
276
  border-radius: var(--border-radius, 3pt);
219
277
  }
220
278
  @media (hover: hover) {
221
279
  .preset-btn:hover {
222
- background: rgba(255, 255, 255, 0.15);
280
+ background: var(
281
+ --btn-bg-hover,
282
+ light-dark(rgba(0, 0, 0, 0.12), rgba(255, 255, 255, 0.15))
283
+ );
223
284
  }
224
285
  }
225
286
  .preset-btn.selected {
226
- border-color: rgba(0, 255, 255, 0.5);
227
- background: rgba(0, 255, 255, 0.4);
287
+ color: var(--cell-select-accent);
288
+ background: color-mix(
289
+ in srgb,
290
+ var(--cell-select-accent) 18%,
291
+ var(--cell-select-surface)
292
+ );
293
+ border-color: color-mix(
294
+ in srgb,
295
+ var(--cell-select-accent) 45%,
296
+ var(--cell-select-border)
297
+ );
228
298
  }
229
299
 
230
300
  /* Custom input row */
@@ -234,10 +304,10 @@ $effect(() => {
234
304
  gap: 4px;
235
305
  }
236
306
  .custom-input-row input {
237
- max-width: 50px;
238
- padding: 2px 4px;
239
- margin-inline: 6px 0;
240
- font-size: 0.9em;
307
+ max-width: 60px;
308
+ min-height: 0;
309
+ padding: 1px 4px;
310
+ font-size: 0.85em;
241
311
  }
242
312
  .custom-input-row input.invalid {
243
313
  border-color: rgba(255, 100, 100, 0.6);
@@ -66,6 +66,7 @@ function get_cylinder_transform(start, end) {
66
66
  color={cell_edge_color}
67
67
  opacity={cell_edge_opacity}
68
68
  transparent
69
+ depthWrite={false}
69
70
  />
70
71
  </T.Mesh>
71
72
  {/each}
@@ -79,6 +80,7 @@ function get_cylinder_transform(start, end) {
79
80
  color={cell_surface_color}
80
81
  opacity={cell_surface_opacity}
81
82
  transparent
83
+ depthWrite={false}
82
84
  />
83
85
  </T.Mesh>
84
86
  {/if}