Skip to content

Commit 45d1aea

Browse files
committed
kpointsvis, Card wrapper, layout improvements etc.
1 parent 92df772 commit 45d1aea

16 files changed

Lines changed: 326 additions & 191 deletions

File tree

package-lock.json

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
],
3232
"dependencies": {
3333
"bands-visualiser": "^0.0.1",
34+
"brillouinzone-visualizer": "^0.4.0",
3435
"js-yaml": "^4.1.0",
3536
"mc-react-structure-visualizer": "^0.8.2",
3637
"plotly.js-basic-dist": "^3.1.1",

src/AiidaExplorer/FlowChart/HorizontalNode/nodeUtils.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,11 @@ export function getNodeDisplay(node) {
7272
return `Grid: ${x} × ${y} × ${z}`;
7373
}
7474
if (node.attributes?.labels && node.attributes?.label_numbers) {
75-
return `Path: ${node.attributes.labels.join(" → ")}`;
75+
let path = node.attributes.labels.join(" → ");
76+
if (path.length > 20) {
77+
path = path.slice(0, 17) + "...";
78+
}
79+
return `Path: ${path}`;
7680
}
7781
return fallback;
7882

@@ -92,7 +96,7 @@ export function getNodeDisplay(node) {
9296
};
9397
const converted = formula.replace(
9498
/\d/g,
95-
(digit) => subscriptMap[digit] || digit,
99+
(digit) => subscriptMap[digit] || digit
96100
);
97101
// Cull if too long
98102
if (converted.length > maxLength) {

src/AiidaExplorer/GroupsViewer/index.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export default function GroupsViewer({ restApiUrl, setRootNodeId }) {
114114
const limit = 200;
115115

116116
// responsive
117-
const isSmallScreen = useMediaQuery("(max-width: 900px)");
117+
const isSmallScreen = useMediaQuery("(max-width: 790px)");
118118

119119
// columns based on screen
120120
const columnsToRender = isSmallScreen
@@ -179,12 +179,12 @@ export default function GroupsViewer({ restApiUrl, setRootNodeId }) {
179179
};
180180

181181
return (
182-
<div className="flex flex-col md:flex-row gap-4 overflow-auto w-full items-start">
182+
<div className="flex flex-col lg:flex-row gap-4 overflow-auto w-full items-start">
183183
{/* Left panel */}
184184
<div className="min-w-[250px] max-w-[400px] flex-shrink-0 bg-theme-50 p-2 px-3 rounded">
185185
{/* Top header + button */}
186186
<div className="flex gap-4 items-center mb-2">
187-
<h4 className="font-medium mt-2">Filter by Node Types</h4>
187+
<h4 className="font-medium">Filter by Node Types</h4>
188188
<button
189189
onClick={() => fetchNodes(0)}
190190
className="px-3 py-1 rounded bg-theme-500 text-white hover:bg-theme-700 transition"

src/AiidaExplorer/TopBar/index.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ export default function TopControls({
1212
{/* Left side buttons */}
1313
<div className="flex gap-2">
1414
<button
15-
className="group px-3 py-1 rounded-md bg-theme-100 text-theme-700 flex items-center gap-1 hover:bg-theme-200 transition-colors"
15+
className="group px-3 py-1 rounded-md bg-theme-200 text-theme-700 flex items-center gap-1 hover:bg-theme-300 transition-colors"
1616
onClick={onFindNode}
1717
>
1818
<GroupIcon className="w-5 h-5" />
1919
<span>Find Node</span>
2020
</button>
2121

2222
<button
23-
className="group px-3 py-1 rounded-md bg-theme-100 text-theme-700 flex items-center gap-1 hover:bg-theme-200 transition-colors"
23+
className="group px-3 py-1 rounded-md bg-theme-200 text-theme-700 flex items-center gap-1 hover:bg-theme-300 transition-colors"
2424
onClick={onGetLinkCounts}
2525
disabled={disableGetCounts || isLoading}
2626
>
@@ -31,7 +31,7 @@ export default function TopControls({
3131

3232
{/* Right side button */}
3333
<button
34-
className="group px-3 py-1 rounded-md bg-theme-100 text-theme-700 flex items-center gap-1 hover:bg-theme-200 transition-colors"
34+
className="group px-3 py-1 rounded-md bg-theme-200 text-theme-700 flex items-center gap-1 hover:bg-theme-300 transition-colors"
3535
onClick={onHelp}
3636
>
3737
<QuestionIcon className="w-5 h-5" />

src/AiidaExplorer/VisualiserPane/Rich/BandsDataVisualiser.jsx

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { useEffect, useRef, useState } from "react";
44
import ErrorDisplay from "../../components/Error";
55
import Spinner from "../../components/Spinner";
66

7+
import CardContainer from "../../components/CardContainer";
8+
79
export default function BandsDataVisualiser({ nodeData }) {
810
const containerRef = useRef(null);
911
const [bandsDataArray, setBandsDataArray] = useState(null);
@@ -46,22 +48,24 @@ export default function BandsDataVisualiser({ nodeData }) {
4648
}, [bandsDataArray]);
4749

4850
return (
49-
<div className="w-full max-w-5xl mx-auto p-2 bg-white border-gray-200 shadow-sm rounded-lg">
50-
<h2 className="text-md md:text-lg font-semibold px-3 pt-2">
51-
Bands Structure Plot
52-
</h2>
53-
54-
<div className="w-full h-[450px] relative flex items-center justify-center">
55-
{loading && <Spinner />}
51+
<div className="w-full mx-auto p-4 space-y-6">
52+
<CardContainer
53+
header="Bands Structure Plot"
54+
className="!px-1.5 !pt-2 !pb-0"
55+
childrenClassName="!p-0"
56+
>
57+
<div className="w-full h-[450px] relative flex items-center justify-center">
58+
{loading && <Spinner />}
5659

57-
{error && !loading && (
58-
<ErrorDisplay message={error} onRetry={() => {}} />
59-
)}
60+
{error && !loading && (
61+
<ErrorDisplay message={error} onRetry={() => {}} />
62+
)}
6063

61-
{!loading && !error && bandsDataArray && (
62-
<div ref={containerRef} className="absolute inset-4" />
63-
)}
64-
</div>
64+
{!loading && !error && bandsDataArray && (
65+
<div ref={containerRef} className="absolute inset-4" />
66+
)}
67+
</div>
68+
</CardContainer>
6569
</div>
6670
);
6771
}

src/AiidaExplorer/VisualiserPane/Rich/KpointsDataVisualiser.jsx

Lines changed: 121 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,115 @@
11
import DataTable from "../../components/DataTable";
2+
import CardContainer from "../../components/CardContainer";
3+
24
import { DownloadIcon } from "../../components/Icons";
35
import { omitGraphKeys } from "../../utils";
46

57
// This could be extended to use the BZ visualiser from this data (if possible);
68
// TODO - build a KPOINT converter that spits out VASP/QE formats
7-
// TODO - See if this data is explicitily able to produce aiida BZ from the brillouin zone
89
// TODO - move this from WIP...
10+
11+
// src/components/BZVisualizer.jsx
12+
import React, { useEffect, useRef } from "react";
13+
import { createBZVisualizer } from "brillouinzone-visualizer";
14+
15+
function BZVisualizer({ data, options }) {
16+
const containerRef = useRef(null);
17+
18+
useEffect(() => {
19+
if (containerRef.current) {
20+
containerRef.current.innerHTML = "";
21+
createBZVisualizer(containerRef.current, data, options);
22+
}
23+
}, [data, options]);
24+
25+
return <div ref={containerRef} style={{ width: "100%", height: "400px" }} />;
26+
}
27+
28+
function reciprocalLattice(cell) {
29+
if (!cell || cell.length < 3)
30+
return [
31+
[1, 0, 0],
32+
[0, 1, 0],
33+
[0, 0, 1],
34+
];
35+
36+
const [a1, a2, a3] = cell;
37+
38+
const cross = (u, v) => [
39+
u[1] * v[2] - u[2] * v[1],
40+
u[2] * v[0] - u[0] * v[2],
41+
u[0] * v[1] - u[1] * v[0],
42+
];
43+
44+
const dot = (u, v) => u[0] * v[0] + u[1] * v[1] + u[2] * v[2];
45+
46+
const volume = dot(a1, cross(a2, a3));
47+
48+
const b1 = cross(a2, a3).map((x) => (2 * Math.PI * x) / volume);
49+
const b2 = cross(a3, a1).map((x) => (2 * Math.PI * x) / volume);
50+
const b3 = cross(a1, a2).map((x) => (2 * Math.PI * x) / volume);
51+
52+
return [b1, b2, b3];
53+
}
54+
955
export default function KpointsDataVisualiser({ nodeData = {} }) {
1056
const attributes = nodeData.attributes || {};
11-
const derivedProperties = nodeData.derivedProperties || {};
57+
const derivedProperties = nodeData.derived_properties || {};
1258

1359
if (!attributes && !derivedProperties) {
1460
return <div>No attributes available</div>;
1561
}
1662

63+
// ----- Safe lattice extraction -----
64+
const lattice = attributes.cell
65+
? {
66+
a: attributes.cell[0]?.[0] ?? 0,
67+
b: attributes.cell[1]?.[1] ?? 0,
68+
c: attributes.cell[2]?.[2] ?? 0,
69+
}
70+
: null;
71+
const numSites = attributes.cell?.length || 0;
72+
73+
// ----- Bundle data for BZVisualizer -----
74+
const [b1, b2, b3] = reciprocalLattice(attributes.cell);
75+
76+
const bzData = {
77+
...derivedProperties,
78+
b1,
79+
b2,
80+
b3,
81+
};
82+
83+
// ----- Reciprocal lattice table -----
84+
const recipColumns = ["vector", "x", "y", "z"];
85+
const recipData = [
86+
{ vector: "b1", x: b1[0], y: b1[1], z: b1[2] },
87+
{ vector: "b2", x: b2[0], y: b2[1], z: b2[2] },
88+
{ vector: "b3", x: b3[0], y: b3[1], z: b3[2] },
89+
];
90+
1791
// ----- Cell table -----
1892
const cellColumns = ["x", "y", "z"];
1993
const cellData = (attributes.cell || []).map((row) => ({
20-
x: row[0],
21-
y: row[1],
22-
z: row[2],
94+
x: row[0] ?? 0,
95+
y: row[1] ?? 0,
96+
z: row[2] ?? 0,
2397
}));
2498

2599
// ----- Mesh table -----
26100
const meshColumns = ["dimension", "value"];
27101
const meshData = (attributes.mesh || []).map((val, idx) => ({
28102
dimension: idx + 1,
29-
value: val,
103+
value: val ?? 0,
30104
}));
31105

32106
// ----- Offset table -----
33107
const offsetColumns = ["i", "x", "y", "z"];
34108
const offsetData = (attributes.offset || []).map((val, idx) => ({
35109
i: idx + 1,
36-
x: val[0] ?? val,
37-
y: val[1] ?? 0,
38-
z: val[2] ?? 0,
110+
x: Array.isArray(val) ? (val[0] ?? 0) : (val ?? 0),
111+
y: Array.isArray(val) ? (val[1] ?? 0) : 0,
112+
z: Array.isArray(val) ? (val[2] ?? 0) : 0,
39113
}));
40114

41115
// ----- PBC table -----
@@ -44,62 +118,80 @@ export default function KpointsDataVisualiser({ nodeData = {} }) {
44118
.filter((key) => key in attributes)
45119
.map((key) => ({
46120
axis: key,
47-
periodic: attributes[key] ? "True" : "False",
121+
periodic: !!attributes[key],
48122
}));
49123

50124
// ----- ABS Kpoints table -----
51125
const kpointColumns = ["K-point number", "x", "y", "z"];
52126
const kpointData = (derivedProperties.explicit_kpoints_abs || []).map(
53127
(row, idx) => ({
54128
"K-point number": idx + 1,
55-
x: row[0],
56-
y: row[1],
57-
z: row[2],
129+
x: row[0] ?? 0,
130+
y: row[1] ?? 0,
131+
z: row[2] ?? 0,
58132
})
59133
);
60134

61135
return (
62-
<div className="w-full h-full p-4 space-y-6 overflow-y-auto">
63-
<div className="flex items-center gap-1">
64-
<h3 className="text-2xl">Kpoints Data</h3>
65-
{/* for now we are downloading all the props. */}
66-
<DownloadIcon
67-
data={omitGraphKeys(nodeData)}
68-
filename={`${nodeData.aiida.uuid}_KpointNodeDetails.json`}
69-
size={22}
70-
className="pb-0.5"
71-
/>
72-
</div>
136+
<div className="w-full mx-auto p-4 space-y-2">
137+
{/* Metadata / summary card */}
138+
139+
{/* Brillouin Zone visualizer card */}
140+
<CardContainer
141+
header="Brillouin Zone"
142+
className="!px-1.5 !py-2"
143+
childrenClassName="!p-0"
144+
>
145+
<div className="w-full">
146+
<BZVisualizer
147+
data={bzData}
148+
options={{
149+
showAxes: true,
150+
showBVectors: true,
151+
showPathpoints: false,
152+
disableInteractOverlay: true,
153+
}}
154+
/>
155+
</div>
156+
</CardContainer>
157+
158+
{/* Tables in cards */}
159+
<DataTable
160+
title="Recipricol cell vectors (1/Å)"
161+
columns={recipColumns}
162+
data={recipData}
163+
sortableCols={false}
164+
/>
165+
73166
<DataTable
74-
key="cell"
75167
title="Cell"
76168
columns={cellColumns}
77169
data={cellData}
78170
sortableCols={false}
79171
/>
172+
80173
<DataTable
81-
key="mesh"
82174
title="Mesh"
83175
columns={meshColumns}
84176
data={meshData}
85177
sortableCols={false}
86178
/>
179+
87180
<DataTable
88-
key="offset"
89181
title="Offset"
90182
columns={offsetColumns}
91183
data={offsetData}
92184
sortableCols={false}
93185
/>
186+
94187
<DataTable
95-
key="pbc"
96188
title="PBC Flags"
97189
columns={pbcColumns}
98190
data={pbcData}
99191
sortableCols={false}
100192
/>
193+
101194
<DataTable
102-
key="kpoints"
103195
title="Explicit K-points (Absolute)"
104196
columns={kpointColumns}
105197
data={kpointData}

0 commit comments

Comments
 (0)