Skip to content

Commit

Permalink
Add slabs workgraph (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
superstar54 committed Sep 16, 2024
1 parent 6a7737d commit d2448fd
Show file tree
Hide file tree
Showing 12 changed files with 1,575 additions and 15 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pip install git+https://github.com/superstar54/workgraph-collections.git
- [Elastic constants](https://workgraph-collections.readthedocs.io/en/latest/ase/espresso/elastic.html)
- [Bands structure](https://workgraph-collections.readthedocs.io/en/latest/ase/espresso/bands.html)
- [Projected density of states (PDOS)](https://workgraph-collections.readthedocs.io/en/latest/ase/espresso/pdos.html)
- [Surface slabs](https://workgraph-collections.readthedocs.io/en/latest/ase/espresso/surface_slabs.html)
- [Bader Charge](https://workgraph-collections.readthedocs.io/en/latest/ase/espresso/bader.html)
- [X-ray photoelectron spectroscopy (XPS)](https://workgraph-collections.readthedocs.io/en/latest/ase/espresso/xps.html)
- [X-ray Absorption Near Edge Structure (XANES)](https://workgraph-collections.readthedocs.io/en/latest/ase/espresso/xas.html) (Ongoing)
Expand Down
278 changes: 278 additions & 0 deletions docs/source/ase/espresso/html/slabs.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rete.js with React in Vanilla JS</title>
<!-- Import React, ReactDOM, and Babel from CDN -->
<script src="https://unpkg.com/react@18.2.0/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18.2.0/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-is/18.2.0/umd/react-is.production.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/styled-components@5.3.6/dist/styled-components.min.js"></script>
<script src="https://unpkg.com/elkjs@0.8.2/lib/elk.bundled.js"></script>

<!-- Import Rete.js and its plugins from CDN -->
<script src="https://cdn.jsdelivr.net/npm/rete@2.0.3/rete.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/rete-area-plugin@2.0.3/rete-area-plugin.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/rete-connection-plugin@2.0.2/rete-connection-plugin.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/rete-render-utils@2.0.2/rete-render-utils.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/rete-react-plugin@2.0.5/rete-react-plugin.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/rete-auto-arrange-plugin@2.0.1/rete-auto-arrange-plugin.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/rete-minimap-plugin@2.0.1/rete-minimap-plugin.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/rete-scopes-plugin@2.1.0/rete-scopes-plugin.min.js"></script>

<style>
.App {
font-family: sans-serif;
background: rgb(200, 190, 190);
}
.rete {
position: relative;
font-size: 1rem;
margin: 1em;
border-radius: 1em;
text-align: left;
}
#fullscreen-btn {
margin-left: 10px;
}
body {
overflow: hidden;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">

const { useState, useRef, useEffect } = React;
const { createRoot } = ReactDOM;
const { NodeEditor, ClassicPreset } = Rete;
const { AreaPlugin, AreaExtensions } = ReteAreaPlugin;
const { ScopesPlugin, Presets: ScopesPresets } = ReteScopesPlugin;
const { ConnectionPlugin, Presets: ConnectionPresets } = ReteConnectionPlugin;
const { ReactPlugin, Presets } = ReteReactPlugin;
const { AutoArrangePlugin, Presets: ArrangePresets, ArrangeAppliers} = ReteAutoArrangePlugin;
const { MinimapExtra, MinimapPlugin } = ReteMinimapPlugin;
const { RenderUtils } = ReteRenderUtils;
const styled = window.styled;

const workgraphData = {"name": "slabs", "uuid": "40160f0c-73fc-11ef-a1ba-906584de3e5b", "state": "CREATED", "nodes": {"relax": {"label": "relax", "node_type": "GRAPH_BUILDER", "inputs": [], "properties": {"atoms": null, "command": "pw.x", "computer": "localhost", "input_data": null, "pseudopotentials": null, "pseudo_dir": null, "kpts": null, "kspacing": null, "run_scf": true, "max_iterations": 5, "volume_threshold": 0.1, "metadata": null, "_wait": null}, "outputs": [{"name": "atoms"}], "position": [30, 30], "children": []}, "generate_slabs": {"label": "generate_slabs", "node_type": "PYTHONJOB", "inputs": [{"name": "atoms"}, {"name": "indices"}, {"name": "atoms"}], "properties": {"atoms": null, "indices": null, "layers": 3, "vacuum": 5.0, "tol": 1e-05, "periodic": true, "center_slab": true, "_wait": null, "computer": "localhost", "code_label": null, "code_path": null, "prepend_text": null, "metadata": null, "metadata.store_provenance": null, "metadata.description": null, "metadata.label": null, "metadata.call_link_label": null, "metadata.disable_cache": null, "metadata.dry_run": null, "metadata.computer": null, "metadata.options": null, "metadata.options.input_filename": null, "metadata.options.output_filename": null, "metadata.options.submit_script_filename": null, "metadata.options.scheduler_stdout": null, "metadata.options.scheduler_stderr": null, "metadata.options.resources": null, "metadata.options.max_wallclock_seconds": null, "metadata.options.custom_scheduler_commands": null, "metadata.options.queue_name": null, "metadata.options.rerunnable": null, "metadata.options.account": null, "metadata.options.qos": null, "metadata.options.withmpi": null, "metadata.options.mpirun_extra_params": null, "metadata.options.import_sys_environment": null, "metadata.options.environment_variables": null, "metadata.options.environment_variables_double_quotes": null, "metadata.options.priority": null, "metadata.options.max_memory_kb": null, "metadata.options.prepend_text": null, "metadata.options.append_text": null, "metadata.options.parser_name": null, "metadata.options.additional_retrieve_list": null, "metadata.options.stash": null, "metadata.options.stash.target_base": null, "metadata.options.stash.source_list": null, "metadata.options.stash.stash_mode": null, "code": null, "monitors": null, "remote_folder": null, "function": null, "function_source_code": null, "function_name": null, "process_label": null, "function_kwargs": null, "function_outputs": null, "parent_folder": null, "parent_folder_name": null, "parent_output_folder": null, "upload_files": null, "copy_files": null, "additional_retrieve_list": null}, "outputs": [{"name": "slabs"}], "position": [60, 60], "children": []}, "relax_slabs": {"label": "relax_slabs", "node_type": "GRAPH_BUILDER", "inputs": [{"name": "slabs"}, {"name": "inputs"}, {"name": "slabs"}], "properties": {"slabs": null, "inputs": null, "_wait": null}, "outputs": [], "position": [90, 90], "children": []}}, "links": [{"from_socket": "atoms", "from_node": "relax", "to_socket": "atoms", "to_node": "generate_slabs", "state": false}, {"from_socket": "slabs", "from_node": "generate_slabs", "to_socket": "slabs", "to_node": "relax_slabs", "state": false}]}

// Define Schemes to use in vanilla JS
const Schemes = {
Node: ClassicPreset.Node,
Connection: ClassicPreset.Connection
};

class Node extends ClassicPreset.Node {
width = 180;
height = 100;
}
class Connection extends ClassicPreset.Connection {}

function createDynamicNode(nodeData) {
const node = new Node(nodeData.label);
// resize the node based on the max length of the input/output names
let maxSocketNameLength = 0;
nodeData.inputs.forEach((input) => {
let socket = new ClassicPreset.Socket(input.name);
if (!node.inputs.hasOwnProperty(input.name)) {
node.addInput(input.name, new ClassicPreset.Input(socket, input.name));
maxSocketNameLength = Math.max(maxSocketNameLength, input.name.length);
}
});

nodeData.outputs.forEach((output) => {
let socket = new ClassicPreset.Socket(output.name);
if (!node.outputs.hasOwnProperty(output.name)) {
node.addOutput(output.name, new ClassicPreset.Output(socket, output.name));
maxSocketNameLength = Math.max(maxSocketNameLength, output.name.length);
}
});
node.height = Math.max(140, node.height + (nodeData.inputs.length + nodeData.outputs.length) * 35)
node.width += maxSocketNameLength * 5;

return node;
}


async function addNode(editor, area, nodeData) {
console.log("Adding node", nodeData);
const node = createDynamicNode(nodeData);
await editor.addNode(node);
editor.nodeMap[nodeData.label] = node; // Assuming each nodeData has a unique ID
await area.translate(node.id, { x: nodeData.position[0], y: nodeData.position[1] });
}

async function addLink(editor, area, layout, linkData) {
const fromNode = editor.nodeMap[linkData.from_node];
const toNode = editor.nodeMap[linkData.to_node];
console.log("fromNode", fromNode, "toNode", toNode);
let socket;
if (fromNode && toNode) {
socket = new ClassicPreset.Socket(linkData.from_socket);
if (!fromNode.outputs.hasOwnProperty(linkData.from_socket)) {
fromNode.addOutput(linkData.from_socket, new ClassicPreset.Output(socket, linkData.from_socket));
fromNode.height += 25; // Increase height of node for each output
area.update('node', fromNode.id);
}
socket = new ClassicPreset.Socket(linkData.to_socket);
if (!toNode.inputs.hasOwnProperty(linkData.to_socket)) {
toNode.addInput(linkData.to_socket, new ClassicPreset.Input(socket, linkData.to_socket));
toNode.height += 25; // Increase height of node for each input
area.update('node', toNode.id);
}
await editor.addConnection(new Connection(fromNode, linkData.from_socket, toNode, linkData.to_socket));
// await layout(true);

}
}

async function loadJSON(editor, area, layout, workgraphData) {
for (const nodeId in workgraphData.nodes) {
const nodeData = workgraphData.nodes[nodeId];
await addNode(editor, area, nodeData);
}

// Adding connections based on workgraphData
workgraphData.links.forEach(async (link) => { // Specify the type of link here
await addLink(editor, area, layout, link);
});

// Add while zones
console.log("Adding while zone: ");
for (const nodeId in workgraphData.nodes) {
const nodeData = workgraphData.nodes[nodeId];
const node_type = nodeData['node_type'];
if (node_type === "WHILE" || node_type === "IF" || node_type === "ZONE") {
// find the node
const node = editor.nodeMap[nodeData.label];
const children = nodeData['children'];
// find the id of all nodes in the editor that has a label in while_zone
for (const nodeId in children) {
const node1 = editor.nodeMap[children[nodeId]];
node1.parent = node.id;
}
}
}
}

async function createEditor(container) {
const socket = new ClassicPreset.Socket("socket");

const editor = new NodeEditor(Schemes);
const area = new AreaPlugin(container);
const connection = new ConnectionPlugin();
const render = new ReactPlugin({ createRoot });
const scopes = new ScopesPlugin();
const arrange = new AutoArrangePlugin();

const minimap = new MinimapPlugin({
boundViewport: true
});

AreaExtensions.selectableNodes(area, AreaExtensions.selector(), {
accumulating: AreaExtensions.accumulateOnCtrl(),
});

render.addPreset(Presets.classic.setup());
render.addPreset(Presets.minimap.setup({ size: 200 }));

connection.addPreset(ConnectionPresets.classic.setup());
scopes.addPreset(ScopesPresets.classic.setup());

const applier = new ArrangeAppliers.TransitionApplier({
duration: 500,
timingFunction: (t) => t,
async onTick() {
await AreaExtensions.zoomAt(area, editor.getNodes());
}
});

arrange.addPreset(ArrangePresets.classic.setup());


editor.use(area);
// area.use(connection);
area.use(render);
area.use(scopes);
area.use(arrange);
area.use(minimap);

async function layout(animate) {
await arrange.layout({ applier: animate ? applier : undefined });
AreaExtensions.zoomAt(area, editor.getNodes());
}

// Adding nodes based on workgraphData
const nodeMap = {}; // To keep track of created nodes for linking
editor.nodeMap = nodeMap;


return {
editor: editor,
area: area,
layout: layout,
destroy: () => area.destroy()
};
}

function toggleFullScreen() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
} else if (document.exitFullscreen) {
document.exitFullscreen();
}
}

function App() {
const [editor, setEditor] = useState(null);
const containerRef = useRef(null);

useEffect(() => {
if (containerRef.current && !editor) {
createEditor(containerRef.current).then((editor) => {
setEditor(editor);
loadJSON(editor.editor, editor.area, editor.layout, workgraphData).then(() => {
// aplly layout twice to ensure all nodes are arranged
editor?.layout(false).then(() => editor?.layout(true));
});
window.editor = editor;
});
}
if (document.getElementById('fullscreen-btn')) {
document.getElementById('fullscreen-btn').addEventListener('click', toggleFullScreen);
}
return () => {
if (editor) {
editor.destroy();
}
};
}, [containerRef, editor]);

return (

<div className="App">
<div>
<button onClick={() => editor?.layout(true)}>Arrange</button>
<button id="fullscreen-btn">Fullscreen</button>
</div>
<div ref={containerRef} className="rete" style={{ height: "100vh", width: "100%" }}></div>
</div>
);
}

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
<App />
);
</script>
</body>
</html>
Loading

0 comments on commit d2448fd

Please sign in to comment.