From 3ab5ba6dc8a80bcecadf75612ff2c8b22f68564d Mon Sep 17 00:00:00 2001 From: Anders Hafreager Date: Sun, 7 Dec 2025 01:14:30 +0000 Subject: [PATCH 01/20] feat: add Biome linting with unused variables as errors - Replace existing linting setup with Biome - Configure Biome to treat unused variables as errors - Add lint and lint:fix scripts to package.json - Configure Biome with recommended rules and formatting --- biome.json | 45 ++++++++++++ package-lock.json | 174 +++++++++++++++++++++++++++++++++++++++++++--- package.json | 3 + 3 files changed, 213 insertions(+), 9 deletions(-) create mode 100644 biome.json diff --git a/biome.json b/biome.json new file mode 100644 index 00000000..9def2939 --- /dev/null +++ b/biome.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.3.8/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": false, + "includes": [ + "**", + "!**/node_modules/**", + "!**/dist/**", + "!**/build/**", + "!**/cpp/**", + "!**/public/lammps.mjs", + "!**/public/lammps.wasm", + "!**/src/wasm/**" + ] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 100 + }, + "assist": { "actions": { "source": { "organizeImports": "on" } } }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + } + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double", + "jsxQuoteStyle": "double", + "trailingCommas": "es5", + "semicolons": "always" + } + } +} diff --git a/package-lock.json b/package-lock.json index c801b6b4..57592d28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "easy-peasy": "^6.0.5", "katex": "^0.16.0", "localforage": "^1.10.0", - "mixpanel-browser": "^2.45.0", + "mixpanel-browser": "^2.72.0", "monaco-editor": "^0.55.1", "omovi": "^0.21.0", "protobufjs": "^7.5.4", @@ -45,10 +45,10 @@ "web-vitals": "^5.1.0" }, "devDependencies": { + "@biomejs/biome": "^2.3.8", "@jupyterlab/nbformat": "^4.5.0", "@testing-library/jest-dom": "^6.9.1", "@types/emscripten": "^1.41.5", - "@types/mixpanel-browser": "^2.38.0", "@types/node": "^20.19.25", "@types/styled-components": "^5.1.26", "@types/uuid": "^8.3.4", @@ -528,6 +528,169 @@ "node": ">=6.9.0" } }, + "node_modules/@biomejs/biome": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.8.tgz", + "integrity": "sha512-Qjsgoe6FEBxWAUzwFGFrB+1+M8y/y5kwmg5CHac+GSVOdmOIqsAiXM5QMVGZJ1eCUCLlPZtq4aFAQ0eawEUuUA==", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.3.8", + "@biomejs/cli-darwin-x64": "2.3.8", + "@biomejs/cli-linux-arm64": "2.3.8", + "@biomejs/cli-linux-arm64-musl": "2.3.8", + "@biomejs/cli-linux-x64": "2.3.8", + "@biomejs/cli-linux-x64-musl": "2.3.8", + "@biomejs/cli-win32-arm64": "2.3.8", + "@biomejs/cli-win32-x64": "2.3.8" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.8.tgz", + "integrity": "sha512-HM4Zg9CGQ3txTPflxD19n8MFPrmUAjaC7PQdLkugeeC0cQ+PiVrd7i09gaBS/11QKsTDBJhVg85CEIK9f50Qww==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.8.tgz", + "integrity": "sha512-lUDQ03D7y/qEao7RgdjWVGCu+BLYadhKTm40HkpJIi6kn8LSv5PAwRlew/DmwP4YZ9ke9XXoTIQDO1vAnbRZlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.8.tgz", + "integrity": "sha512-Uo1OJnIkJgSgF+USx970fsM/drtPcQ39I+JO+Fjsaa9ZdCN1oysQmy6oAGbyESlouz+rzEckLTF6DS7cWse95g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.8.tgz", + "integrity": "sha512-PShR4mM0sjksUMyxbyPNMxoKFPVF48fU8Qe8Sfx6w6F42verbwRLbz+QiKNiDPRJwUoMG1nPM50OBL3aOnTevA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.8.tgz", + "integrity": "sha512-QDPMD5bQz6qOVb3kiBui0zKZXASLo0NIQ9JVJio5RveBEFgDgsvJFUvZIbMbUZT3T00M/1wdzwWXk4GIh0KaAw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.8.tgz", + "integrity": "sha512-YGLkqU91r1276uwSjiUD/xaVikdxgV1QpsicT0bIA1TaieM6E5ibMZeSyjQ/izBn4tKQthUSsVZacmoJfa3pDA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.8.tgz", + "integrity": "sha512-H4IoCHvL1fXKDrTALeTKMiE7GGWFAraDwBYFquE/L/5r1927Te0mYIGseXi4F+lrrwhSWbSGt5qPFswNoBaCxg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.8.tgz", + "integrity": "sha512-RguzimPoZWtBapfKhKjcWXBVI91tiSprqdBYu7tWhgN8pKRZhw24rFeNZTNf6UiBfjCYCi9eFQs/JzJZIhuK4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, "node_modules/@csstools/color-helpers": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", @@ -2254,13 +2417,6 @@ "@types/unist": "*" } }, - "node_modules/@types/mixpanel-browser": { - "version": "2.60.0", - "resolved": "https://registry.npmjs.org/@types/mixpanel-browser/-/mixpanel-browser-2.60.0.tgz", - "integrity": "sha512-70oe8T3KdxHwsSo5aZphALdoqcsIorQBrlisnouIn9Do4dmC2C6/D56978CmSE/BO2QHgb85ojPGa4R8OFvVHA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", diff --git a/package.json b/package.json index 4f660bc6..2312db0d 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,8 @@ "test:run": "vitest run", "test:ui": "vitest --ui", "typecheck": "tsc --noEmit", + "lint": "biome check .", + "lint:fix": "biome check --write .", "predeploy": "npm run build", "deploy": "gh-pages -d dist" }, @@ -64,6 +66,7 @@ ] }, "devDependencies": { + "@biomejs/biome": "^2.3.8", "@jupyterlab/nbformat": "^4.5.0", "@testing-library/jest-dom": "^6.9.1", "@types/emscripten": "^1.41.5", From c0d425128d29b0d3b8d1de9a7599b24acfd4cd22 Mon Sep 17 00:00:00 2001 From: Anders Hafreager Date: Sun, 7 Dec 2025 01:15:04 +0000 Subject: [PATCH 02/20] style: apply Biome auto-fixes - Format code according to Biome rules - Fix linting issues automatically resolved by Biome --- .devcontainer/devcontainer.json | 8 +- biome.json | 10 +- jupyter-lite.json | 2 +- public/examples/examples.json | 2567 +++++++-------- public/manifest.json | 2 +- src/App.test.tsx | 2 +- src/App.tsx | 141 +- src/components/AutoStartSimulation.tsx | 55 +- src/components/ColorLegend.test.tsx | 6 +- src/components/ColorLegend.tsx | 15 +- src/components/Figure.test.tsx | 68 +- src/components/Figure.tsx | 35 +- src/components/LoadingSimulationScreen.tsx | 10 +- .../ResponsiveSimulationSummary.test.tsx | 38 +- .../ResponsiveSimulationSummary.tsx | 7 +- src/components/SelectedAtomsInfo.tsx | 358 ++- src/components/Simulation.tsx | 36 +- src/components/SimulationSummary.tsx | 43 +- src/components/SimulationSummaryContent.tsx | 96 +- src/components/SimulationSummaryExpanded.tsx | 95 +- src/components/SimulationSummaryModal.tsx | 3 +- src/containers/Console.tsx | 4 +- src/containers/Edit.tsx | 2790 ++++++++--------- src/containers/Examples.css | 21 +- src/containers/Examples.tsx | 71 +- src/containers/Main.tsx | 40 +- src/containers/NewSimulation.tsx | 62 +- src/containers/Notebook.tsx | 8 +- src/containers/RunInCloud.tsx | 51 +- src/containers/Settings.tsx | 40 +- src/containers/ShareSimulation.tsx | 59 +- src/containers/View.tsx | 130 +- src/global.d.ts | 3 - src/hooks/index.ts | 2 +- src/hooks/useEmbeddedMode.ts | 39 +- src/index.css | 70 +- src/index.tsx | 10 +- src/modifiers/ColorModifierSettings.tsx | 84 +- src/modifiers/SyncBondsSettings.tsx | 18 +- src/modifiers/SyncParticlesSettings.tsx | 20 +- src/modifiers/colormodifier.test.ts | 37 +- src/modifiers/colormodifier.ts | 46 +- src/modifiers/modifier.ts | 8 +- src/modifiers/syncbondsmodifier.ts | 10 +- src/modifiers/synccomputesmodifier.ts | 18 +- src/modifiers/syncfixesmodifier.ts | 18 +- src/modifiers/syncparticlesmodifier.ts | 22 +- src/modifiers/syncvariablesmodifier.ts | 20 +- src/modifiers/types.ts | 9 +- src/setupTests.ts | 21 +- src/store/app.ts | 2 +- src/store/index.ts | 2 +- src/store/model.ts | 15 +- src/store/processing.ts | 108 +- src/store/render.ts | 6 +- src/store/settings.ts | 2 +- src/store/simulation.ts | 304 +- src/store/simulationstatus.ts | 10 +- src/types.ts | 2 +- src/utils/AnalyzeNotebook.test.ts | 9 +- src/utils/AnalyzeNotebook.ts | 8 +- src/utils/atomtypes.test.ts | 3 +- src/utils/atomtypes.ts | 6 +- src/utils/boxGeometry.test.ts | 4 +- src/utils/boxGeometry.ts | 106 +- src/utils/embed/codec.test.ts | 5 +- src/utils/embed/codec.ts | 35 +- src/utils/embed/proto.ts | 152 +- src/utils/embeddedMode.test.ts | 19 +- src/utils/embeddedMode.ts | 18 +- src/utils/metrics.test.ts | 3 +- src/utils/metrics.ts | 23 +- src/utils/parsers.test.ts | 9 +- src/utils/parsers.ts | 13 +- vite.config.ts | 19 +- vitest.config.mts | 2 - 76 files changed, 3884 insertions(+), 4329 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 623872c6..ac86e4ab 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,7 +1,7 @@ { "name": "Atomify Dev Container with Emscripten", "image": "mcr.microsoft.com/devcontainers/base:ubuntu", - + "features": { "ghcr.io/devcontainers/features/node:1": { "version": "lts" @@ -18,10 +18,7 @@ "customizations": { "vscode": { - "extensions": [ - "ms-vscode.cpptools", - "ms-python.python" - ], + "extensions": ["ms-vscode.cpptools", "ms-python.python"], "settings": { "terminal.integrated.defaultProfile.linux": "bash" } @@ -30,4 +27,3 @@ "remoteUser": "vscode" } - diff --git a/biome.json b/biome.json index 9def2939..01af29dc 100644 --- a/biome.json +++ b/biome.json @@ -9,13 +9,13 @@ "ignoreUnknown": false, "includes": [ "**", - "!**/node_modules/**", - "!**/dist/**", - "!**/build/**", - "!**/cpp/**", + "!**/node_modules", + "!**/dist", + "!**/build", + "!**/cpp", "!**/public/lammps.mjs", "!**/public/lammps.wasm", - "!**/src/wasm/**" + "!**/src/wasm" ] }, "formatter": { diff --git a/jupyter-lite.json b/jupyter-lite.json index a180a2eb..8ae833b6 100644 --- a/jupyter-lite.json +++ b/jupyter-lite.json @@ -5,4 +5,4 @@ "settingsStorageName": "JupyterLite Storage", "workspacesStorageName": "JupyterLite Storage" } -} \ No newline at end of file +} diff --git a/public/examples/examples.json b/public/examples/examples.json index 1c320140..574c631f 100644 --- a/public/examples/examples.json +++ b/public/examples/examples.json @@ -1,1375 +1,1196 @@ { - "baseUrl": "examples", - "title": "Examples", - "descriptionFile": "examples.md", - "examples": [ - { - "id": "diffusion", - "title": "Diffusion", - "description": "This example shows how you can measure the diffusion coefficient using the mean square displacement. The white atoms have mass 1 while the red atoms have mass 4. This results in the red atoms having diffusion coefficient half the value of the white ones.", - "analysisDescription": "# About this simulation\nWe simulate two Lennard Jones particle types with masses 1.0 and 4.0. The diffusion coefficient [https://en.wikipedia.org/wiki/Mass_diffusivity](scales like) D$\\propto 1/\\sqrt{m}$, so we should see a factor 2 higher diffusion coefficient for the small molecules.", - "imageUrl": "diffusion/diffusion/simple_diffusion.png", - "inputScript": "simple_diffusion.in", - "keywords": [ - "lennard jones", - "diffusion" - ], - "files": [ - { - "fileName": "simple_diffusion.in", - "url": "diffusion/diffusion/simple_diffusion.in" - } - ] - }, - { - "id": "2d-msd-diffusion", - "title": "2D diffusion coefficient MSD", - "description": "We measure the diffusion coefficient using the mean square displacement.", - "analysisDescription": "# About this simulation\nWe measure the diffusion coefficient using the mean square displacement.", - "analysisScript": "2d-msd-diffusion.ipynb", - "imageUrl": "diffusion/2d-msd-diffusion/2d-msd-diffusion.png", - "inputScript": "2d-msd-diffusion.in", - "keywords": [ - "lennard jones", - "diffusion", - "mean square displacement" - ], - "files": [ - { - "fileName": "2d-msd-diffusion.in", - "url": "diffusion/2d-msd-diffusion/2d-msd-diffusion.in" - }, - { - "fileName": "2d-msd-diffusion.ipynb", - "url": "diffusion/2d-msd-diffusion/2d-msd-diffusion.ipynb" - } - ] - }, - { - "id": "2d-vacf-diffusion", - "title": "2D diffusion coefficient VACF", - "description": "We measure the diffusion coefficient using the velocity auto correlation function.", - "analysisDescription": "# About this simulation\nWe measure the diffusion coefficient using the velocity auto correlation function.", - "analysisScript": "2d-vacf-diffusion.ipynb", - "imageUrl": "diffusion/2d-vacf-diffusion/2d-vacf-diffusion.png", - "inputScript": "2d-vacf-diffusion.in", - "keywords": [ - "lennard jones", - "diffusion", - "velocity autocorrelation function" - ], - "files": [ - { - "fileName": "2d-vacf-diffusion.in", - "url": "diffusion/2d-vacf-diffusion/2d-vacf-diffusion.in" - }, - { - "fileName": "2d-vacf-diffusion.ipynb", - "url": "diffusion/2d-vacf-diffusion/2d-vacf-diffusion.ipynb" - } - ] - }, - { - "id": "proteinfolding", - "title": "Protein folding", - "description": "During this short simulation the protein evolves from an unfolded initial conformation to a misfolded conformation. It can take a very long time.", - "imageUrl": "moltemplate/frustrated/frustrated.png", - "inputScript": "run_short_sim.in.nvt", - "keywords": [ - "protein folding", - "moltemplate" - ], - "files": [ - { - "fileName": "table_dihedral_frustrated.dat", - "url": "moltemplate/frustrated/table_dihedral_frustrated.dat" - }, - { - "fileName": "system.in.init", - "url": "moltemplate/frustrated/system.in.init" - }, - { - "fileName": "system.in.settings", - "url": "moltemplate/frustrated/system.in.settings" - }, - { - "fileName": "system.data", - "url": "moltemplate/frustrated/system.data" - }, - { - "fileName": "run_short_sim.in.nvt", - "url": "moltemplate/frustrated/run_short_sim.in.nvt" - } - ] - }, - { - "id": "2D-lj-fluid", - "title": "2D Lennard Jones fluid", - "description": "This simulation consists a binary 2D gas of LJ particles.", - "imageUrl": "simongravelle/liquids/2D-lj-fluid/2D-lj-fluid.png", - "inputScript": "2D-lj-fluid.in", - "keywords": [ - "lennard jones", - "fluid", - "2d" - ], - "author": "Simon Gravelle", - "authorUrl": "https://simongravelle.github.io/", - "files": [ - { - "fileName": "2D-lj-fluid.in", - "url": "simongravelle/liquids/2D-lj-fluid/2D-lj-fluid.in" - } - ] - }, - { - "id": "3D-lj-fluid", - "title": "3D Lennard Jones fluid", - "description": "This simulation consists a binary 3D gas of LJ particles.", - "imageUrl": "simongravelle/liquids/3D-lj-fluid/3D-lj-fluid.png", - "inputScript": "3D-lj-fluid.in", - "keywords": [ - "lennard jones", - "fluid" - ], - "author": "Simon Gravelle", - "authorUrl": "https://simongravelle.github.io/", - "files": [ - { - "fileName": "3D-lj-fluid.in", - "url": "simongravelle/liquids/3D-lj-fluid/3D-lj-fluid.in" - } - ] - }, - { - "id": "nanoporous_sio2", - "title": "Large nanoporous SiO2", - "description": "A nanoporous (porosity 0.75) silica glass with 1.5 million atoms. ", - "imageUrl": "silica/nanoporous_sio2/nanoporous_sio2.png", - "inputScript": "nanoporous_sio2.in", - "keywords": [ - "vashishta", - "sio2", - "nanoporous_sio2" - ], - "files": [ - { - "fileName": "nanoporous_sio2.in", - "url": "silica/nanoporous_sio2/nanoporous_sio2.in" - }, - { - "fileName": "nanoporous_sio2.data", - "url": "silica/nanoporous_sio2/nanoporous_sio2.data" - }, - { - "fileName": "SiO2.vashishta", - "url": "silica/nanoporous_sio2/SiO2.vashishta" - } - ] - }, - { - "id": "generate_nanoporous", - "title": "Generate nanoporous silica", - "description": "Generate a nanoporous silica structure from betacristobalite using the vashishta potential.", - "imageUrl": "silica/generate_nanoporous/generate_nanoporous.png", - "inputScript": "generate_nanoporous.in", - "keywords": [ - "vashishta", - "sio2", - "amorphous", - "nanoporous" - ], - "files": [ - { - "fileName": "SiO2.vashishta", - "url": "silica/generate_nanoporous/SiO2.vashishta" - }, - { - "fileName": "generate_nanoporous.in", - "url": "silica/generate_nanoporous/generate_nanoporous.in" - }, - { - "fileName": "betacristobalite.data", - "url": "silica/generate_nanoporous/betacristobalite.data" - } - ] - }, - { - "id": "reax_cho", - "title": "Hydrocarbon oxcidation", - "description": "Hydrocarbons (such as pentane) will at high temperatures react with oxygen and form water, CO, CO2, HO2 and OH. After around 3000 timesteps, we see some interesting reactions forming new molecules.", - "imageUrl": "reaxff/CHO/CHO.png", - "inputScript": "CHO.in", - "keywords": [ - "reaxff", - "hydrocarbon", - "pentane", - "water" - ], - "files": [ - { - "fileName": "lmp_control", - "url": "reaxff/CHO/lmp_control" - }, - { - "fileName": "CHO.in", - "url": "reaxff/CHO/CHO.in" - }, - { - "fileName": "ffield.reax.cho", - "url": "reaxff/CHO/ffield.reax.cho" - }, - { - "fileName": "data.CHO", - "url": "reaxff/CHO/data.CHO" - }, - { - "fileName": "param.qeq", - "url": "reaxff/CHO/param.qeq" - } - ] - }, - { - "id": "sic_nanoparticle", - "title": "SiC nanoparticle", - "description": "A silicon carbide (SiC) nano particle in a carbon gas. The whole system is neutral, but initially, the particle has net charge. In this simulation, you can observe how the carbon gas attaches to the surface. The system cools down from 2200 K to 500 K.", - "imageUrl": "sic/nanoparticle/nanoparticle.png", - "inputScript": "nanoparticle.in", - "keywords": [ - "vashishta", - "sic", - "nanoparticle" - ], - "files": [ - { - "fileName": "nanoparticle.in", - "url": "sic/nanoparticle/nanoparticle.in" - }, - { - "fileName": "make_stoichiometric.in", - "url": "sic/nanoparticle/make_stoichiometric.in" - }, - { - "fileName": "SiC.vashishta", - "url": "sic/nanoparticle/SiC.vashishta" - }, - { - "fileName": "siliconcarbide.data", - "url": "sic/nanoparticle/siliconcarbide.data" - } - ] - }, - { - "id": "sic_faceted_nanoparticle", - "title": "Faceted SiC nanoparticle", - "description": "A faceted SiC nano particle. All facets are of the same type (110 planes), but due a nucleation barrier during a volume conserving shape change (see Mullins, Rohrer 2000) two of the surfaces are prevented from reaching equilibrium.", - "imageUrl": "sic/faceted_nanoparticle/faceted_nanoparticle.png", - "inputScript": "faceted_nanoparticle.in", - "keywords": [ - "vashishta", - "sic", - "nanoparticle" - ], - "files": [ - { - "fileName": "faceted_nanoparticle.in", - "url": "sic/faceted_nanoparticle/faceted_nanoparticle.in" - }, - { - "fileName": "SiC.vashishta", - "url": "sic/faceted_nanoparticle/SiC.vashishta" - }, - { - "fileName": "sic_nanoparticle.data", - "url": "sic/faceted_nanoparticle/sic_nanoparticle.data" - } - ] - }, - { - "id": "singlewater", - "title": "Single water molecule", - "description": "A single water molecule using the vashishta potential.", - "imageUrl": "water/singlewater/singlewater.png", - "inputScript": "singlewater.in", - "keywords": [ - "vashishta", - "water" - ], - "files": [ - { - "fileName": "singlewater.data", - "url": "water/singlewater/singlewater.data" - }, - { - "fileName": "singlewater.in", - "url": "water/singlewater/singlewater.in" - }, - { - "fileName": "H2O.vashishta", - "url": "water/singlewater/H2O.vashishta" - } - ] - }, - { - "id": "watervapor", - "title": "Water vapor", - "description": "Low density water gas using the vashishta potential.", - "imageUrl": "water/vapor/vapor.png", - "inputScript": "vapor.in", - "keywords": [ - "vashishta", - "water" - ], - "files": [ - { - "fileName": "vapor.data", - "url": "water/vapor/vapor.data" - }, - { - "fileName": "vapor.in", - "url": "water/vapor/vapor.in" - }, - { - "fileName": "H2O.vashishta", - "url": "water/vapor/H2O.vashishta" - } - ] - }, - { - "id": "granular_patterns", - "title": "Pattern formation in granular materials", - "description": "When particles (like sand, but really any particle) lie on a vibrating surface, there are various patterns that suddenly appear for certain values for the frequency and amplitude. In this simulation, you will see one such pattern. It might take some time, so be patient.", - "imageUrl": "granular/patterns/patterns.png", - "inputScript": "patterns.in", - "keywords": [ - "granular" - ], - "files": [ - { - "fileName": "patterns.in", - "url": "granular/patterns/patterns.in" - } - ] - }, - { - "id": "go-nanoparticle", - "title": "Graphene Oxcide nanoparticle", - "description": "The simulation consists of a single graphene oxide particle with a few water molecules. The initial GO nanoparticle has been generated using [make graphitics].", - "imageUrl": "simongravelle/solids/GO-nanoparticle/GO-nanoparticle.png", - "inputScript": "GO-nanoparticle.in", - "keywords": [ - "airebo", - "graphene oxcide", - "graphene", - "simongravelle" - ], - "author": "Simon Gravelle", - "authorUrl": "https://simongravelle.github.io/", - "files": [ - { - "fileName": "GO-nanoparticle.in", - "url": "simongravelle/solids/GO-nanoparticle/GO-nanoparticle.in" - }, - { - "fileName": "GO-nanoparticle.data", - "url": "simongravelle/solids/GO-nanoparticle/GO-nanoparticle.data" - }, - { - "fileName": "PARM.lammps", - "url": "simongravelle/solids/GO-nanoparticle/PARM.lammps" - } - ] - }, - { - "id": "cnt_deformation", - "title": "Carbon nanotube under deformation", - "description": "The simulation consists of a single carbon nanotube (CNT) in vacuum. At the beginning of the simulation, a few atoms are removed from the CNT. The CNT is divided into three parts: the central part and the two edges. The two edges are forced to move, which leads to the gradual elongation of the CNT. Eventually, the CNT breaks. The breaking of the chemical bonds is permitted by the use of a reactive force field (AIREBO).", - "imageUrl": "simongravelle/solids/cnt-under-deformation/cnt-under-deformation.png", - "inputScript": "cnt-under-deformation.in", - "keywords": [ - "airebo", - "cnt", - "carbon nanotube", - "carbon", - "simongravelle" - ], - "author": "Simon Gravelle", - "authorUrl": "https://simongravelle.github.io/", - "files": [ - { - "fileName": "cnt-under-deformation.data", - "url": "simongravelle/solids/cnt-under-deformation/cnt-under-deformation.data" - }, - { - "fileName": "cnt-under-deformation.in", - "url": "simongravelle/solids/cnt-under-deformation/cnt-under-deformation.in" - }, - { - "fileName": "CH.airebo", - "url": "simongravelle/solids/shared/CH.airebo" - } - ] - }, - { - "id": "nacl-solution", - "title": "NaCl solution", - "description": "The simulation consists of a bulk solution of NaCl in water.", - "imageUrl": "simongravelle/liquids/nacl-solution/nacl-solution.png", - "inputScript": "nacl-solution.in", - "keywords": [ - "lennard jones", - "salt", - "water", - "tip4p" - ], - "author": "Simon Gravelle", - "authorUrl": "https://simongravelle.github.io/", - "files": [ - { - "fileName": "nacl-solution.in", - "url": "simongravelle/liquids/nacl-solution/nacl-solution.in" - }, - { - "fileName": "nacl-solution.data", - "url": "simongravelle/liquids/nacl-solution/nacl-solution.data" - }, - { - "fileName": "PARM.lammps", - "url": "simongravelle/liquids/nacl-solution/PARM.lammps" - } - ] - }, - { - "id": "reversibly-adsorbing-particles", - "title": "Adsorption combined with diffusion create pink noise in nanopore", - "description": "The simulation consists of particles diffusing inside a cylindrical nanopore. The surface of the nanopore is covered with adsorbing sites, and the particles reversibly adsorb at the inner surface of the nanopore. The adsorption/desorption processes are modelled using bond/create and bond/break commands respectively. A bond forms if a particle comes close enough to a trap. An additional harmonic potential is added to trapped particles. The wall of the cylinder is modelled using the wall/region command.", - "imageUrl": "simongravelle/interfaces/reversibly-adsorbing-particles/reversibly-adsorbing-particles.png", - "inputScript": "reversibly-adsorbing-particles.in", - "keywords": [ - "lennard jones", - "salt", - "water", - "tip4p" - ], - "author": "Simon Gravelle", - "authorUrl": "https://simongravelle.github.io/", - "files": [ - { - "fileName": "reversibly-adsorbing-particles.in", - "url": "simongravelle/interfaces/reversibly-adsorbing-particles/reversibly-adsorbing-particles.in" - }, - { - "fileName": "reversibly-adsorbing-particles.data", - "url": "simongravelle/interfaces/reversibly-adsorbing-particles/reversibly-adsorbing-particles.data" - }, - { - "fileName": "bonds.in", - "url": "simongravelle/interfaces/reversibly-adsorbing-particles/bonds.in" - }, - { - "fileName": "groups.in", - "url": "simongravelle/interfaces/reversibly-adsorbing-particles/groups.in" - } - ] - }, - { - "id": "betacristobalite", - "title": "Silica beta cristobalite", - "description": "Beta cristobalite crystal using the vashishta potential.", - "imageUrl": "silica/betacristobalite/betacristobalite.png", - "inputScript": "betacristobalite.in", - "keywords": [ - "vashishta", - "sio2", - "crystal" - ], - "files": [ - { - "fileName": "betacristobalite.in", - "url": "silica/betacristobalite/betacristobalite.in" - }, - { - "fileName": "SiO2.vashishta", - "url": "silica/betacristobalite/SiO2.vashishta" - }, - { - "fileName": "betacristobalite.data", - "url": "silica/betacristobalite/betacristobalite.data" - } - ] - }, - { - "id": "zeolite_zsm5", - "title": "Zeolite ZSM-5", - "description": "Zeolite ZSM-5 using the vashishta potential.", - "imageUrl": "silica/zeolite_zsm5/zeolite_zsm5.png", - "inputScript": "zeolite_zsm5.in", - "keywords": [ - "vashishta", - "zeolite" - ], - "files": [ - { - "fileName": "zeolite_zsm5.in", - "url": "silica/zeolite_zsm5/zeolite_zsm5.in" - }, - { - "fileName": "zeolite_zsm5.data", - "url": "silica/zeolite_zsm5/zeolite_zsm5.data" - }, - { - "fileName": "SiO2.vashishta", - "url": "silica/zeolite_zsm5/SiO2.vashishta" - } - ] - }, - { - "id": "crack", - "title": "Crack 2D", - "description": "Crack propagation in a 2d solid.", - "imageUrl": "lammps/crack/crack.png", - "inputScript": "crack.in", - "keywords": [ - "lennard jones", - "crack", - "2d" - ], - "files": [ - { - "fileName": "crack.in", - "url": "lammps/crack/crack.in" - } - ] - }, - { - "id": "flow_couette", - "title": "Couette flow 2D", - "description": "Couette flow in a 2d channel.", - "imageUrl": "lammps/flow_couette/flow_couette.png", - "inputScript": "flow_couette.in", - "keywords": [ - "lennard jones", - "flow", - "2d" - ], - "files": [ - { - "fileName": "flow_couette.in", - "url": "lammps/flow_couette/flow_couette.in" - } - ] - }, - { - "id": "flow_poiseuille", - "title": "Poiseuille flow 2D", - "description": "Poiseuille flow in a 2d channel.", - "imageUrl": "lammps/flow_pois/flow_pois.png", - "inputScript": "flow_pois.in", - "keywords": [ - "lennard jones", - "flow", - "2d" - ], - "files": [ - { - "fileName": "flow_pois.in", - "url": "lammps/flow_pois/flow_pois.in" - } - ] - }, - { - "id": "friction", - "title": "Friction 2D", - "description": "Frictional contact of spherical asperities between 2d surfaces.", - "imageUrl": "lammps/friction/friction.png", - "inputScript": "friction.in", - "keywords": [ - "lennard jones", - "friction", - "2d" - ], - "files": [ - { - "fileName": "friction.in", - "url": "lammps/friction/friction.in" - } - ] - }, - { - "id": "cmap", - "title": "CMAP 5-body", - "description": "CMAP 5-body contributions to CHARMM force field.", - "imageUrl": "lammps/cmap/cmap.png", - "inputScript": "in.cmap", - "keywords": [ - "charmm" - ], - "files": [ - { - "fileName": "gagg.data", - "url": "lammps/cmap/gagg.data" - }, - { - "fileName": "in.cmap", - "url": "lammps/cmap/in.cmap" - }, - { - "fileName": "charmm22.cmap", - "url": "lammps/cmap/charmm22.cmap" - } - ] - }, - { - "id": "deposit_atom", - "title": "Deposit atoms", - "description": "Deposition of atoms onto a 3d substrate.", - "imageUrl": "lammps/deposit_atom/deposit.png", - "inputScript": "in.deposit.atom", - "keywords": [ - "lennard jones", - "deposit" - ], - "files": [ - { - "fileName": "in.deposit.atom", - "url": "lammps/deposit_atom/in.deposit.atom" - } - ] - }, - { - "id": "deposit_molecule", - "title": "Deposit molecules", - "description": "Deposition of molecules onto a 3d substrate.", - "imageUrl": "lammps/deposit_molecule/deposit_molecule.png", - "inputScript": "in.deposit.molecule", - "keywords": [ - "lennard jones", - "deposit", - "3d" - ], - "files": [ - { - "fileName": "in.deposit.molecule", - "url": "lammps/deposit_molecule/in.deposit.molecule" - }, - { - "fileName": "molecule.dimer", - "url": "lammps/deposit_molecule/molecule.dimer" - } - ] - }, - { - "id": "micelle", - "title": "Micelle", - "description": "Self-assembly of small lipid-like molecules into 2d bilayers.", - "imageUrl": "lammps/micelle/micelle.png", - "inputScript": "in.micelle", - "keywords": [ - "micelle" - ], - "files": [ - { - "fileName": "data.micelle", - "url": "lammps/micelle/data.micelle" - }, - { - "fileName": "def.micelle", - "url": "lammps/micelle/def.micelle" - }, - { - "fileName": "in.micelle", - "url": "lammps/micelle/in.micelle" - } - ] - }, - { - "id": "obstacle", - "title": "2d obstacle", - "description": "Flow around two voids in a 2d channel.", - "imageUrl": "lammps/obstacle/obstacle.png", - "inputScript": "in.obstacle", - "keywords": [ - "lennard jones", - "flow", - "2d" - ], - "files": [ - { - "fileName": "in.obstacle", - "url": "lammps/obstacle/in.obstacle" - } - ] - }, - { - "id": "peptide", - "title": "Peptide solvation", - "description": "Dynamics of a small solvated peptide chain (5-mer).", - "imageUrl": "lammps/peptide/peptide.png", - "inputScript": "in.peptide", - "keywords": [ - "peptide" - ], - "files": [ - { - "fileName": "in.peptide", - "url": "lammps/peptide/in.peptide" - }, - { - "fileName": "data.peptide", - "url": "lammps/peptide/data.peptide" - } - ] - }, - { - "id": "pour_2d", - "title": "2d pour.", - "description": "Pouring of granular molecules into a 2d box, then chute flow.", - "imageUrl": "lammps/pour_2d/pour_2d.png", - "inputScript": "in.pour.2d", - "keywords": [ - "granular", - "pour", - "2d" - ], - "files": [ - { - "fileName": "in.pour.2d", - "url": "lammps/pour_2d/in.pour.2d" - } - ] - }, - { - "id": "pour_2d_molecule", - "title": "2d molecule pour", - "description": "Pouring of granular molecules into a 2d box, then chute flow.", - "imageUrl": "lammps/pour_2d_molecule/pour_2d_molecule.png", - "inputScript": "in.pour.2d.molecule", - "keywords": [ - "granular", - "pour", - "2d" - ], - "files": [ - { - "fileName": "in.pour.2d.molecule", - "url": "lammps/pour_2d_molecule/in.pour.2d.molecule" - }, - { - "fileName": "molecule.vshape", - "url": "lammps/pour_2d_molecule/molecule.vshape" - } - ] - }, - { - "id": "pour_3d", - "title": "3d pour", - "description": "Pouring of granular particles into a 3d box, then chute flow.", - "imageUrl": "lammps/pour_3d/pour.png", - "inputScript": "in.pour", - "keywords": [ - "granular", - "pour" - ], - "files": [ - { - "fileName": "in.pour", - "url": "lammps/pour_3d/in.pour" - } - ] - }, - { - "id": "shear", - "title": "Shear", - "description": "sideways shear applied to 2d solid.", - "imageUrl": "lammps/shear/shear.png", - "inputScript": "in.shear", - "keywords": [ - "lennard jones", - "shear", - "2d" - ], - "files": [ - { - "fileName": "Ni_u3.eam", - "url": "lammps/shear/Ni_u3.eam" - }, - { - "fileName": "in.shear", - "url": "lammps/shear/in.shear" - } - ] - }, - { - "id": "shear_void", - "title": "Shear with void", - "description": "Sideways shear applied to 2d solid with a void.", - "imageUrl": "lammps/shear_void/shear_void.png", - "inputScript": "in.shear.void", - "keywords": [ - "lennard jones", - "shear", - "2d" - ], - "files": [ - { - "fileName": "in.shear.void", - "url": "lammps/shear_void/in.shear.void" - }, - { - "fileName": "Ni_u3.eam", - "url": "lammps/shear_void/Ni_u3.eam" - } - ] - }, - { - "id": "abstract_translocation", - "title": "Polymer through hole", - "description": "This example contains a (crude and somewhat simple) example of the translocation of a (rather short) polymer through a hole in a wall, surrounded by an explicit LJ solvent.", - "imageUrl": "moltemplate/abstract_translocation/abstract_translocation.png", - "inputScript": "run.in.npt", - "keywords": [ - "polymer" - ], - "files": [ - { - "fileName": "run.in.npt", - "url": "moltemplate/abstract_translocation/run.in.npt" - }, - { - "fileName": "system.in", - "url": "moltemplate/abstract_translocation/system.in" - }, - { - "fileName": "system.in.init", - "url": "moltemplate/abstract_translocation/system.in.init" - }, - { - "fileName": "system.in.settings", - "url": "moltemplate/abstract_translocation/system.in.settings" - }, - { - "fileName": "run.in.nvt", - "url": "moltemplate/abstract_translocation/run.in.nvt" - }, - { - "fileName": "system.data", - "url": "moltemplate/abstract_translocation/system.data" - } - ] - }, - { - "id": "alkane_chain_single", - "title": "Alkane chain", - "description": "This example is a simple simulation of a long alkane chain, in a vacuum at room temperature using the OPLSAA force field.", - "imageUrl": "moltemplate/alkane_chain_single/alkane_chain_2.png", - "inputScript": "run.in.nvt", - "keywords": [ - "alkane" - ], - "files": [ - { - "fileName": "system_after_min.data", - "url": "moltemplate/alkane_chain_single/system_after_min.data" - }, - { - "fileName": "system.in", - "url": "moltemplate/alkane_chain_single/system.in" - }, - { - "fileName": "system.in.init", - "url": "moltemplate/alkane_chain_single/system.in.init" - }, - { - "fileName": "run.in.min", - "url": "moltemplate/alkane_chain_single/run.in.min" - }, - { - "fileName": "system.in.settings", - "url": "moltemplate/alkane_chain_single/system.in.settings" - }, - { - "fileName": "run.in.nvt", - "url": "moltemplate/alkane_chain_single/run.in.nvt" - }, - { - "fileName": "system.data", - "url": "moltemplate/alkane_chain_single/system.data" - }, - { - "fileName": "system.in.charges", - "url": "moltemplate/alkane_chain_single/system.in.charges" - } - ] - }, - { - "id": "benzene", - "title": "Benzene", - "description": "This example shows how to build a box of benzene molecules using the AMBER/GAFF force-field.", - "imageUrl": "moltemplate/benzene/benzene.png", - "inputScript": "run.in.npt", - "keywords": [ - "amber", - "benzene" - ], - "files": [ - { - "fileName": "run.in.npt", - "url": "moltemplate/benzene/run.in.npt" - }, - { - "fileName": "system.in", - "url": "moltemplate/benzene/system.in" - }, - { - "fileName": "system.in.init", - "url": "moltemplate/benzene/system.in.init" - }, - { - "fileName": "system.in.settings", - "url": "moltemplate/benzene/system.in.settings" - }, - { - "fileName": "run.in.nvt", - "url": "moltemplate/benzene/run.in.nvt" - }, - { - "fileName": "system.data", - "url": "moltemplate/benzene/system.data" - } - ] - }, - { - "id": "ice_crystal", - "title": "Ice crystal", - "description": "A simulation of an ice crystal using SPCE water and the shake algorithm.", - "imageUrl": "moltemplate/ice_crystal/ice_crystal.png", - "inputScript": "run.in.npt", - "keywords": [ - "spce", - "water", - "ice", - "shake" - ], - "files": [ - { - "fileName": "run.in.npt", - "url": "moltemplate/ice_crystal/run.in.npt" - }, - { - "fileName": "system.in", - "url": "moltemplate/ice_crystal/system.in" - }, - { - "fileName": "system_after_npt.data", - "url": "moltemplate/ice_crystal/system_after_npt.data" - }, - { - "fileName": "system.in.init", - "url": "moltemplate/ice_crystal/system.in.init" - }, - { - "fileName": "system.in.settings", - "url": "moltemplate/ice_crystal/system.in.settings" - }, - { - "fileName": "run.in.nvt", - "url": "moltemplate/ice_crystal/run.in.nvt" - }, - { - "fileName": "system.data", - "url": "moltemplate/ice_crystal/system.data" - } - ] - }, - { - "id": "methane", - "title": "Methane", - "description": "This example demonstrates how to build a simulation containing a box of methane.", - "imageUrl": "moltemplate/methane/methane.png", - "inputScript": "run.in.npt", - "keywords": [ - "methane" - ], - "files": [ - { - "fileName": "run.in.npt", - "url": "moltemplate/methane/run.in.npt" - }, - { - "fileName": "system_after_min.data", - "url": "moltemplate/methane/system_after_min.data" - }, - { - "fileName": "system.in", - "url": "moltemplate/methane/system.in" - }, - { - "fileName": "system.in.init", - "url": "moltemplate/methane/system.in.init" - }, - { - "fileName": "system.in.settings", - "url": "moltemplate/methane/system.in.settings" - }, - { - "fileName": "run.in.nvt", - "url": "moltemplate/methane/run.in.nvt" - }, - { - "fileName": "system.data", - "url": "moltemplate/methane/system.data" - }, - { - "fileName": "system.in.charges", - "url": "moltemplate/methane/system.in.charges" - } - ] - }, - { - "id": "nanotube_water", - "title": "Water in nanotube", - "description": "This is a small version of a carbon-nanotube, water capillary system.", - "imageUrl": "moltemplate/nanotube+water/nanotube+water.png", - "inputScript": "run.in.npt", - "keywords": [ - "water", - "cnt", - "carbon nanotube" - ], - "files": [ - { - "fileName": "run.in.npt", - "url": "moltemplate/nanotube+water/run.in.npt" - }, - { - "fileName": "system.in", - "url": "moltemplate/nanotube+water/system.in" - }, - { - "fileName": "system.in.init", - "url": "moltemplate/nanotube+water/system.in.init" - }, - { - "fileName": "system.in.settings", - "url": "moltemplate/nanotube+water/system.in.settings" - }, - { - "fileName": "system.data", - "url": "moltemplate/nanotube+water/system.data" - } - ] - }, - { - "id": "water_methane", - "title": "Methane in water", - "description": "This example contains a mixture of SPCE water and methane. The methane molecules use OPLSAA force-field.", - "imageUrl": "moltemplate/waterSPCE+methane/waterSPCE+methane.png", - "inputScript": "run.in.npt", - "keywords": [ - "spce", - "water", - "methane" - ], - "files": [ - { - "fileName": "run.in.npt", - "url": "moltemplate/waterSPCE+methane/run.in.npt" - }, - { - "fileName": "system_after_min.data", - "url": "moltemplate/waterSPCE+methane/system_after_min.data" - }, - { - "fileName": "system.in", - "url": "moltemplate/waterSPCE+methane/system.in" - }, - { - "fileName": "system.in.init", - "url": "moltemplate/waterSPCE+methane/system.in.init" - }, - { - "fileName": "system.in.settings", - "url": "moltemplate/waterSPCE+methane/system.in.settings" - }, - { - "fileName": "run.in.nvt", - "url": "moltemplate/waterSPCE+methane/run.in.nvt" - }, - { - "fileName": "system.data", - "url": "moltemplate/waterSPCE+methane/system.data" - }, - { - "fileName": "system.in.charges", - "url": "moltemplate/waterSPCE+methane/system.in.charges" - } - ] - }, - { - "id": "water_nacl", - "title": "Salt water", - "description": "This example contains a mixture of SPCE water and salt (NaCl).", - "imageUrl": "moltemplate/waterSPCE+Na+Cl/waterSPCE+NA+CL.png", - "inputScript": "run.in.npt", - "keywords": [ - "spce", - "water", - "salt" - ], - "files": [ - { - "fileName": "run.in.npt", - "url": "moltemplate/waterSPCE+Na+Cl/run.in.npt" - }, - { - "fileName": "system.in", - "url": "moltemplate/waterSPCE+Na+Cl/system.in" - }, - { - "fileName": "system.in.init", - "url": "moltemplate/waterSPCE+Na+Cl/system.in.init" - }, - { - "fileName": "system.in.settings", - "url": "moltemplate/waterSPCE+Na+Cl/system.in.settings" - }, - { - "fileName": "run.in.nvt", - "url": "moltemplate/waterSPCE+Na+Cl/run.in.nvt" - }, - { - "fileName": "system.data", - "url": "moltemplate/waterSPCE+Na+Cl/system.data" - } - ] - }, - { - "id": "water_isobutane", - "title": "TIP3P water and isobutane", - "description": "The simulation consists of a mixture of isobutane and water. Over time (less than 1 ns), the two molecules phase-separate.", - "imageUrl": "moltemplate/waterTIP3P+isobutane/waterTIP3P+isobutane.png", - "inputScript": "run.in.npt", - "keywords": [ - "tip3p", - "water", - "isobutane" - ], - "files": [ - { - "fileName": "run.in.npt", - "url": "moltemplate/waterTIP3P+isobutane/run.in.npt" - }, - { - "fileName": "system.in", - "url": "moltemplate/waterTIP3P+isobutane/system.in" - }, - { - "fileName": "system.in.init", - "url": "moltemplate/waterTIP3P+isobutane/system.in.init" - }, - { - "fileName": "system.in.settings", - "url": "moltemplate/waterTIP3P+isobutane/system.in.settings" - }, - { - "fileName": "run.in.nvt", - "url": "moltemplate/waterTIP3P+isobutane/run.in.nvt" - }, - { - "fileName": "system.data", - "url": "moltemplate/waterTIP3P+isobutane/system.data" - } - ] - }, - { - "id": "reax_ab", - "title": "Ammonia-Borine (AB)", - "description": "Combustion of Ammonia-Borine (AB) can be simulated with ReaxFF. Two AB molecules meet and a H2 molecule is formed.", - "imageUrl": "reaxff/AB/AB.png", - "inputScript": "AB.in", - "keywords": [ - "reaxff", - "combustion", - "ammonia borine" - ], - "files": [ - { - "fileName": "lmp_control", - "url": "reaxff/AB/lmp_control" - }, - { - "fileName": "ffield.reax.AB", - "url": "reaxff/AB/ffield.reax.AB" - }, - { - "fileName": "AB.in", - "url": "reaxff/AB/AB.in" - }, - { - "fileName": "data.AB", - "url": "reaxff/AB/data.AB" - }, - { - "fileName": "param.qeq", - "url": "reaxff/AB/param.qeq" - } - ] - }, - { - "id": "reax_feoh3", - "title": "Iron(III) hydroxide", - "description": "In this simulation, we see low density Iron(III) hydroxide (Fe(OH)3) at high temperature.", - "imageUrl": "reaxff/FeOH3/FeOH3.png", - "inputScript": "FeOH3.in", - "keywords": [ - "reaxff", - "iron hydroxide" - ], - "files": [ - { - "fileName": "data.FeOH3", - "url": "reaxff/FeOH3/data.FeOH3" - }, - { - "fileName": "lmp_control", - "url": "reaxff/FeOH3/lmp_control" - }, - { - "fileName": "ffield.reax.Fe_O_C_H", - "url": "reaxff/FeOH3/ffield.reax.Fe_O_C_H" - }, - { - "fileName": "FeOH3.in", - "url": "reaxff/FeOH3/FeOH3.in" - }, - { - "fileName": "param.qeq", - "url": "reaxff/FeOH3/param.qeq" - } - ] - }, - { - "id": "reax_rdf", - "title": "RDX explosive", - "description": "RDX is an organic compound normally used as an explosive, especially in world war II.", - "imageUrl": "reaxff/RDX/RDX.png", - "inputScript": "RDX.in", - "keywords": [ - "reaxff", - "explosive" - ], - "files": [ - { - "fileName": "lmp_control", - "url": "reaxff/RDX/lmp_control" - }, - { - "fileName": "RDX.in", - "url": "reaxff/RDX/RDX.in" - }, - { - "fileName": "data.RDX", - "url": "reaxff/RDX/data.RDX" - }, - { - "fileName": "ffield.reax.rdx", - "url": "reaxff/RDX/ffield.reax.rdx" - }, - { - "fileName": "param.qeq", - "url": "reaxff/RDX/param.qeq" - } - ] - }, - { - "id": "reax_ssz13", - "title": "Zeolite SSZ-13", - "description": "This structure is a zeolite called SSZ-13 (or CHA) and has one of the silicon sites replaced with Aluminum and a hydrogen atom.", - "imageUrl": "reaxff/ssz_13/ssz_13.png", - "inputScript": "ssz_13.in", - "keywords": [ - "reaxff", - "zeolite" - ], - "files": [ - { - "fileName": "ffield_Psofogiannakis_CuOH_2015", - "url": "reaxff/ssz_13/ffield_Psofogiannakis_CuOH_2015" - }, - { - "fileName": "ssz_13.in", - "url": "reaxff/ssz_13/ssz_13.in" - }, - { - "fileName": "CHA_wAl.data", - "url": "reaxff/ssz_13/CHA_wAl.data" - } - ] - }, - { - "id": "reax_voh", - "title": "Vanadium Oxide", - "description": "In this simulation, we see vanadium oxide being simulated using ReaxFF at high temperature.", - "imageUrl": "reaxff/VOH/VOH.png", - "inputScript": "VOH.in", - "keywords": [ - "reaxff", - "vanadium oxide" - ], - "files": [ - { - "fileName": "lmp_control", - "url": "reaxff/VOH/lmp_control" - }, - { - "fileName": "ffield.reax.V_O_C_H", - "url": "reaxff/VOH/ffield.reax.V_O_C_H" - }, - { - "fileName": "data.VOH", - "url": "reaxff/VOH/data.VOH" - }, - { - "fileName": "VOH.in", - "url": "reaxff/VOH/VOH.in" - }, - { - "fileName": "param.qeq", - "url": "reaxff/VOH/param.qeq" - } - ] - }, - { - "id": "reax_znoh2", - "title": "Zn(OH)2", - "description": "In this simulation, we see Zn(OH)2 at high temperature.", - "imageUrl": "reaxff/ZnOH2/ZnOH2.png", - "inputScript": "ZnOH2.in", - "keywords": [ - "reaxff" - ], - "files": [ - { - "fileName": "ZnOH2.in", - "url": "reaxff/ZnOH2/ZnOH2.in" - }, - { - "fileName": "lmp_control", - "url": "reaxff/ZnOH2/lmp_control" - }, - { - "fileName": "data.ZnOH2", - "url": "reaxff/ZnOH2/data.ZnOH2" - }, - { - "fileName": "ffield.reax.ZnOH", - "url": "reaxff/ZnOH2/ffield.reax.ZnOH" - }, - { - "fileName": "param.qeq", - "url": "reaxff/ZnOH2/param.qeq" - } - ] + "baseUrl": "examples", + "title": "Examples", + "descriptionFile": "examples.md", + "examples": [ + { + "id": "diffusion", + "title": "Diffusion", + "description": "This example shows how you can measure the diffusion coefficient using the mean square displacement. The white atoms have mass 1 while the red atoms have mass 4. This results in the red atoms having diffusion coefficient half the value of the white ones.", + "analysisDescription": "# About this simulation\nWe simulate two Lennard Jones particle types with masses 1.0 and 4.0. The diffusion coefficient [https://en.wikipedia.org/wiki/Mass_diffusivity](scales like) D$\\propto 1/\\sqrt{m}$, so we should see a factor 2 higher diffusion coefficient for the small molecules.", + "imageUrl": "diffusion/diffusion/simple_diffusion.png", + "inputScript": "simple_diffusion.in", + "keywords": ["lennard jones", "diffusion"], + "files": [ + { + "fileName": "simple_diffusion.in", + "url": "diffusion/diffusion/simple_diffusion.in" + } + ] + }, + { + "id": "2d-msd-diffusion", + "title": "2D diffusion coefficient MSD", + "description": "We measure the diffusion coefficient using the mean square displacement.", + "analysisDescription": "# About this simulation\nWe measure the diffusion coefficient using the mean square displacement.", + "analysisScript": "2d-msd-diffusion.ipynb", + "imageUrl": "diffusion/2d-msd-diffusion/2d-msd-diffusion.png", + "inputScript": "2d-msd-diffusion.in", + "keywords": ["lennard jones", "diffusion", "mean square displacement"], + "files": [ + { + "fileName": "2d-msd-diffusion.in", + "url": "diffusion/2d-msd-diffusion/2d-msd-diffusion.in" + }, + { + "fileName": "2d-msd-diffusion.ipynb", + "url": "diffusion/2d-msd-diffusion/2d-msd-diffusion.ipynb" + } + ] + }, + { + "id": "2d-vacf-diffusion", + "title": "2D diffusion coefficient VACF", + "description": "We measure the diffusion coefficient using the velocity auto correlation function.", + "analysisDescription": "# About this simulation\nWe measure the diffusion coefficient using the velocity auto correlation function.", + "analysisScript": "2d-vacf-diffusion.ipynb", + "imageUrl": "diffusion/2d-vacf-diffusion/2d-vacf-diffusion.png", + "inputScript": "2d-vacf-diffusion.in", + "keywords": ["lennard jones", "diffusion", "velocity autocorrelation function"], + "files": [ + { + "fileName": "2d-vacf-diffusion.in", + "url": "diffusion/2d-vacf-diffusion/2d-vacf-diffusion.in" + }, + { + "fileName": "2d-vacf-diffusion.ipynb", + "url": "diffusion/2d-vacf-diffusion/2d-vacf-diffusion.ipynb" + } + ] + }, + { + "id": "proteinfolding", + "title": "Protein folding", + "description": "During this short simulation the protein evolves from an unfolded initial conformation to a misfolded conformation. It can take a very long time.", + "imageUrl": "moltemplate/frustrated/frustrated.png", + "inputScript": "run_short_sim.in.nvt", + "keywords": ["protein folding", "moltemplate"], + "files": [ + { + "fileName": "table_dihedral_frustrated.dat", + "url": "moltemplate/frustrated/table_dihedral_frustrated.dat" + }, + { + "fileName": "system.in.init", + "url": "moltemplate/frustrated/system.in.init" + }, + { + "fileName": "system.in.settings", + "url": "moltemplate/frustrated/system.in.settings" + }, + { + "fileName": "system.data", + "url": "moltemplate/frustrated/system.data" + }, + { + "fileName": "run_short_sim.in.nvt", + "url": "moltemplate/frustrated/run_short_sim.in.nvt" + } + ] + }, + { + "id": "2D-lj-fluid", + "title": "2D Lennard Jones fluid", + "description": "This simulation consists a binary 2D gas of LJ particles.", + "imageUrl": "simongravelle/liquids/2D-lj-fluid/2D-lj-fluid.png", + "inputScript": "2D-lj-fluid.in", + "keywords": ["lennard jones", "fluid", "2d"], + "author": "Simon Gravelle", + "authorUrl": "https://simongravelle.github.io/", + "files": [ + { + "fileName": "2D-lj-fluid.in", + "url": "simongravelle/liquids/2D-lj-fluid/2D-lj-fluid.in" + } + ] + }, + { + "id": "3D-lj-fluid", + "title": "3D Lennard Jones fluid", + "description": "This simulation consists a binary 3D gas of LJ particles.", + "imageUrl": "simongravelle/liquids/3D-lj-fluid/3D-lj-fluid.png", + "inputScript": "3D-lj-fluid.in", + "keywords": ["lennard jones", "fluid"], + "author": "Simon Gravelle", + "authorUrl": "https://simongravelle.github.io/", + "files": [ + { + "fileName": "3D-lj-fluid.in", + "url": "simongravelle/liquids/3D-lj-fluid/3D-lj-fluid.in" + } + ] + }, + { + "id": "nanoporous_sio2", + "title": "Large nanoporous SiO2", + "description": "A nanoporous (porosity 0.75) silica glass with 1.5 million atoms. ", + "imageUrl": "silica/nanoporous_sio2/nanoporous_sio2.png", + "inputScript": "nanoporous_sio2.in", + "keywords": ["vashishta", "sio2", "nanoporous_sio2"], + "files": [ + { + "fileName": "nanoporous_sio2.in", + "url": "silica/nanoporous_sio2/nanoporous_sio2.in" + }, + { + "fileName": "nanoporous_sio2.data", + "url": "silica/nanoporous_sio2/nanoporous_sio2.data" + }, + { + "fileName": "SiO2.vashishta", + "url": "silica/nanoporous_sio2/SiO2.vashishta" + } + ] + }, + { + "id": "generate_nanoporous", + "title": "Generate nanoporous silica", + "description": "Generate a nanoporous silica structure from betacristobalite using the vashishta potential.", + "imageUrl": "silica/generate_nanoporous/generate_nanoporous.png", + "inputScript": "generate_nanoporous.in", + "keywords": ["vashishta", "sio2", "amorphous", "nanoporous"], + "files": [ + { + "fileName": "SiO2.vashishta", + "url": "silica/generate_nanoporous/SiO2.vashishta" + }, + { + "fileName": "generate_nanoporous.in", + "url": "silica/generate_nanoporous/generate_nanoporous.in" + }, + { + "fileName": "betacristobalite.data", + "url": "silica/generate_nanoporous/betacristobalite.data" + } + ] + }, + { + "id": "reax_cho", + "title": "Hydrocarbon oxcidation", + "description": "Hydrocarbons (such as pentane) will at high temperatures react with oxygen and form water, CO, CO2, HO2 and OH. After around 3000 timesteps, we see some interesting reactions forming new molecules.", + "imageUrl": "reaxff/CHO/CHO.png", + "inputScript": "CHO.in", + "keywords": ["reaxff", "hydrocarbon", "pentane", "water"], + "files": [ + { + "fileName": "lmp_control", + "url": "reaxff/CHO/lmp_control" + }, + { + "fileName": "CHO.in", + "url": "reaxff/CHO/CHO.in" + }, + { + "fileName": "ffield.reax.cho", + "url": "reaxff/CHO/ffield.reax.cho" + }, + { + "fileName": "data.CHO", + "url": "reaxff/CHO/data.CHO" + }, + { + "fileName": "param.qeq", + "url": "reaxff/CHO/param.qeq" + } + ] + }, + { + "id": "sic_nanoparticle", + "title": "SiC nanoparticle", + "description": "A silicon carbide (SiC) nano particle in a carbon gas. The whole system is neutral, but initially, the particle has net charge. In this simulation, you can observe how the carbon gas attaches to the surface. The system cools down from 2200 K to 500 K.", + "imageUrl": "sic/nanoparticle/nanoparticle.png", + "inputScript": "nanoparticle.in", + "keywords": ["vashishta", "sic", "nanoparticle"], + "files": [ + { + "fileName": "nanoparticle.in", + "url": "sic/nanoparticle/nanoparticle.in" + }, + { + "fileName": "make_stoichiometric.in", + "url": "sic/nanoparticle/make_stoichiometric.in" + }, + { + "fileName": "SiC.vashishta", + "url": "sic/nanoparticle/SiC.vashishta" + }, + { + "fileName": "siliconcarbide.data", + "url": "sic/nanoparticle/siliconcarbide.data" + } + ] + }, + { + "id": "sic_faceted_nanoparticle", + "title": "Faceted SiC nanoparticle", + "description": "A faceted SiC nano particle. All facets are of the same type (110 planes), but due a nucleation barrier during a volume conserving shape change (see Mullins, Rohrer 2000) two of the surfaces are prevented from reaching equilibrium.", + "imageUrl": "sic/faceted_nanoparticle/faceted_nanoparticle.png", + "inputScript": "faceted_nanoparticle.in", + "keywords": ["vashishta", "sic", "nanoparticle"], + "files": [ + { + "fileName": "faceted_nanoparticle.in", + "url": "sic/faceted_nanoparticle/faceted_nanoparticle.in" + }, + { + "fileName": "SiC.vashishta", + "url": "sic/faceted_nanoparticle/SiC.vashishta" + }, + { + "fileName": "sic_nanoparticle.data", + "url": "sic/faceted_nanoparticle/sic_nanoparticle.data" + } + ] + }, + { + "id": "singlewater", + "title": "Single water molecule", + "description": "A single water molecule using the vashishta potential.", + "imageUrl": "water/singlewater/singlewater.png", + "inputScript": "singlewater.in", + "keywords": ["vashishta", "water"], + "files": [ + { + "fileName": "singlewater.data", + "url": "water/singlewater/singlewater.data" + }, + { + "fileName": "singlewater.in", + "url": "water/singlewater/singlewater.in" + }, + { + "fileName": "H2O.vashishta", + "url": "water/singlewater/H2O.vashishta" + } + ] + }, + { + "id": "watervapor", + "title": "Water vapor", + "description": "Low density water gas using the vashishta potential.", + "imageUrl": "water/vapor/vapor.png", + "inputScript": "vapor.in", + "keywords": ["vashishta", "water"], + "files": [ + { + "fileName": "vapor.data", + "url": "water/vapor/vapor.data" + }, + { + "fileName": "vapor.in", + "url": "water/vapor/vapor.in" + }, + { + "fileName": "H2O.vashishta", + "url": "water/vapor/H2O.vashishta" + } + ] + }, + { + "id": "granular_patterns", + "title": "Pattern formation in granular materials", + "description": "When particles (like sand, but really any particle) lie on a vibrating surface, there are various patterns that suddenly appear for certain values for the frequency and amplitude. In this simulation, you will see one such pattern. It might take some time, so be patient.", + "imageUrl": "granular/patterns/patterns.png", + "inputScript": "patterns.in", + "keywords": ["granular"], + "files": [ + { + "fileName": "patterns.in", + "url": "granular/patterns/patterns.in" + } + ] + }, + { + "id": "go-nanoparticle", + "title": "Graphene Oxcide nanoparticle", + "description": "The simulation consists of a single graphene oxide particle with a few water molecules. The initial GO nanoparticle has been generated using [make graphitics].", + "imageUrl": "simongravelle/solids/GO-nanoparticle/GO-nanoparticle.png", + "inputScript": "GO-nanoparticle.in", + "keywords": ["airebo", "graphene oxcide", "graphene", "simongravelle"], + "author": "Simon Gravelle", + "authorUrl": "https://simongravelle.github.io/", + "files": [ + { + "fileName": "GO-nanoparticle.in", + "url": "simongravelle/solids/GO-nanoparticle/GO-nanoparticle.in" + }, + { + "fileName": "GO-nanoparticle.data", + "url": "simongravelle/solids/GO-nanoparticle/GO-nanoparticle.data" + }, + { + "fileName": "PARM.lammps", + "url": "simongravelle/solids/GO-nanoparticle/PARM.lammps" + } + ] + }, + { + "id": "cnt_deformation", + "title": "Carbon nanotube under deformation", + "description": "The simulation consists of a single carbon nanotube (CNT) in vacuum. At the beginning of the simulation, a few atoms are removed from the CNT. The CNT is divided into three parts: the central part and the two edges. The two edges are forced to move, which leads to the gradual elongation of the CNT. Eventually, the CNT breaks. The breaking of the chemical bonds is permitted by the use of a reactive force field (AIREBO).", + "imageUrl": "simongravelle/solids/cnt-under-deformation/cnt-under-deformation.png", + "inputScript": "cnt-under-deformation.in", + "keywords": ["airebo", "cnt", "carbon nanotube", "carbon", "simongravelle"], + "author": "Simon Gravelle", + "authorUrl": "https://simongravelle.github.io/", + "files": [ + { + "fileName": "cnt-under-deformation.data", + "url": "simongravelle/solids/cnt-under-deformation/cnt-under-deformation.data" + }, + { + "fileName": "cnt-under-deformation.in", + "url": "simongravelle/solids/cnt-under-deformation/cnt-under-deformation.in" + }, + { + "fileName": "CH.airebo", + "url": "simongravelle/solids/shared/CH.airebo" + } + ] + }, + { + "id": "nacl-solution", + "title": "NaCl solution", + "description": "The simulation consists of a bulk solution of NaCl in water.", + "imageUrl": "simongravelle/liquids/nacl-solution/nacl-solution.png", + "inputScript": "nacl-solution.in", + "keywords": ["lennard jones", "salt", "water", "tip4p"], + "author": "Simon Gravelle", + "authorUrl": "https://simongravelle.github.io/", + "files": [ + { + "fileName": "nacl-solution.in", + "url": "simongravelle/liquids/nacl-solution/nacl-solution.in" + }, + { + "fileName": "nacl-solution.data", + "url": "simongravelle/liquids/nacl-solution/nacl-solution.data" + }, + { + "fileName": "PARM.lammps", + "url": "simongravelle/liquids/nacl-solution/PARM.lammps" + } + ] + }, + { + "id": "reversibly-adsorbing-particles", + "title": "Adsorption combined with diffusion create pink noise in nanopore", + "description": "The simulation consists of particles diffusing inside a cylindrical nanopore. The surface of the nanopore is covered with adsorbing sites, and the particles reversibly adsorb at the inner surface of the nanopore. The adsorption/desorption processes are modelled using bond/create and bond/break commands respectively. A bond forms if a particle comes close enough to a trap. An additional harmonic potential is added to trapped particles. The wall of the cylinder is modelled using the wall/region command.", + "imageUrl": "simongravelle/interfaces/reversibly-adsorbing-particles/reversibly-adsorbing-particles.png", + "inputScript": "reversibly-adsorbing-particles.in", + "keywords": ["lennard jones", "salt", "water", "tip4p"], + "author": "Simon Gravelle", + "authorUrl": "https://simongravelle.github.io/", + "files": [ + { + "fileName": "reversibly-adsorbing-particles.in", + "url": "simongravelle/interfaces/reversibly-adsorbing-particles/reversibly-adsorbing-particles.in" + }, + { + "fileName": "reversibly-adsorbing-particles.data", + "url": "simongravelle/interfaces/reversibly-adsorbing-particles/reversibly-adsorbing-particles.data" + }, + { + "fileName": "bonds.in", + "url": "simongravelle/interfaces/reversibly-adsorbing-particles/bonds.in" + }, + { + "fileName": "groups.in", + "url": "simongravelle/interfaces/reversibly-adsorbing-particles/groups.in" + } + ] + }, + { + "id": "betacristobalite", + "title": "Silica beta cristobalite", + "description": "Beta cristobalite crystal using the vashishta potential.", + "imageUrl": "silica/betacristobalite/betacristobalite.png", + "inputScript": "betacristobalite.in", + "keywords": ["vashishta", "sio2", "crystal"], + "files": [ + { + "fileName": "betacristobalite.in", + "url": "silica/betacristobalite/betacristobalite.in" + }, + { + "fileName": "SiO2.vashishta", + "url": "silica/betacristobalite/SiO2.vashishta" + }, + { + "fileName": "betacristobalite.data", + "url": "silica/betacristobalite/betacristobalite.data" + } + ] + }, + { + "id": "zeolite_zsm5", + "title": "Zeolite ZSM-5", + "description": "Zeolite ZSM-5 using the vashishta potential.", + "imageUrl": "silica/zeolite_zsm5/zeolite_zsm5.png", + "inputScript": "zeolite_zsm5.in", + "keywords": ["vashishta", "zeolite"], + "files": [ + { + "fileName": "zeolite_zsm5.in", + "url": "silica/zeolite_zsm5/zeolite_zsm5.in" + }, + { + "fileName": "zeolite_zsm5.data", + "url": "silica/zeolite_zsm5/zeolite_zsm5.data" + }, + { + "fileName": "SiO2.vashishta", + "url": "silica/zeolite_zsm5/SiO2.vashishta" + } + ] + }, + { + "id": "crack", + "title": "Crack 2D", + "description": "Crack propagation in a 2d solid.", + "imageUrl": "lammps/crack/crack.png", + "inputScript": "crack.in", + "keywords": ["lennard jones", "crack", "2d"], + "files": [ + { + "fileName": "crack.in", + "url": "lammps/crack/crack.in" + } + ] + }, + { + "id": "flow_couette", + "title": "Couette flow 2D", + "description": "Couette flow in a 2d channel.", + "imageUrl": "lammps/flow_couette/flow_couette.png", + "inputScript": "flow_couette.in", + "keywords": ["lennard jones", "flow", "2d"], + "files": [ + { + "fileName": "flow_couette.in", + "url": "lammps/flow_couette/flow_couette.in" + } + ] + }, + { + "id": "flow_poiseuille", + "title": "Poiseuille flow 2D", + "description": "Poiseuille flow in a 2d channel.", + "imageUrl": "lammps/flow_pois/flow_pois.png", + "inputScript": "flow_pois.in", + "keywords": ["lennard jones", "flow", "2d"], + "files": [ + { + "fileName": "flow_pois.in", + "url": "lammps/flow_pois/flow_pois.in" + } + ] + }, + { + "id": "friction", + "title": "Friction 2D", + "description": "Frictional contact of spherical asperities between 2d surfaces.", + "imageUrl": "lammps/friction/friction.png", + "inputScript": "friction.in", + "keywords": ["lennard jones", "friction", "2d"], + "files": [ + { + "fileName": "friction.in", + "url": "lammps/friction/friction.in" + } + ] + }, + { + "id": "cmap", + "title": "CMAP 5-body", + "description": "CMAP 5-body contributions to CHARMM force field.", + "imageUrl": "lammps/cmap/cmap.png", + "inputScript": "in.cmap", + "keywords": ["charmm"], + "files": [ + { + "fileName": "gagg.data", + "url": "lammps/cmap/gagg.data" + }, + { + "fileName": "in.cmap", + "url": "lammps/cmap/in.cmap" + }, + { + "fileName": "charmm22.cmap", + "url": "lammps/cmap/charmm22.cmap" + } + ] + }, + { + "id": "deposit_atom", + "title": "Deposit atoms", + "description": "Deposition of atoms onto a 3d substrate.", + "imageUrl": "lammps/deposit_atom/deposit.png", + "inputScript": "in.deposit.atom", + "keywords": ["lennard jones", "deposit"], + "files": [ + { + "fileName": "in.deposit.atom", + "url": "lammps/deposit_atom/in.deposit.atom" + } + ] + }, + { + "id": "deposit_molecule", + "title": "Deposit molecules", + "description": "Deposition of molecules onto a 3d substrate.", + "imageUrl": "lammps/deposit_molecule/deposit_molecule.png", + "inputScript": "in.deposit.molecule", + "keywords": ["lennard jones", "deposit", "3d"], + "files": [ + { + "fileName": "in.deposit.molecule", + "url": "lammps/deposit_molecule/in.deposit.molecule" + }, + { + "fileName": "molecule.dimer", + "url": "lammps/deposit_molecule/molecule.dimer" + } + ] + }, + { + "id": "micelle", + "title": "Micelle", + "description": "Self-assembly of small lipid-like molecules into 2d bilayers.", + "imageUrl": "lammps/micelle/micelle.png", + "inputScript": "in.micelle", + "keywords": ["micelle"], + "files": [ + { + "fileName": "data.micelle", + "url": "lammps/micelle/data.micelle" + }, + { + "fileName": "def.micelle", + "url": "lammps/micelle/def.micelle" + }, + { + "fileName": "in.micelle", + "url": "lammps/micelle/in.micelle" + } + ] + }, + { + "id": "obstacle", + "title": "2d obstacle", + "description": "Flow around two voids in a 2d channel.", + "imageUrl": "lammps/obstacle/obstacle.png", + "inputScript": "in.obstacle", + "keywords": ["lennard jones", "flow", "2d"], + "files": [ + { + "fileName": "in.obstacle", + "url": "lammps/obstacle/in.obstacle" + } + ] + }, + { + "id": "peptide", + "title": "Peptide solvation", + "description": "Dynamics of a small solvated peptide chain (5-mer).", + "imageUrl": "lammps/peptide/peptide.png", + "inputScript": "in.peptide", + "keywords": ["peptide"], + "files": [ + { + "fileName": "in.peptide", + "url": "lammps/peptide/in.peptide" + }, + { + "fileName": "data.peptide", + "url": "lammps/peptide/data.peptide" + } + ] + }, + { + "id": "pour_2d", + "title": "2d pour.", + "description": "Pouring of granular molecules into a 2d box, then chute flow.", + "imageUrl": "lammps/pour_2d/pour_2d.png", + "inputScript": "in.pour.2d", + "keywords": ["granular", "pour", "2d"], + "files": [ + { + "fileName": "in.pour.2d", + "url": "lammps/pour_2d/in.pour.2d" + } + ] + }, + { + "id": "pour_2d_molecule", + "title": "2d molecule pour", + "description": "Pouring of granular molecules into a 2d box, then chute flow.", + "imageUrl": "lammps/pour_2d_molecule/pour_2d_molecule.png", + "inputScript": "in.pour.2d.molecule", + "keywords": ["granular", "pour", "2d"], + "files": [ + { + "fileName": "in.pour.2d.molecule", + "url": "lammps/pour_2d_molecule/in.pour.2d.molecule" + }, + { + "fileName": "molecule.vshape", + "url": "lammps/pour_2d_molecule/molecule.vshape" + } + ] + }, + { + "id": "pour_3d", + "title": "3d pour", + "description": "Pouring of granular particles into a 3d box, then chute flow.", + "imageUrl": "lammps/pour_3d/pour.png", + "inputScript": "in.pour", + "keywords": ["granular", "pour"], + "files": [ + { + "fileName": "in.pour", + "url": "lammps/pour_3d/in.pour" + } + ] + }, + { + "id": "shear", + "title": "Shear", + "description": "sideways shear applied to 2d solid.", + "imageUrl": "lammps/shear/shear.png", + "inputScript": "in.shear", + "keywords": ["lennard jones", "shear", "2d"], + "files": [ + { + "fileName": "Ni_u3.eam", + "url": "lammps/shear/Ni_u3.eam" + }, + { + "fileName": "in.shear", + "url": "lammps/shear/in.shear" + } + ] + }, + { + "id": "shear_void", + "title": "Shear with void", + "description": "Sideways shear applied to 2d solid with a void.", + "imageUrl": "lammps/shear_void/shear_void.png", + "inputScript": "in.shear.void", + "keywords": ["lennard jones", "shear", "2d"], + "files": [ + { + "fileName": "in.shear.void", + "url": "lammps/shear_void/in.shear.void" + }, + { + "fileName": "Ni_u3.eam", + "url": "lammps/shear_void/Ni_u3.eam" + } + ] + }, + { + "id": "abstract_translocation", + "title": "Polymer through hole", + "description": "This example contains a (crude and somewhat simple) example of the translocation of a (rather short) polymer through a hole in a wall, surrounded by an explicit LJ solvent.", + "imageUrl": "moltemplate/abstract_translocation/abstract_translocation.png", + "inputScript": "run.in.npt", + "keywords": ["polymer"], + "files": [ + { + "fileName": "run.in.npt", + "url": "moltemplate/abstract_translocation/run.in.npt" + }, + { + "fileName": "system.in", + "url": "moltemplate/abstract_translocation/system.in" + }, + { + "fileName": "system.in.init", + "url": "moltemplate/abstract_translocation/system.in.init" + }, + { + "fileName": "system.in.settings", + "url": "moltemplate/abstract_translocation/system.in.settings" + }, + { + "fileName": "run.in.nvt", + "url": "moltemplate/abstract_translocation/run.in.nvt" + }, + { + "fileName": "system.data", + "url": "moltemplate/abstract_translocation/system.data" + } + ] + }, + { + "id": "alkane_chain_single", + "title": "Alkane chain", + "description": "This example is a simple simulation of a long alkane chain, in a vacuum at room temperature using the OPLSAA force field.", + "imageUrl": "moltemplate/alkane_chain_single/alkane_chain_2.png", + "inputScript": "run.in.nvt", + "keywords": ["alkane"], + "files": [ + { + "fileName": "system_after_min.data", + "url": "moltemplate/alkane_chain_single/system_after_min.data" + }, + { + "fileName": "system.in", + "url": "moltemplate/alkane_chain_single/system.in" + }, + { + "fileName": "system.in.init", + "url": "moltemplate/alkane_chain_single/system.in.init" + }, + { + "fileName": "run.in.min", + "url": "moltemplate/alkane_chain_single/run.in.min" + }, + { + "fileName": "system.in.settings", + "url": "moltemplate/alkane_chain_single/system.in.settings" + }, + { + "fileName": "run.in.nvt", + "url": "moltemplate/alkane_chain_single/run.in.nvt" + }, + { + "fileName": "system.data", + "url": "moltemplate/alkane_chain_single/system.data" + }, + { + "fileName": "system.in.charges", + "url": "moltemplate/alkane_chain_single/system.in.charges" + } + ] + }, + { + "id": "benzene", + "title": "Benzene", + "description": "This example shows how to build a box of benzene molecules using the AMBER/GAFF force-field.", + "imageUrl": "moltemplate/benzene/benzene.png", + "inputScript": "run.in.npt", + "keywords": ["amber", "benzene"], + "files": [ + { + "fileName": "run.in.npt", + "url": "moltemplate/benzene/run.in.npt" + }, + { + "fileName": "system.in", + "url": "moltemplate/benzene/system.in" + }, + { + "fileName": "system.in.init", + "url": "moltemplate/benzene/system.in.init" + }, + { + "fileName": "system.in.settings", + "url": "moltemplate/benzene/system.in.settings" + }, + { + "fileName": "run.in.nvt", + "url": "moltemplate/benzene/run.in.nvt" + }, + { + "fileName": "system.data", + "url": "moltemplate/benzene/system.data" + } + ] + }, + { + "id": "ice_crystal", + "title": "Ice crystal", + "description": "A simulation of an ice crystal using SPCE water and the shake algorithm.", + "imageUrl": "moltemplate/ice_crystal/ice_crystal.png", + "inputScript": "run.in.npt", + "keywords": ["spce", "water", "ice", "shake"], + "files": [ + { + "fileName": "run.in.npt", + "url": "moltemplate/ice_crystal/run.in.npt" + }, + { + "fileName": "system.in", + "url": "moltemplate/ice_crystal/system.in" + }, + { + "fileName": "system_after_npt.data", + "url": "moltemplate/ice_crystal/system_after_npt.data" + }, + { + "fileName": "system.in.init", + "url": "moltemplate/ice_crystal/system.in.init" + }, + { + "fileName": "system.in.settings", + "url": "moltemplate/ice_crystal/system.in.settings" + }, + { + "fileName": "run.in.nvt", + "url": "moltemplate/ice_crystal/run.in.nvt" + }, + { + "fileName": "system.data", + "url": "moltemplate/ice_crystal/system.data" + } + ] + }, + { + "id": "methane", + "title": "Methane", + "description": "This example demonstrates how to build a simulation containing a box of methane.", + "imageUrl": "moltemplate/methane/methane.png", + "inputScript": "run.in.npt", + "keywords": ["methane"], + "files": [ + { + "fileName": "run.in.npt", + "url": "moltemplate/methane/run.in.npt" + }, + { + "fileName": "system_after_min.data", + "url": "moltemplate/methane/system_after_min.data" + }, + { + "fileName": "system.in", + "url": "moltemplate/methane/system.in" + }, + { + "fileName": "system.in.init", + "url": "moltemplate/methane/system.in.init" + }, + { + "fileName": "system.in.settings", + "url": "moltemplate/methane/system.in.settings" + }, + { + "fileName": "run.in.nvt", + "url": "moltemplate/methane/run.in.nvt" + }, + { + "fileName": "system.data", + "url": "moltemplate/methane/system.data" + }, + { + "fileName": "system.in.charges", + "url": "moltemplate/methane/system.in.charges" + } + ] + }, + { + "id": "nanotube_water", + "title": "Water in nanotube", + "description": "This is a small version of a carbon-nanotube, water capillary system.", + "imageUrl": "moltemplate/nanotube+water/nanotube+water.png", + "inputScript": "run.in.npt", + "keywords": ["water", "cnt", "carbon nanotube"], + "files": [ + { + "fileName": "run.in.npt", + "url": "moltemplate/nanotube+water/run.in.npt" + }, + { + "fileName": "system.in", + "url": "moltemplate/nanotube+water/system.in" + }, + { + "fileName": "system.in.init", + "url": "moltemplate/nanotube+water/system.in.init" + }, + { + "fileName": "system.in.settings", + "url": "moltemplate/nanotube+water/system.in.settings" + }, + { + "fileName": "system.data", + "url": "moltemplate/nanotube+water/system.data" + } + ] + }, + { + "id": "water_methane", + "title": "Methane in water", + "description": "This example contains a mixture of SPCE water and methane. The methane molecules use OPLSAA force-field.", + "imageUrl": "moltemplate/waterSPCE+methane/waterSPCE+methane.png", + "inputScript": "run.in.npt", + "keywords": ["spce", "water", "methane"], + "files": [ + { + "fileName": "run.in.npt", + "url": "moltemplate/waterSPCE+methane/run.in.npt" + }, + { + "fileName": "system_after_min.data", + "url": "moltemplate/waterSPCE+methane/system_after_min.data" + }, + { + "fileName": "system.in", + "url": "moltemplate/waterSPCE+methane/system.in" + }, + { + "fileName": "system.in.init", + "url": "moltemplate/waterSPCE+methane/system.in.init" + }, + { + "fileName": "system.in.settings", + "url": "moltemplate/waterSPCE+methane/system.in.settings" + }, + { + "fileName": "run.in.nvt", + "url": "moltemplate/waterSPCE+methane/run.in.nvt" + }, + { + "fileName": "system.data", + "url": "moltemplate/waterSPCE+methane/system.data" + }, + { + "fileName": "system.in.charges", + "url": "moltemplate/waterSPCE+methane/system.in.charges" + } + ] + }, + { + "id": "water_nacl", + "title": "Salt water", + "description": "This example contains a mixture of SPCE water and salt (NaCl).", + "imageUrl": "moltemplate/waterSPCE+Na+Cl/waterSPCE+NA+CL.png", + "inputScript": "run.in.npt", + "keywords": ["spce", "water", "salt"], + "files": [ + { + "fileName": "run.in.npt", + "url": "moltemplate/waterSPCE+Na+Cl/run.in.npt" + }, + { + "fileName": "system.in", + "url": "moltemplate/waterSPCE+Na+Cl/system.in" + }, + { + "fileName": "system.in.init", + "url": "moltemplate/waterSPCE+Na+Cl/system.in.init" + }, + { + "fileName": "system.in.settings", + "url": "moltemplate/waterSPCE+Na+Cl/system.in.settings" + }, + { + "fileName": "run.in.nvt", + "url": "moltemplate/waterSPCE+Na+Cl/run.in.nvt" + }, + { + "fileName": "system.data", + "url": "moltemplate/waterSPCE+Na+Cl/system.data" + } + ] + }, + { + "id": "water_isobutane", + "title": "TIP3P water and isobutane", + "description": "The simulation consists of a mixture of isobutane and water. Over time (less than 1 ns), the two molecules phase-separate.", + "imageUrl": "moltemplate/waterTIP3P+isobutane/waterTIP3P+isobutane.png", + "inputScript": "run.in.npt", + "keywords": ["tip3p", "water", "isobutane"], + "files": [ + { + "fileName": "run.in.npt", + "url": "moltemplate/waterTIP3P+isobutane/run.in.npt" + }, + { + "fileName": "system.in", + "url": "moltemplate/waterTIP3P+isobutane/system.in" + }, + { + "fileName": "system.in.init", + "url": "moltemplate/waterTIP3P+isobutane/system.in.init" + }, + { + "fileName": "system.in.settings", + "url": "moltemplate/waterTIP3P+isobutane/system.in.settings" + }, + { + "fileName": "run.in.nvt", + "url": "moltemplate/waterTIP3P+isobutane/run.in.nvt" + }, + { + "fileName": "system.data", + "url": "moltemplate/waterTIP3P+isobutane/system.data" + } + ] + }, + { + "id": "reax_ab", + "title": "Ammonia-Borine (AB)", + "description": "Combustion of Ammonia-Borine (AB) can be simulated with ReaxFF. Two AB molecules meet and a H2 molecule is formed.", + "imageUrl": "reaxff/AB/AB.png", + "inputScript": "AB.in", + "keywords": ["reaxff", "combustion", "ammonia borine"], + "files": [ + { + "fileName": "lmp_control", + "url": "reaxff/AB/lmp_control" + }, + { + "fileName": "ffield.reax.AB", + "url": "reaxff/AB/ffield.reax.AB" + }, + { + "fileName": "AB.in", + "url": "reaxff/AB/AB.in" + }, + { + "fileName": "data.AB", + "url": "reaxff/AB/data.AB" + }, + { + "fileName": "param.qeq", + "url": "reaxff/AB/param.qeq" + } + ] + }, + { + "id": "reax_feoh3", + "title": "Iron(III) hydroxide", + "description": "In this simulation, we see low density Iron(III) hydroxide (Fe(OH)3) at high temperature.", + "imageUrl": "reaxff/FeOH3/FeOH3.png", + "inputScript": "FeOH3.in", + "keywords": ["reaxff", "iron hydroxide"], + "files": [ + { + "fileName": "data.FeOH3", + "url": "reaxff/FeOH3/data.FeOH3" + }, + { + "fileName": "lmp_control", + "url": "reaxff/FeOH3/lmp_control" + }, + { + "fileName": "ffield.reax.Fe_O_C_H", + "url": "reaxff/FeOH3/ffield.reax.Fe_O_C_H" + }, + { + "fileName": "FeOH3.in", + "url": "reaxff/FeOH3/FeOH3.in" + }, + { + "fileName": "param.qeq", + "url": "reaxff/FeOH3/param.qeq" + } + ] + }, + { + "id": "reax_rdf", + "title": "RDX explosive", + "description": "RDX is an organic compound normally used as an explosive, especially in world war II.", + "imageUrl": "reaxff/RDX/RDX.png", + "inputScript": "RDX.in", + "keywords": ["reaxff", "explosive"], + "files": [ + { + "fileName": "lmp_control", + "url": "reaxff/RDX/lmp_control" + }, + { + "fileName": "RDX.in", + "url": "reaxff/RDX/RDX.in" + }, + { + "fileName": "data.RDX", + "url": "reaxff/RDX/data.RDX" + }, + { + "fileName": "ffield.reax.rdx", + "url": "reaxff/RDX/ffield.reax.rdx" + }, + { + "fileName": "param.qeq", + "url": "reaxff/RDX/param.qeq" + } + ] + }, + { + "id": "reax_ssz13", + "title": "Zeolite SSZ-13", + "description": "This structure is a zeolite called SSZ-13 (or CHA) and has one of the silicon sites replaced with Aluminum and a hydrogen atom.", + "imageUrl": "reaxff/ssz_13/ssz_13.png", + "inputScript": "ssz_13.in", + "keywords": ["reaxff", "zeolite"], + "files": [ + { + "fileName": "ffield_Psofogiannakis_CuOH_2015", + "url": "reaxff/ssz_13/ffield_Psofogiannakis_CuOH_2015" + }, + { + "fileName": "ssz_13.in", + "url": "reaxff/ssz_13/ssz_13.in" + }, + { + "fileName": "CHA_wAl.data", + "url": "reaxff/ssz_13/CHA_wAl.data" + } + ] + }, + { + "id": "reax_voh", + "title": "Vanadium Oxide", + "description": "In this simulation, we see vanadium oxide being simulated using ReaxFF at high temperature.", + "imageUrl": "reaxff/VOH/VOH.png", + "inputScript": "VOH.in", + "keywords": ["reaxff", "vanadium oxide"], + "files": [ + { + "fileName": "lmp_control", + "url": "reaxff/VOH/lmp_control" + }, + { + "fileName": "ffield.reax.V_O_C_H", + "url": "reaxff/VOH/ffield.reax.V_O_C_H" + }, + { + "fileName": "data.VOH", + "url": "reaxff/VOH/data.VOH" + }, + { + "fileName": "VOH.in", + "url": "reaxff/VOH/VOH.in" + }, + { + "fileName": "param.qeq", + "url": "reaxff/VOH/param.qeq" + } + ] + }, + { + "id": "reax_znoh2", + "title": "Zn(OH)2", + "description": "In this simulation, we see Zn(OH)2 at high temperature.", + "imageUrl": "reaxff/ZnOH2/ZnOH2.png", + "inputScript": "ZnOH2.in", + "keywords": ["reaxff"], + "files": [ + { + "fileName": "ZnOH2.in", + "url": "reaxff/ZnOH2/ZnOH2.in" + }, + { + "fileName": "lmp_control", + "url": "reaxff/ZnOH2/lmp_control" + }, + { + "fileName": "data.ZnOH2", + "url": "reaxff/ZnOH2/data.ZnOH2" + }, + { + "fileName": "ffield.reax.ZnOH", + "url": "reaxff/ZnOH2/ffield.reax.ZnOH" + }, + { + "fileName": "param.qeq", + "url": "reaxff/ZnOH2/param.qeq" } - ] -} \ No newline at end of file + ] + } + ] +} diff --git a/public/manifest.json b/public/manifest.json index 9a79fb97..6f776abe 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -12,4 +12,4 @@ "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff" -} \ No newline at end of file +} diff --git a/src/App.test.tsx b/src/App.test.tsx index bdb211c3..cecdb550 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -1,6 +1,6 @@ -import { describe, it } from "vitest"; import React from "react"; import { createRoot } from "react-dom/client"; +import { describe, it } from "vitest"; import App from "./App"; describe("App", () => { diff --git a/src/App.tsx b/src/App.tsx index b0d017f3..7c224edc 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,34 +1,36 @@ import { + AlignLeftOutlined, BorderOuterOutlined, - LineChartOutlined, - EditOutlined, - InsertRowAboveOutlined, - FileOutlined, - PlaySquareOutlined, BorderOutlined, - AlignLeftOutlined, - PlusSquareOutlined, CaretRightOutlined, CloudOutlined, - PauseOutlined, - ShareAltOutlined, + EditOutlined, + FileOutlined, + InsertRowAboveOutlined, + LineChartOutlined, MenuFoldOutlined, MenuUnfoldOutlined, + PauseOutlined, + PlaySquareOutlined, + PlusSquareOutlined, SettingOutlined, + ShareAltOutlined, } from "@ant-design/icons"; -import { useMeasure } from "react-use"; -import React, { useState, useEffect, useCallback } from "react"; import type { MenuProps } from "antd"; -import { Layout, Menu, Badge, ConfigProvider, theme } from "antd"; +import { Badge, ConfigProvider, Layout, Menu, theme } from "antd"; +import type React from "react"; +import { useCallback, useEffect, useState } from "react"; +import { useMeasure } from "react-use"; +import AutoStartSimulation from "./components/AutoStartSimulation"; import Simulation from "./components/Simulation"; import Main from "./containers/Main"; -import { useStoreActions, useStoreState } from "./hooks"; -import { track } from "./utils/metrics"; import NewSimulation from "./containers/NewSimulation"; -import ShareSimulation from "./containers/ShareSimulation"; import Settings from "./containers/Settings"; -import AutoStartSimulation from "./components/AutoStartSimulation"; +import ShareSimulation from "./containers/ShareSimulation"; +import { useStoreActions, useStoreState } from "./hooks"; import { useEmbeddedMode } from "./hooks/useEmbeddedMode"; +import { track } from "./utils/metrics"; + const { Sider } = Layout; type MenuItem = Required["items"][number]; @@ -36,16 +38,16 @@ type MenuItem = Required["items"][number]; const darkThemeConfig = { algorithm: theme.darkAlgorithm, token: { - colorPrimary: '#1890ff', + colorPrimary: "#1890ff", borderRadius: 8, - colorBgBase: '#29282d', - colorBgContainer: '#2a292f', + colorBgBase: "#29282d", + colorBgContainer: "#2a292f", fontSizeHeading1: 48, fontSizeHeading2: 36, }, components: { Menu: { - colorBgContainer: 'transparent', + colorBgContainer: "transparent", itemBorderRadius: 8, itemMarginInline: 8, itemActiveBorderWidth: 0, @@ -53,17 +55,17 @@ const darkThemeConfig = { subMenuItemBorderRadius: 8, }, Layout: { - siderBg: 'transparent', - triggerBg: '#1f1f1f', + siderBg: "transparent", + triggerBg: "#1f1f1f", }, Select: { - selectorBg: '#2a292f', - optionSelectedBg: '#2a2a2a', - colorText: '#ffffff', - colorTextPlaceholder: '#888', - colorBorder: '#3a3a3a', - } - } + selectorBg: "#2a292f", + optionSelectedBg: "#2a2a2a", + colorText: "#ffffff", + colorTextPlaceholder: "#888", + colorBorder: "#3a3a3a", + }, + }, }; function getItem( @@ -72,7 +74,7 @@ function getItem( icon?: React.ReactNode, children?: MenuItem[], onClick?: () => void, - disabled?: boolean, + disabled?: boolean ): MenuItem { return { key, @@ -93,16 +95,10 @@ const App: React.FC = () => { const running = useStoreState((state) => state.simulation.running); const simulation = useStoreState((state) => state.simulation.simulation); const selectedFile = useStoreState((state) => state.app.selectedFile); - const setSelectedFile = useStoreActions( - (actions) => actions.app.setSelectedFile, - ); - const setSelectedMenu = useStoreActions( - (actions) => actions.app.setSelectedMenu, - ); + const setSelectedFile = useStoreActions((actions) => actions.app.setSelectedFile); + const setSelectedMenu = useStoreActions((actions) => actions.app.setSelectedMenu); const preferredView = useStoreState((state) => state.app.preferredView); - const setPreferredView = useStoreActions( - (actions) => actions.app.setPreferredView, - ); + const setPreferredView = useStoreActions((actions) => actions.app.setPreferredView); const paused = useStoreState((state) => state.simulation.paused); const setPaused = useStoreActions((actions) => actions.simulation.setPaused); const selectedMenu = useStoreState((state) => state.app.selectedMenu); @@ -136,7 +132,7 @@ const App: React.FC = () => { setPreferredView("view"); } }, - simulation == null, + simulation == null ); const pauseButtonTitle = paused ? "Continue" : "Pause"; @@ -149,7 +145,7 @@ const App: React.FC = () => { setPaused(!paused); setPreferredView(selectedMenu); // This is another hack. Should really rethink menu system. }, - running === false, + running === false ); const newSimulationButton = getItem( @@ -161,12 +157,12 @@ const App: React.FC = () => { setShowNewSimulation(true); setPreferredView(selectedMenu); // This is another hack. Should really rethink menu system. }, - running, + running ); const shareSimulationButton = getItem( - Share simulation + Share simulation , "share", , @@ -175,20 +171,14 @@ const App: React.FC = () => { setShowShareSimulation(true); setPreferredView(selectedMenu); // This is another hack. Should really rethink menu system. }, - simulation == null || running, + simulation == null || running ); - const settingsButton = getItem( - "Settings", - "settings", - , - undefined, - () => { - track("Settings.Open"); - setShowSettings(true); - setPreferredView(selectedMenu); // This is another hack. Should really rethink menu system. - }, - ); + const settingsButton = getItem("Settings", "settings", , undefined, () => { + track("Settings.Open"); + setShowSettings(true); + setPreferredView(selectedMenu); // This is another hack. Should really rethink menu system. + }); const items: MenuItem[] = [ getItem("View", "view", ), @@ -200,15 +190,11 @@ const App: React.FC = () => { , simulation ? simulation.files.map((file) => { - return getItem( - file.fileName, - "file" + file.fileName, - , - ); + return getItem(file.fileName, "file" + file.fileName, ); }) : [], undefined, - selectedFile == null, + selectedFile == null ), { type: "divider" }, newSimulationButton, @@ -222,7 +208,7 @@ const App: React.FC = () => { , undefined, undefined, - simulation == null, + simulation == null ), pauseButton, { type: "divider" }, @@ -255,7 +241,12 @@ const App: React.FC = () => { paused, }); - if (selected === "run" || selected === "settings" || selected === "newsimulation" || selected === "share") { + if ( + selected === "run" || + selected === "settings" || + selected === "newsimulation" || + selected === "share" + ) { return; } @@ -264,16 +255,14 @@ const App: React.FC = () => { // Oh god this is ugly const fileName = selected.substring(4); const files = simulation?.files; - const selectedFile = files?.filter( - (file) => file.fileName === fileName, - )[0]; + const selectedFile = files?.filter((file) => file.fileName === fileName)[0]; if (selectedFile) { setSelectedFile(selectedFile); } } }, - [simulation, setSelectedFile, running, paused, setSelectedMenu], + [simulation, setSelectedFile, running, paused, setSelectedMenu] ); return ( @@ -288,10 +277,7 @@ const App: React.FC = () => { onCollapse={(value) => setCollapsed(value)} trigger={null} > -
setCollapsed(!collapsed)} - > +
setCollapsed(!collapsed)}> {collapsed ? : }
{ mode="inline" items={items} onSelect={(info) => onMenuSelect(info.key)} - style={{ background: 'transparent' }} + style={{ background: "transparent" }} /> )} @@ -312,9 +298,7 @@ const App: React.FC = () => {
- {showNewSimulation && ( - setShowNewSimulation(false)} /> - )} + {showNewSimulation && setShowNewSimulation(false)} />} {showShareSimulation && simulation && ( { simulation={simulation} /> )} - {showSettings && ( - setShowSettings(false)} - /> - )} + {showSettings && setShowSettings(false)} />} ); }; diff --git a/src/components/AutoStartSimulation.tsx b/src/components/AutoStartSimulation.tsx index 0b92191b..089e79b2 100644 --- a/src/components/AutoStartSimulation.tsx +++ b/src/components/AutoStartSimulation.tsx @@ -1,9 +1,9 @@ +import { notification } from "antd"; +import type React from "react"; import { useEffect, useRef } from "react"; import { useStoreActions, useStoreState } from "../hooks"; import { useEmbeddedMode } from "../hooks/useEmbeddedMode"; -import { Simulation } from "../store/simulation"; -import { notification } from "antd"; -import React from "react"; +import type { Simulation } from "../store/simulation"; import { decodeSimulation } from "../utils/embed/codec"; interface Example { @@ -30,16 +30,13 @@ interface ExamplesData { const AutoStartSimulation: React.FC = () => { const hasInitiatedStart = useRef(false); - const setNewSimulation = useStoreActions( - (actions) => actions.simulation.newSimulation - ); - const setPreferredView = useStoreActions( - (actions) => actions.app.setPreferredView - ); + const setNewSimulation = useStoreActions((actions) => actions.simulation.newSimulation); + const setPreferredView = useStoreActions((actions) => actions.app.setPreferredView); const running = useStoreState((state) => state.simulation.running); const simulation = useStoreState((state) => state.simulation.simulation); - const { embeddedSimulationUrl, simulationIndex, embeddedData, autoStart, isEmbeddedMode, vars } = useEmbeddedMode(); - + const { embeddedSimulationUrl, simulationIndex, embeddedData, autoStart, isEmbeddedMode, vars } = + useEmbeddedMode(); + useEffect(() => { const fetchAndStartSimulation = async () => { try { @@ -47,11 +44,11 @@ const AutoStartSimulation: React.FC = () => { if (embeddedData) { const decodedSimulation = decodeSimulation(embeddedData, autoStart); decodedSimulation.vars = vars; // Add URL vars - + if (simulation?.id !== decodedSimulation.id) { hasInitiatedStart.current = true; setNewSimulation(decodedSimulation); - + // Set view based on autoStart if (autoStart) { setPreferredView("view"); // Visualizer showing atoms @@ -61,28 +58,29 @@ const AutoStartSimulation: React.FC = () => { } return; } - + // Handle URL-based embedding (only in embedded mode) if (!isEmbeddedMode) { return; } - + const response = await fetch(embeddedSimulationUrl!); const data: ExamplesData = await response.json(); - + // Override baseUrl for localhost development // When Atomify runs on localhost, derive baseUrl from embeddedSimulationUrl - const isLocalhost = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'; + const isLocalhost = + window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1"; if (isLocalhost && embeddedSimulationUrl) { const url = new URL(embeddedSimulationUrl); - const pathParts = url.pathname.split('/'); + const pathParts = url.pathname.split("/"); pathParts.pop(); // Remove simulations.json filename - data.baseUrl = url.origin + pathParts.join('/'); + data.baseUrl = url.origin + pathParts.join("/"); } - + if (simulationIndex >= 0 && simulationIndex < data.examples.length) { const selectedExample = data.examples[simulationIndex]; - + selectedExample.imageUrl = `${data.baseUrl}/${selectedExample.imageUrl}`; if (selectedExample.analysisScript) { selectedExample.analysisScript = `${data.baseUrl}/${selectedExample.analysisScript}`; @@ -98,7 +96,7 @@ const AutoStartSimulation: React.FC = () => { analysisDescription: selectedExample.analysisDescription, analysisScript: selectedExample.analysisScript, start: true, - vars: vars // Add URL vars + vars: vars, // Add URL vars }; if (simulation?.id !== newSimulation.id) { @@ -128,7 +126,18 @@ const AutoStartSimulation: React.FC = () => { }; checkWasmAndStart(); - }, [simulation?.id, running, setNewSimulation, setPreferredView, embeddedSimulationUrl, embeddedData, autoStart, isEmbeddedMode, simulationIndex, vars]); + }, [ + simulation?.id, + running, + setNewSimulation, + setPreferredView, + embeddedSimulationUrl, + embeddedData, + autoStart, + isEmbeddedMode, + simulationIndex, + vars, + ]); return null; }; diff --git a/src/components/ColorLegend.test.tsx b/src/components/ColorLegend.test.tsx index 46ae21c7..355eb46e 100644 --- a/src/components/ColorLegend.test.tsx +++ b/src/components/ColorLegend.test.tsx @@ -1,5 +1,5 @@ -import { describe, it, expect, beforeEach, vi } from "vitest"; import { render, screen } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import ColorLegend from "./ColorLegend"; describe("ColorLegend", () => { @@ -12,7 +12,9 @@ describe("ColorLegend", () => { fillRect: vi.fn(), }; - HTMLCanvasElement.prototype.getContext = vi.fn(() => mockContext) as unknown as typeof HTMLCanvasElement.prototype.getContext; + HTMLCanvasElement.prototype.getContext = vi.fn( + () => mockContext + ) as unknown as typeof HTMLCanvasElement.prototype.getContext; }); describe("rendering", () => { diff --git a/src/components/ColorLegend.tsx b/src/components/ColorLegend.tsx index 71c9bad0..a88d8955 100644 --- a/src/components/ColorLegend.tsx +++ b/src/components/ColorLegend.tsx @@ -1,6 +1,6 @@ -import { useEffect, useRef } from "react"; -import colormap from "colormap"; import { SettingOutlined } from "@ant-design/icons"; +import colormap from "colormap"; +import { useEffect, useRef } from "react"; interface ColorLegendProps { computeName: string; @@ -11,7 +11,14 @@ interface ColorLegendProps { onSettingsClick?: () => void; } -const ColorLegend = ({ computeName, minValue, maxValue, type, colormap: colormapName = "jet", onSettingsClick }: ColorLegendProps) => { +const ColorLegend = ({ + computeName, + minValue, + maxValue, + type, + colormap: colormapName = "jet", + onSettingsClick, +}: ColorLegendProps) => { const canvasRef = useRef(null); useEffect(() => { @@ -34,7 +41,7 @@ const ColorLegend = ({ computeName, minValue, maxValue, type, colormap: colormap // Create gradient const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0); - + colors.forEach((color, index) => { const position = index / (colors.length - 1); const r = Math.floor(color[0] * 255); diff --git a/src/components/Figure.test.tsx b/src/components/Figure.test.tsx index 45dbf65c..01ab8fd7 100644 --- a/src/components/Figure.test.tsx +++ b/src/components/Figure.test.tsx @@ -1,7 +1,14 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; -import { render, screen, act } from "@testing-library/react"; +import { act, render, screen } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { + type Compute, + type Fix, + type LMPModifier, + ModifierType, + type PlotData, + type Variable, +} from "../types"; import Figure from "./Figure"; -import { Compute, Fix, Variable, PlotData, LMPModifier, ModifierType } from "../types"; // Mock useStoreState hook vi.mock("../hooks", () => ({ @@ -21,9 +28,20 @@ vi.mock("dygraphs", () => { // Mock antd Modal and Empty components vi.mock("antd", () => ({ - Modal: ({ children, open, onCancel }: { children: React.ReactNode; open: boolean; onCancel: () => void }) => ( - open ?
{children}
: null - ), + Modal: ({ + children, + open, + onCancel, + }: { + children: React.ReactNode; + open: boolean; + onCancel: () => void; + }) => + open ? ( +
+ {children} +
+ ) : null, Empty: () =>
Empty
, })); @@ -32,7 +50,7 @@ describe("Figure", () => { let mockOnToggleSyncDataPoints: ( name: string, type: "compute" | "fix" | "variable", - value: boolean, + value: boolean ) => void; beforeEach(() => { @@ -73,7 +91,10 @@ describe("Figure", () => { hasScalarData: true, scalarValue: 1.0, data1D: { - data: [[0, 1], [1, 2]], + data: [ + [0, 1], + [1, 2], + ], labels: ["x", "y"], }, xLabel: "Time", @@ -95,7 +116,10 @@ describe("Figure", () => { hasScalarData: true, scalarValue: 1.0, data1D: { - data: [[0, 1], [1, 2]], + data: [ + [0, 1], + [1, 2], + ], labels: ["x", "y"], }, xLabel: "Time", @@ -117,7 +141,10 @@ describe("Figure", () => { hasScalarData: true, scalarValue: 1.0, data1D: { - data: [[0, 1], [1, 2]], + data: [ + [0, 1], + [1, 2], + ], labels: ["x", "y"], }, xLabel: "Time", @@ -134,7 +161,10 @@ describe("Figure", () => { const createMockPlotData = (overrides?: Partial): PlotData => ({ name: "test-plot", data1D: { - data: [[0, 1], [1, 2]], + data: [ + [0, 1], + [1, 2], + ], labels: ["x", "y"], }, xLabel: "Time", @@ -233,12 +263,7 @@ describe("Figure", () => { const plotData = createMockPlotData(); // Act - render( -
- ); + render(
); // Assert expect(mockOnToggleSyncDataPoints).not.toHaveBeenCalled(); @@ -249,13 +274,7 @@ describe("Figure", () => { const modifier = createMockCompute(); // Act - render( -
- ); + render(
); // Assert expect(mockOnToggleSyncDataPoints).not.toHaveBeenCalled(); @@ -325,4 +344,3 @@ describe("Figure", () => { }); }); }); - diff --git a/src/components/Figure.tsx b/src/components/Figure.tsx index 01e140f3..38a5c894 100644 --- a/src/components/Figure.tsx +++ b/src/components/Figure.tsx @@ -1,15 +1,15 @@ -import { Modal, Empty } from "antd"; -import { Compute, Fix, Variable, PlotData } from "../types"; -import { useEffect, useState, useId, useMemo, useRef } from "react"; -import { useStoreState } from "../hooks"; +import { Empty, Modal } from "antd"; import Dygraph from "dygraphs"; +import { useEffect, useId, useMemo, useRef, useState } from "react"; +import { useStoreState } from "../hooks"; +import type { Compute, Fix, PlotData, Variable } from "../types"; type FigureProps = { onClose: () => void; onToggleSyncDataPoints?: ( name: string, type: "compute" | "fix" | "variable", - value: boolean, + value: boolean ) => void; } & ( | { @@ -34,10 +34,7 @@ const Figure = ({ const [graph, setGraph] = useState(); const timesteps = useStoreState((state) => state.simulationStatus.timesteps); const graphId = useId(); - const width = - window.innerWidth < 1000 - ? window.innerWidth * 0.8 - : window.innerWidth * 0.6; + const width = window.innerWidth < 1000 ? window.innerWidth * 0.8 : window.innerWidth * 0.6; const height = (width * 3) / 4; // Extract plot configuration from either modifier or plotData @@ -64,24 +61,24 @@ const Figure = ({ // Only set syncDataPoints when a modifier is provided // Use ref to track previous modifier name to detect actual changes const prevModifierNameRef = useRef(undefined); - + useEffect(() => { if (modifier && modifierType && onToggleSyncDataPoints) { const modifierName = modifier.name; const prevModifierName = prevModifierNameRef.current; - + // Only update if modifier name actually changed (not just object reference) if (prevModifierName !== modifierName) { // Cleanup previous modifier if name changed if (prevModifierName !== undefined) { onToggleSyncDataPoints(prevModifierName, modifierType, false); } - + // Set up new modifier onToggleSyncDataPoints(modifierName, modifierType, true); prevModifierNameRef.current = modifierName; } - + return () => { // Cleanup on unmount if (prevModifierNameRef.current !== undefined) { @@ -106,19 +103,19 @@ const Figure = ({ height, legend: "always", // Dark theme styling - colors: ['#40a9ff', '#52c41a', '#f5222d', '#fa8c16', '#13c2c2', '#eb2f96', '#722ed1'], - legendFormatter: function(data) { + colors: ["#40a9ff", "#52c41a", "#f5222d", "#fa8c16", "#13c2c2", "#eb2f96", "#722ed1"], + legendFormatter: (data) => { if (data.x == null) { - return ''; + return ""; } let html = '
'; - data.series.forEach(function(series) { + data.series.forEach((series) => { if (!series.isVisible) return; const color = series.color; html += ''; - html += series.labelHTML + ': ' + series.yHTML + '
'; + html += series.labelHTML + ": " + series.yHTML + "
"; }); - html += '
'; + html += "
"; return html; }, }); diff --git a/src/components/LoadingSimulationScreen.tsx b/src/components/LoadingSimulationScreen.tsx index 0f1765bb..438b5a78 100644 --- a/src/components/LoadingSimulationScreen.tsx +++ b/src/components/LoadingSimulationScreen.tsx @@ -1,4 +1,4 @@ -import { Spin, Progress } from "antd"; +import { Progress, Spin } from "antd"; import styled from "styled-components"; interface LoadingSimulationScreenProps { @@ -38,11 +38,9 @@ const StatusSubtext = styled.div` margin-top: 8px; `; -const LoadingSimulationScreen = ({ - status, - wasmReady, -}: LoadingSimulationScreenProps) => { - const displayTitle = status?.title || (wasmReady ? "Loading simulation..." : "Initializing simulation engine..."); +const LoadingSimulationScreen = ({ status, wasmReady }: LoadingSimulationScreenProps) => { + const displayTitle = + status?.title || (wasmReady ? "Loading simulation..." : "Initializing simulation engine..."); const displayText = status?.text || ""; const progress = status?.progress ?? 0; diff --git a/src/components/ResponsiveSimulationSummary.test.tsx b/src/components/ResponsiveSimulationSummary.test.tsx index 64cd5285..bbdf7529 100644 --- a/src/components/ResponsiveSimulationSummary.test.tsx +++ b/src/components/ResponsiveSimulationSummary.test.tsx @@ -1,7 +1,7 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; import { render, screen } from "@testing-library/react"; -import ResponsiveSimulationSummary from "./ResponsiveSimulationSummary"; import type { ComponentProps } from "react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import ResponsiveSimulationSummary from "./ResponsiveSimulationSummary"; import type SimulationSummary from "./SimulationSummary"; import type SimulationSummaryExpanded from "./SimulationSummaryExpanded"; @@ -9,17 +9,25 @@ import type SimulationSummaryExpanded from "./SimulationSummaryExpanded"; // Note: Using string paths for vi.mock as it's more reliable with JSX in mock factories // Type safety is maintained through ComponentProps for prop types vi.mock("./SimulationSummary", () => ({ - default: ({ - isCollapsed, - onShowMore, - onExpand, - onCollapse + default: ({ + isCollapsed, + onShowMore, + onExpand, + onCollapse, }: ComponentProps) => (
{isCollapsed &&
Collapsed
} {onShowMore && } - {onExpand && } - {onCollapse && } + {onExpand && ( + + )} + {onCollapse && ( + + )}
), })); @@ -27,7 +35,11 @@ vi.mock("./SimulationSummary", () => ({ vi.mock("./SimulationSummaryExpanded", () => ({ default: ({ onShowLess }: ComponentProps) => (
- {onShowLess && } + {onShowLess && ( + + )}
), })); @@ -146,7 +158,11 @@ describe("ResponsiveSimulationSummary", () => { it("should pass isCollapsed prop to SimulationSummary", () => { // Arrange & Act render( - + ); // Assert diff --git a/src/components/ResponsiveSimulationSummary.tsx b/src/components/ResponsiveSimulationSummary.tsx index e87721a9..6b5a5ed4 100644 --- a/src/components/ResponsiveSimulationSummary.tsx +++ b/src/components/ResponsiveSimulationSummary.tsx @@ -43,11 +43,7 @@ const ResponsiveSimulationSummary = ({ // In non-embedded mode: show expanded overlay when showAnalyze is true (desktop only) if (showAnalyze && isDesktop) { - return ( - - ); + return ; } // Show regular overlay @@ -64,4 +60,3 @@ const ResponsiveSimulationSummary = ({ }; export default ResponsiveSimulationSummary; - diff --git a/src/components/SelectedAtomsInfo.tsx b/src/components/SelectedAtomsInfo.tsx index 537f58af..54455380 100644 --- a/src/components/SelectedAtomsInfo.tsx +++ b/src/components/SelectedAtomsInfo.tsx @@ -1,9 +1,9 @@ import { Button } from "antd"; -import { Particles } from "omovi"; -import { useMemo, useState, useEffect, useRef } from "react"; -import { Data1D, PlotData } from "../types"; -import Figure from "./Figure"; +import type { Particles } from "omovi"; +import { useEffect, useMemo, useRef, useState } from "react"; import { useStoreState } from "../hooks"; +import { type Data1D, PlotData } from "../types"; +import Figure from "./Figure"; interface SelectedAtomsInfoProps { selectedAtoms: Set; @@ -34,14 +34,14 @@ const calculateAngle = ( // Vectors from p2 to p1 and p3 const v1 = [p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2]]; const v2 = [p3[0] - p2[0], p3[1] - p2[1], p3[2] - p2[2]]; - + // Dot product const dot = v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; - + // Magnitudes const mag1 = Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]); const mag2 = Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2]); - + // Angle in radians, then convert to degrees const cosAngle = dot / (mag1 * mag2); return Math.acos(Math.max(-1, Math.min(1, cosAngle))) * (180 / Math.PI); @@ -87,9 +87,16 @@ interface MeasurementRowProps { onPlotClick: (key: string) => void; } -const MeasurementRow = ({ label, value, unit, plotKey, timeSeriesData, onPlotClick }: MeasurementRowProps) => { +const MeasurementRow = ({ + label, + value, + unit, + plotKey, + timeSeriesData, + onPlotClick, +}: MeasurementRowProps) => { const hasData = timeSeriesData[plotKey]?.data.length > 0; - + return (
{hasData ? ( @@ -121,7 +128,7 @@ const SelectedAtomsInfo = ({ const prevSelectedAtomsRef = useRef>(new Set()); const prevTimestepsRef = useRef(0); const prevRunningRef = useRef(false); - + // Get running state from store const running = useStoreState((state) => state.simulation.running); @@ -129,12 +136,12 @@ const SelectedAtomsInfo = ({ useEffect(() => { const prevRunning = prevRunningRef.current; const currentRunning = running; - + // When simulation starts (running transitions from false to true), clear data if (!prevRunning && currentRunning) { setTimeSeriesData({}); } - + prevRunningRef.current = currentRunning; }, [running]); @@ -142,13 +149,13 @@ const SelectedAtomsInfo = ({ useEffect(() => { const prevSelected = prevSelectedAtomsRef.current; const currentSelected = selectedAtoms; - + // Check if selection has changed (atoms added or removed) - const selectionChanged = + const selectionChanged = prevSelected.size !== currentSelected.size || - Array.from(prevSelected).some(id => !currentSelected.has(id)) || - Array.from(currentSelected).some(id => !prevSelected.has(id)); - + Array.from(prevSelected).some((id) => !currentSelected.has(id)) || + Array.from(currentSelected).some((id) => !prevSelected.has(id)); + if (selectionChanged) { setTimeSeriesData({}); prevSelectedAtomsRef.current = new Set(currentSelected); @@ -224,9 +231,21 @@ const SelectedAtomsInfo = ({ getCanonicalAngleKey(atomPositions[2].id, atomPositions[0].id, atomPositions[1].id), ]; const angles = [ - calculateAngle(atomPositions[1].position, atomPositions[0].position, atomPositions[2].position), - calculateAngle(atomPositions[0].position, atomPositions[1].position, atomPositions[2].position), - calculateAngle(atomPositions[0].position, atomPositions[2].position, atomPositions[1].position), + calculateAngle( + atomPositions[1].position, + atomPositions[0].position, + atomPositions[2].position + ), + calculateAngle( + atomPositions[0].position, + atomPositions[1].position, + atomPositions[2].position + ), + calculateAngle( + atomPositions[0].position, + atomPositions[2].position, + atomPositions[1].position + ), ]; angleKeys.forEach((key, idx) => { @@ -256,7 +275,7 @@ const SelectedAtomsInfo = ({ if (arrayIndex === undefined) continue; const position = particles.getPosition(arrayIndex); - + data.push({ atomId, position: [position.x, position.y, position.z], @@ -291,146 +310,189 @@ const SelectedAtomsInfo = ({ )} {/* Distance for 2 atoms */} - {atomData.length === 2 && (() => { - const distanceKey = getCanonicalDistanceKey(atomData[0].atomId, atomData[1].atomId); - const distance = calculateDistance(atomData[0].position, atomData[1].position); - - return ( -
-
- Geometry + {atomData.length === 2 && + (() => { + const distanceKey = getCanonicalDistanceKey(atomData[0].atomId, atomData[1].atomId); + const distance = calculateDistance(atomData[0].position, atomData[1].position); + + return ( +
+
+ Geometry +
+
- -
- ); - })()} + ); + })()} {/* Distances and angles for 3 atoms */} - {atomData.length === 3 && (() => { - const distanceMeasurements = [ - { - label: `d(${atomData[0].atomId}-${atomData[1].atomId})`, - value: calculateDistance(atomData[0].position, atomData[1].position).toFixed(3), - unit: "Å", - plotKey: getCanonicalDistanceKey(atomData[0].atomId, atomData[1].atomId), - }, - { - label: `d(${atomData[1].atomId}-${atomData[2].atomId})`, - value: calculateDistance(atomData[1].position, atomData[2].position).toFixed(3), - unit: "Å", - plotKey: getCanonicalDistanceKey(atomData[1].atomId, atomData[2].atomId), - }, - { - label: `d(${atomData[0].atomId}-${atomData[2].atomId})`, - value: calculateDistance(atomData[0].position, atomData[2].position).toFixed(3), - unit: "Å", - plotKey: getCanonicalDistanceKey(atomData[0].atomId, atomData[2].atomId), - }, - ]; - - const angleMeasurements = [ - { - label: `∠${atomData[1].atomId}-${atomData[0].atomId}-${atomData[2].atomId}`, - value: calculateAngle(atomData[1].position, atomData[0].position, atomData[2].position).toFixed(1), - unit: "°", - plotKey: getCanonicalAngleKey(atomData[0].atomId, atomData[1].atomId, atomData[2].atomId), - }, - { - label: `∠${atomData[0].atomId}-${atomData[1].atomId}-${atomData[2].atomId}`, - value: calculateAngle(atomData[0].position, atomData[1].position, atomData[2].position).toFixed(1), - unit: "°", - plotKey: getCanonicalAngleKey(atomData[1].atomId, atomData[0].atomId, atomData[2].atomId), - }, - { - label: `∠${atomData[0].atomId}-${atomData[2].atomId}-${atomData[1].atomId}`, - value: calculateAngle(atomData[0].position, atomData[2].position, atomData[1].position).toFixed(1), - unit: "°", - plotKey: getCanonicalAngleKey(atomData[2].atomId, atomData[0].atomId, atomData[1].atomId), - }, - ]; - - return ( -
-
- Geometry -
-
- {distanceMeasurements.map((measurement, idx) => ( - - ))} -
-
- {angleMeasurements.map((measurement, idx) => ( - - ))} + {atomData.length === 3 && + (() => { + const distanceMeasurements = [ + { + label: `d(${atomData[0].atomId}-${atomData[1].atomId})`, + value: calculateDistance(atomData[0].position, atomData[1].position).toFixed(3), + unit: "Å", + plotKey: getCanonicalDistanceKey(atomData[0].atomId, atomData[1].atomId), + }, + { + label: `d(${atomData[1].atomId}-${atomData[2].atomId})`, + value: calculateDistance(atomData[1].position, atomData[2].position).toFixed(3), + unit: "Å", + plotKey: getCanonicalDistanceKey(atomData[1].atomId, atomData[2].atomId), + }, + { + label: `d(${atomData[0].atomId}-${atomData[2].atomId})`, + value: calculateDistance(atomData[0].position, atomData[2].position).toFixed(3), + unit: "Å", + plotKey: getCanonicalDistanceKey(atomData[0].atomId, atomData[2].atomId), + }, + ]; + + const angleMeasurements = [ + { + label: `∠${atomData[1].atomId}-${atomData[0].atomId}-${atomData[2].atomId}`, + value: calculateAngle( + atomData[1].position, + atomData[0].position, + atomData[2].position + ).toFixed(1), + unit: "°", + plotKey: getCanonicalAngleKey( + atomData[0].atomId, + atomData[1].atomId, + atomData[2].atomId + ), + }, + { + label: `∠${atomData[0].atomId}-${atomData[1].atomId}-${atomData[2].atomId}`, + value: calculateAngle( + atomData[0].position, + atomData[1].position, + atomData[2].position + ).toFixed(1), + unit: "°", + plotKey: getCanonicalAngleKey( + atomData[1].atomId, + atomData[0].atomId, + atomData[2].atomId + ), + }, + { + label: `∠${atomData[0].atomId}-${atomData[2].atomId}-${atomData[1].atomId}`, + value: calculateAngle( + atomData[0].position, + atomData[2].position, + atomData[1].position + ).toFixed(1), + unit: "°", + plotKey: getCanonicalAngleKey( + atomData[2].atomId, + atomData[0].atomId, + atomData[1].atomId + ), + }, + ]; + + return ( +
+
+ Geometry +
+
+ {distanceMeasurements.map((measurement, idx) => ( + + ))} +
+
+ {angleMeasurements.map((measurement, idx) => ( + + ))} +
-
- ); - })()} + ); + })()} - - -
+ +
Hold shift to select more particles
{/* Plot modals */} - {visiblePlot && timeSeriesData[visiblePlot] && (() => { - const getPlotName = (key: string): string => { - if (key.startsWith("distance-")) { - const ids = key.replace("distance-", "").split("-"); - return `Distance ${ids[0]}-${ids[1]}`; - } else if (key.startsWith("angle-")) { - const ids = key.replace("angle-", "").split("-"); - return `Angle ${ids[0]}-${ids[1]}-${ids[2]}`; - } - return key; - }; - - return ( -
setVisiblePlot(null)} - /> - ); - })()} + {visiblePlot && + timeSeriesData[visiblePlot] && + (() => { + const getPlotName = (key: string): string => { + if (key.startsWith("distance-")) { + const ids = key.replace("distance-", "").split("-"); + return `Distance ${ids[0]}-${ids[1]}`; + } else if (key.startsWith("angle-")) { + const ids = key.replace("angle-", "").split("-"); + return `Angle ${ids[0]}-${ids[1]}-${ids[2]}`; + } + return key; + }; + + return ( +
setVisiblePlot(null)} + /> + ); + })()}
); }; export default SelectedAtomsInfo; - diff --git a/src/components/Simulation.tsx b/src/components/Simulation.tsx index b2d4c049..b5c44b1c 100644 --- a/src/components/Simulation.tsx +++ b/src/components/Simulation.tsx @@ -1,10 +1,10 @@ +import { notification } from "antd"; import { useCallback, useEffect, useRef } from "react"; import { useStoreActions, useStoreState } from "../hooks"; -import createModule from "../wasm/lammps.mjs"; import { LammpsWeb } from "../types"; -import { AtomifyWasmModule } from "../wasm/types"; -import { notification } from "antd"; import { time_event, track } from "../utils/metrics"; +import createModule from "../wasm/lammps.mjs"; +import { AtomifyWasmModule } from "../wasm/types"; const SimulationComponent = () => { const wasm = window.wasm; @@ -12,27 +12,19 @@ const SimulationComponent = () => { const simulation = useStoreState((state) => state.simulation.simulation); const paused = useStoreState((state) => state.simulation.paused); const setPaused = useStoreActions((actions) => actions.simulation.setPaused); - const simulationSettings = useStoreState( - (state) => state.settings.simulation, - ); - const setSimulationSettings = useStoreActions( - (actions) => actions.settings.setSimulation, - ); + const simulationSettings = useStoreState((state) => state.settings.simulation); + const setSimulationSettings = useStoreActions((actions) => actions.settings.setSimulation); const running = useStoreState((state) => state.simulation.running); - const addLammpsOutput = useStoreActions( - (actions) => actions.simulation.addLammpsOutput, - ); + const addLammpsOutput = useStoreActions((actions) => actions.simulation.addLammpsOutput); const selectedMenu = useStoreState((state) => state.app.selectedMenu); const setLammps = useStoreActions((actions) => actions.simulation.setLammps); const setStatus = useStoreActions((actions) => actions.app.setStatus); - const runPostTimestep = useStoreActions( - (actions) => actions.processing.runPostTimestep, - ); + const runPostTimestep = useStoreActions((actions) => actions.processing.runPostTimestep); const runPostTimestepRendering = useStoreActions( - (actions) => actions.processing.runPostTimestepRendering, + (actions) => actions.processing.runPostTimestepRendering ); const setHasSynchronized = useStoreActions( - (actions) => actions.simulationStatus.setHasSynchronized, + (actions) => actions.simulationStatus.setHasSynchronized ); const renderCycleCounter = useRef(0); @@ -45,7 +37,7 @@ const SimulationComponent = () => { addLammpsOutput(text); console.log(text); }, - [addLammpsOutput], + [addLammpsOutput] ); useEffect(() => { @@ -107,10 +99,10 @@ const SimulationComponent = () => { } if (lammps && wasm && simulation) { setHasSynchronized(true); - + // Always update 3D rendering (high frequency) runPostTimestepRendering(); - + // Update UI state only every Nth cycle (low frequency) renderCycleCounter.current += 1; const uiUpdateFrequency = simulationSettings.uiUpdateFrequency || 15; @@ -155,11 +147,11 @@ const SimulationComponent = () => { print: onPrint, printErr: onPrint, locateFile: (path: string) => { - if (path.endsWith('.wasm')) { + if (path.endsWith(".wasm")) { return import.meta.env.BASE_URL + path; } return path; - } + }, }).then((Module) => { track("WASM.Load"); setStatus({ diff --git a/src/components/SimulationSummary.tsx b/src/components/SimulationSummary.tsx index ddcf5c73..d9ad91b2 100644 --- a/src/components/SimulationSummary.tsx +++ b/src/components/SimulationSummary.tsx @@ -1,6 +1,6 @@ -import { useStoreState, useStoreActions } from "../hooks"; -import { Slider, Button } from "antd"; -import { MinusOutlined, ExpandOutlined } from "@ant-design/icons"; +import { ExpandOutlined, MinusOutlined } from "@ant-design/icons"; +import { Button, Slider } from "antd"; +import { useStoreActions, useStoreState } from "../hooks"; import { track } from "../utils/metrics"; interface SimulationSummaryProps { @@ -10,29 +10,21 @@ interface SimulationSummaryProps { onCollapse?: () => void; } -const SimulationSummary = ({ - onShowMore, - isCollapsed = false, +const SimulationSummary = ({ + onShowMore, + isCollapsed = false, onExpand, - onCollapse + onCollapse, }: SimulationSummaryProps) => { - const simulationSettings = useStoreState( - (state) => state.settings.simulation, - ); - const setSimulationSettings = useStoreActions( - (actions) => actions.settings.setSimulation, - ); + const simulationSettings = useStoreState((state) => state.settings.simulation); + const setSimulationSettings = useStoreActions((actions) => actions.settings.setSimulation); const simulation = useStoreState((state) => state.simulation.simulation); const runType = useStoreState((state) => state.simulationStatus.runType); const numAtoms = useStoreState((state) => state.simulationStatus.numAtoms); const numBonds = useStoreState((state) => state.simulationStatus.numBonds); const timesteps = useStoreState((state) => state.simulationStatus.timesteps); - const remainingTime = useStoreState( - (state) => state.simulationStatus.remainingTime, - ); - const timestepsPerSecond = useStoreState( - (state) => state.simulationStatus.timestepsPerSecond, - ); + const remainingTime = useStoreState((state) => state.simulationStatus.remainingTime); + const timestepsPerSecond = useStoreState((state) => state.simulationStatus.timestepsPerSecond); const setSyncFrequency = (value: number | null) => { if (value && value > 0) { @@ -67,9 +59,7 @@ const SimulationSummary = ({
{/* No buttons when collapsed - just clickable text */}
); @@ -79,13 +69,16 @@ const SimulationSummary = ({
{simulation && ( <> -
+
{onCollapse && ( {" "} - {" " + - (record.hasScalarData - ? record.scalarValue.toPrecision(5).toString() - : "")} + {" " + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} ); } else { @@ -142,9 +117,7 @@ const SimulationSummaryContent = () => { <> {value + " " + - (record.hasScalarData - ? record.scalarValue.toPrecision(5).toString() - : "")} + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} ); } @@ -171,10 +144,7 @@ const SimulationSummaryContent = () => { > {value} {" "} - {" " + - (record.hasScalarData - ? record.scalarValue.toPrecision(5).toString() - : "")} + {" " + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} ); } else { @@ -182,9 +152,7 @@ const SimulationSummaryContent = () => { <> {value + " " + - (record.hasScalarData - ? record.scalarValue.toPrecision(5).toString() - : "")} + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} ); } @@ -211,10 +179,7 @@ const SimulationSummaryContent = () => { > {value} {" "} - {" " + - (record.hasScalarData - ? record.scalarValue.toPrecision(5).toString() - : "")} + {" " + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} ); } else { @@ -222,9 +187,7 @@ const SimulationSummaryContent = () => { <> {value + " " + - (record.hasScalarData - ? record.scalarValue.toPrecision(5).toString() - : "")} + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} ); } @@ -471,4 +434,3 @@ const SimulationSummaryContent = () => { }; export default React.memo(SimulationSummaryContent); - diff --git a/src/components/SimulationSummaryExpanded.tsx b/src/components/SimulationSummaryExpanded.tsx index 90377635..9fdda92c 100644 --- a/src/components/SimulationSummaryExpanded.tsx +++ b/src/components/SimulationSummaryExpanded.tsx @@ -1,16 +1,16 @@ -import { useStoreState, useStoreActions } from "../hooks"; -import { SettingOutlined, MinusOutlined } from "@ant-design/icons"; -import { Table, Row, Col, Button, Slider } from "antd"; +import { MinusOutlined, SettingOutlined } from "@ant-design/icons"; +import { Button, Col, Row, Slider, Table } from "antd"; import type { ColumnsType } from "antd/es/table"; import type { TableRowSelection } from "antd/es/table/interface"; -import { Compute, Fix, Variable } from "../types"; -import React, { useState, useMemo, useCallback } from "react"; -import Modifier from "../modifiers/modifier"; +import React, { useCallback, useMemo, useState } from "react"; +import Figure from "../components/Figure"; +import { useStoreActions, useStoreState } from "../hooks"; +import ColorModifierSettings from "../modifiers/ColorModifierSettings"; +import type ColorModifier from "../modifiers/colormodifier"; +import type Modifier from "../modifiers/modifier"; import SyncBondsSettings from "../modifiers/SyncBondsSettings"; import SyncParticlesSettings from "../modifiers/SyncParticlesSettings"; -import ColorModifierSettings from "../modifiers/ColorModifierSettings"; -import ColorModifier from "../modifiers/colormodifier"; -import Figure from "../components/Figure"; +import type { Compute, Fix, Variable } from "../types"; import { track } from "../utils/metrics"; interface SimulationSummaryType { @@ -27,59 +27,37 @@ const SimulationSummaryExpanded = ({ onShowLess }: SimulationSummaryExpandedProp const [visibleSettings, setVisibleSettings] = useState(); const [visibleCompute, setVisibleCompute] = useState(); const [visibleFix, setVisibleFix] = useState(); - const [visibleVariable, setVisibleVariable] = useState< - Variable | undefined - >(); + const [visibleVariable, setVisibleVariable] = useState(); - const simulationSettings = useStoreState( - (state) => state.settings.simulation, - ); - const modifiers = useStoreState( - (state) => state.processing.postTimestepModifiers, - ); - const postTimestepModifiers = useStoreState( - (state) => state.processing.postTimestepModifiers, - ); + const simulationSettings = useStoreState((state) => state.settings.simulation); + const modifiers = useStoreState((state) => state.processing.postTimestepModifiers); + const postTimestepModifiers = useStoreState((state) => state.processing.postTimestepModifiers); const colorModifier = postTimestepModifiers.filter( - (modifier) => modifier.name === "Colors", + (modifier) => modifier.name === "Colors" )[0] as ColorModifier; - const selectedModifiers = postTimestepModifiers - .filter((m) => m.active) - .map((m) => m.name); + const selectedModifiers = postTimestepModifiers.filter((m) => m.active).map((m) => m.name); const simulation = useStoreState((state) => state.simulation.simulation); const runType = useStoreState((state) => state.simulationStatus.runType); const numAtoms = useStoreState((state) => state.simulationStatus.numAtoms); const numBonds = useStoreState((state) => state.simulationStatus.numBonds); const timesteps = useStoreState((state) => state.simulationStatus.timesteps); - const remainingTime = useStoreState( - (state) => state.simulationStatus.remainingTime, - ); - const timestepsPerSecond = useStoreState( - (state) => state.simulationStatus.timestepsPerSecond, - ); - const memoryUsage = useStoreState( - (state) => state.simulationStatus.memoryUsage, - ); + const remainingTime = useStoreState((state) => state.simulationStatus.remainingTime); + const timestepsPerSecond = useStoreState((state) => state.simulationStatus.timestepsPerSecond); + const memoryUsage = useStoreState((state) => state.simulationStatus.memoryUsage); - const setSimulationSettings = useStoreActions( - (actions) => actions.settings.setSimulation, - ); + const setSimulationSettings = useStoreActions((actions) => actions.settings.setSimulation); const computes = useStoreState((state) => state.simulationStatus.computes); const fixes = useStoreState((state) => state.simulationStatus.fixes); const variables = useStoreState((state) => state.simulationStatus.variables); const setModifierSyncDataPointsAction = useStoreActions( - (actions) => actions.simulationStatus.setModifierSyncDataPoints, + (actions) => actions.simulationStatus.setModifierSyncDataPoints ); const handleToggleSyncDataPoints = useCallback( - ( - name: string, - type: "compute" | "fix" | "variable", - value: boolean, - ) => { + (name: string, type: "compute" | "fix" | "variable", value: boolean) => { setModifierSyncDataPointsAction({ name, type, value }); }, - [setModifierSyncDataPointsAction], + [setModifierSyncDataPointsAction] ); const setSyncFrequency = (value: number | null) => { @@ -137,10 +115,7 @@ const SimulationSummaryExpanded = ({ onShowLess }: SimulationSummaryExpandedProp > {value} {" "} - {" " + - (record.hasScalarData - ? record.scalarValue.toPrecision(5).toString() - : "")} + {" " + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} ); } else { @@ -148,9 +123,7 @@ const SimulationSummaryExpanded = ({ onShowLess }: SimulationSummaryExpandedProp <> {value + " " + - (record.hasScalarData - ? record.scalarValue.toPrecision(5).toString() - : "")} + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} ); } @@ -177,10 +150,7 @@ const SimulationSummaryExpanded = ({ onShowLess }: SimulationSummaryExpandedProp > {value} {" "} - {" " + - (record.hasScalarData - ? record.scalarValue.toPrecision(5).toString() - : "")} + {" " + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} ); } else { @@ -188,9 +158,7 @@ const SimulationSummaryExpanded = ({ onShowLess }: SimulationSummaryExpandedProp <> {value + " " + - (record.hasScalarData - ? record.scalarValue.toPrecision(5).toString() - : "")} + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} ); } @@ -217,10 +185,7 @@ const SimulationSummaryExpanded = ({ onShowLess }: SimulationSummaryExpandedProp > {value} {" "} - {" " + - (record.hasScalarData - ? record.scalarValue.toPrecision(5).toString() - : "")} + {" " + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} ); } else { @@ -228,9 +193,7 @@ const SimulationSummaryExpanded = ({ onShowLess }: SimulationSummaryExpandedProp <> {value + " " + - (record.hasScalarData - ? record.scalarValue.toPrecision(5).toString() - : "")} + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} ); } @@ -374,7 +337,7 @@ const SimulationSummaryExpanded = ({ onShowLess }: SimulationSummaryExpandedProp type="text" icon={} onClick={handleShowLess} - style={{ color: '#fff', padding: 0 }} + style={{ color: "#fff", padding: 0 }} />
)} diff --git a/src/components/SimulationSummaryModal.tsx b/src/components/SimulationSummaryModal.tsx index 6d0c3a8c..c12cde2c 100644 --- a/src/components/SimulationSummaryModal.tsx +++ b/src/components/SimulationSummaryModal.tsx @@ -1,6 +1,6 @@ import { Modal } from "antd"; -import SimulationSummaryContent from "./SimulationSummaryContent"; import { track } from "../utils/metrics"; +import SimulationSummaryContent from "./SimulationSummaryContent"; interface SimulationSummaryModalProps { open: boolean; @@ -28,4 +28,3 @@ const SimulationSummaryModal = ({ open, onClose }: SimulationSummaryModalProps) }; export default SimulationSummaryModal; - diff --git a/src/containers/Console.tsx b/src/containers/Console.tsx index 513250e6..d467907d 100644 --- a/src/containers/Console.tsx +++ b/src/containers/Console.tsx @@ -1,7 +1,7 @@ import Editor from "@monaco-editor/react"; +import type * as Monaco from "monaco-editor"; import { useEffect, useRef } from "react"; import { useStoreState } from "../hooks"; -import type * as Monaco from "monaco-editor"; interface ConsoleProps { width?: number | string; @@ -18,7 +18,7 @@ const Console = ({ width, height }: ConsoleProps) => { selectOnLineNumbers: true, readOnly: true, }; - + useEffect(() => { const editor = editorRef.current; if (editor) { diff --git a/src/containers/Edit.tsx b/src/containers/Edit.tsx index 927d95c6..b563d7aa 100644 --- a/src/containers/Edit.tsx +++ b/src/containers/Edit.tsx @@ -1,1414 +1,1413 @@ -import { useCallback } from "react"; -import { useStoreState } from "../hooks"; import Editor, { loader } from "@monaco-editor/react"; import type * as Monaco from "monaco-editor"; +import { useCallback } from "react"; +import { useStoreState } from "../hooks"; // Initialize Monaco loader.init().then((monaco) => { monaco.languages.register({ id: "lammps" }); monaco.languages.setMonarchTokensProvider("lammps", { - keywords: [ - "ave/correlate", - "lj/spica/coul/msm/omp", - "return", - "heat/gran", - "pair_write", - "fene/intel", - "temper", - "lj/charmm/coul/charmm/omp", - "cna/atom", - "nbond/atom", - "boundary_integral", - "airebo/intel", - "rdf", - "nodeset_to_elementset", - "movie", - "lb/momentum", - "local/gran/vtk", - "tersoff/mod/gpu", - "colloid/omp", - "atom/molecule", - "threebody/table", - "mesh/surface", - "kspace_style", - "plugin", - "write_dump", - "rebo/omp", - "thermo", - "qeq/point", - "mvv/edpd", - "colloid/gpu", - "neigh_settings", - "coul/shield", - "compute", - "scafacos", - "check/timestep/gran", - "lj/cut/thole/long", - "dpd/tstat", - "store/state", - "temp/deform/kk", - "group/group", - "dpd/fdt", - "edip/multi", - "setforce/kk", - "lj/charmm/coul/charmm", - "run_style", - "sph/taitwater/morris", - "born/coul/wolf/cs", - "yukawa/colloid", - "nvt/sllod/kk", - "sw/mod", - "lj/class2/coul/cut/soft", - "cross", - "damping/cundall", - "hbond/dreiding/lj/omp", - "aveforce", - "smd/damage", - "nve/asphere/noforce", - "rigid/npt/omp", - "smd/triangle/vertices", - "harmonic/shift/cut/omp", - "srp", - "lj/cut/coul/dsf/omp", - "read_dump", - "airebo/omp", - "minimize", - "voronoi/atom", - "rigid/nph", - "rigid/local", - "pair/local", - "nve/asphere/gpu", - "lj/cut/dipole/cut/gpu", - "coul/msm", - "kernel_bandwidth", - "tdpd/cc/atom", - "zbl/kk", - "lumped_lambda_solve", - "suffix", - "peri/lps/omp", - "coul/wolf", - "fene/expand", - "pafi", - "nm/cut/split", - "stress/mop", - "coul/wolf/omp", - "nve/kk", - "table/omp", - "lj/cut/coul/debye/dielectric/omp", - "group2ndx", - "brownian", - "neigh_modify", - "lj/cut/coul/cut/soft", - "hbond/dreiding/lj", - "tersoff/zbl", - "hybrid/overlay", - "dump_modify", - "bond/react", - "nve/bpm/sphere", - "lj/charmm/coul/charmm/kk", - "lj/cut/coul/long/dielectric/omp", - "pair", - "colvars", - "e3b", - "custom/vtk", - "equal", - "coul/diel", - "ke/multisphere", - "temp/deform", - "on_the_fly", - "hybrid", - "eim/omp", - "lj/charmm/coul/long/omp", - "vtk", - "heat", - "temp/chunk", - "smd/tlsph/num/neighs", - "multi/harmonic", - "bubble", - "neb/spin", - "wall/body/polyhedron", - "atom/adios", - "reaxff/omp", - "nvt/sllod/intel", - "precession/spin", - "pressure", - "partition", - "ave/sphere/atom/kk", - "buck/long/coul/long", - "bond", - "buck", - "third_order/kk", - "cfg/uef", - "rigid/nve/small", - "buck/coul/long/cs", - "damage/atom", - "coul/cut/global", - "reaxff/kk", - "multisphere", - "track_displacement", - "temp/rotate", - "sqdistharm", - "smd/ulsph", - "smd/ulsph/num/neighs", - "smd/tri_surface", - "off", - "pressure/uef", - "vcm/chunk", - "lj/cut/coul/debye", - "gravity", - "sna/grid", - "lj/charmm/coul/long/opt", - "lj/cut/coul/long/omp", - "dpd/kk", - "harmonic/shift", - "lj/cut/soft", - "sph/density/corr", - "lj/charmm/coul/long/soft/omp", - "lj/cut/coul/long/cs", - "smd/tlsph/stress", - "qeq/reaxff/omp", - "nve/line", - "thermal", - "gld", - "halt", - "reset_timestep", - "gran", - "msst", - "smtbq", - "force/tally", - "body/rounded/polygon", - "adp/kk", - "morse/smooth/linear/omp", - "bocs", - "yes", - "coul/long/soft/omp", - "born/coul/dsf", - "cosine/buck6d", - "sph/artVisc/tensCorr", - "lj/spica/coul/long/omp", - "source_integration", - "sph/rho/atom", - "born/coul/long/cs/gpu", - "setforce/spin", - "min_style fire", - "gran/hooke", - "computes", - "gauss/cut/omp", - "tersoff/mod/c", - "plumed", - "smd/move_tri_surf", - "amoeba/bitorsion", - "awpmd/cut", - "filter", - "lj/cut/coul/msm", - "localized_lambda", - "coul/cut/omp", - "wall/lj93", - "hybrid/kk", - "com", - "coul/long/soft", - "temp/deform/eff", - "lj/gromacs/omp", - "peri/pmb", - "lj/cut/coul/long/soft/omp", - "gauss", - "wall/gran/region", - "lj/charmm/coul/msm", - "unfix", - "pppm/disp", - "multi/lucy/rx/kk", - "morse/kk", - "internal_element_set", - "nve/omp", - "qeq/comb/omp", - "coul/cut/soft", - "coul/streitz", - "buck/gpu", - "angle/local", - "nph", - "gayberne", - "lj/gromacs/coul/gromacs", - "rebo", - "manifoldforce", - "lj/spica/omp", - "coul/long/dielectric", - "adf", - "smd/tlsph/strain", - "gaussian", - "compute_modify", - "shake/kk", - "lj/class2/gpu", - "msd", - "meam/spline/omp", - "while", - "reaxff/bonds", - "yukawa/gpu", - "tersoff/zbl/gpu", - "multisphere/break", - "lj/cut/opt", - "ttm", - "born/coul/long/cs", - "if", - "min_style sd", - "rebo/intel", - "dihedral/local", - "special", - "helix", - "tersoff/zbl/omp", - "body/local", - "smd/rho", - "bond_coeff", - "sw/mod/omp", - "dimension", - "smd/ulsph/strain/rate", - "smd/tlsph/defgrad", - "npt/eff", - "lj/spica/kk", - "fep/ta", - "nvt/sphere", - "propel/self", - "granular", - "vashishta/table/omp", - "special_bonds", - "meam", - "lj/cut/dipole/cut", - "erotate/asphere", - "lj/cut/coul/long/dielectric", - "lj/cut/kk", - "lcbop", - "adapt/fep", - "fabric", - "coul/long/cs", - "lubricateU", - "com/chunk", - "lj/charmm/coul/long/kk", - "lj/cut/coul/debye/gpu", - "temp/drude", - "lj/cut/coul/cut/dielectric", - "ke/atom/eff", - "buoyancy", - "lj/charmm/coul/charmm/implicit", - "oxrna2/excv", - "gayberne/omp", - "rigid/npt/small", - "lj/long/coul/long", - "gravity/kk", - "massflow/mesh/sieve", - "lj96/cut/gpu", - "snap", - "NULL", - "resquared", - "freeze/kk", - "temp/ramp", - "charge/regulation", - "pair_modify", - "spica", - "external", - "property/atom/tracer/stream", - "tersoff/mod/kk", - "lebedeva/z", - "gle", - "lj/cut/coul/debye/omp", - "ring", - "smd/internal/energy", - "stress/cartesian", - "eam/cd", - "oxdna/hbond", - "lj/cut/coul/msm/gpu", - "buck/coul/msm", - "lj/relres", - "continuum/weighted", - "ke/eff", - "oxdna/xstk", - "wall/region", - "orientorder/atom", - "boundary_faceset", - "heat/flux/virial/tally", - "temp/eff", - "pe/tally", - "angmom/chunk", - "coul/cut/kk", - "pppm/stagger", - "vashishta/gpu", - "qeq/reaxff/kk", - "nph/omp", - "gradients", - "thole", - "setforce", - "reset_atom_ids", - "momentum/chunk", - "deposit", - "dihedral_coeff", - "inversion/harmonic", - "table/rx", - "inertia/molecule", - "lj/cut/coul/long/kk", - "property/atom/kk", - "pppm", - "kolmogorov/crespi/z", - "spring/rg", - "particletemplate/sphere", - "nvt/sphere/omp", - "eam/alloy/gpu", - "lj/cut/thole/long/omp", - "smd/ulsph/strain", - "oxdna2/hbond", - "lj/class2/coul/long/kk", - "harmonic/omp", - "nve/gpu", - "coul/slater/cut", - "contour_integral", - "morse/omp", - "min_style spin", - "agni/omp", - "insert/rate/region", - "ipi", - "airebo/morse/intel", - "buck/coul/cut/omp", - "ke/atom", - "peri/lps", - "pair/gran/local", - "wall/ees", - "umbrella", - "gran/hooke/history", - "morse/smooth/linear", - "lj/smooth/linear", - "meso/move", - "rann", - "atc", - "nve/intel", - "cosine/kk", - "kolmogorov/crespi/full", - "temp/region/eff", - "rhok", - "nph/asphere", - "wall/lj93/kk", - "msd/chunk", - "opls/intel", - "then", - "ave/euler", - "run", - "smd/plastic/strain", - "yukawa", - "wall/body/polygon", - "lj/cut/tip4p/long/soft", - "cossq", - "lineforce", - "sph/idealgas", - "tgnvt/drude", - "imd", - "lj/charmm/coul/long", - "rx/kk", - "nharmonic", - "cosine/shift", - "orient/fcc", - "pppm/tip4p", - "wf/cut", - "cmap", - "nm/cut/coul/cut", - "improper_coeff", - "eam/omp", - "nm/cut/coul/long/omp", - "amoeba", - "tip4p/long/soft", - "morse", - "controller", - "spin/exchange", - "dipole", - "contact/atom", - "edip", - "mdpd/rhosum", - "wall/lj1043", - "spica/omp", - "lj/cut/coul/cut/dielectric/omp", - "nvt/manifold/rattle", - "kim", - "edip/omp", - "vacf", - "soft/omp", - "contact/atom/gran", - "units", - "qmmm", - "lj/cut/coul/wolf", - "lubricate/omp", - "exp6/rx", - "buck/coul/cut", - "coul/cut", - "move/mesh", - "comm_modify", - "lj/long/coul/long/omp", - "neb", - "erotate/sphere", - "lj/class2/omp", - "omega/chunk", - "lj/cut/coul/msm/omp", - "lj/gromacs/coul/gromacs/kk", - "sph/e/atom", - "coul/wolf/kk", - "molfile", - "born/coul/wolf/gpu", - "born/coul/wolf/cs/gpu", - "nve/body", - "amoeba/pitorsion", - "npt", - "lj/expand/omp", - "electron_integration", - "atom/swap", - "create", - "eff/cut", - "lj96/cut", - "saed/vtk", - "pace/kk", - "dsmc", - "rigid/meso", - "beck/gpu", - "coul/dsf/omp", - "rigid", - "npt/asphere", - "smd/adjust_dt", - "born/matrix", - "lj/charmmfsw/coul/charmmfsh", - "table", - "cosine/shift/exp/omp", - "langevin/kk", - "lj/expand", - "yukawa/colloid/gpu", - "insert/pack", - "rigid/small", - "newton", - "atomic_charge", - "dynamical_matrix", - "dpd/fdt/energy/kk", - "angle_style", - "eim", - "meam/sw/spline", - "cosine/shift/omp", - "variable", - "read_restart", - "mgpt", - "viscosity/cos", - "lj/gromacs", - "dpd/gpu", - "polymorphic", - "phonon", - "lj/gromacs/coul/gromacs/omp", - "cosine/periodic", - "coul/debye/omp", - "smd/tlsph/shape", - "lj/cut/coul/dsf", - "label", - "qbmsst", - "morse/opt", - "rigid/nve/omp", - "indent", - "eam", - "eos/cv", - "nb3b/harmonic", - "dpd/tstat/kk", - "gauss/gpu", - "mliap", - "polarize/bem/icc", - "oxrna2/hbond", - "property/atom", - "sw/intel", - "multisphere/single", - "bond/swap", - "nvt/asphere", - "efield", - "nm/cut", - "lj/spica/coul/long/gpu", - "mie/cut/gpu", - "adapt", - "lj/class2/coul/long/gpu", - "fe_md_boundary", - "grem", - "buck/omp", - "smd/hourglass/error", - "peri/pmb/omp", - "tersoff/mod/c/omp", - "bond/break", - "beck/omp", - "smd/plastic/strain/rate", - "append/atoms", - "lj/cubic", - "gromos/omp", - "nvt/gpu", - "nph/sphere/omp", - "ffl", - "for", - "uncompute", - "ti/spring", - "class2/p6", - "dynamical_matrix/kk", - "lj/class2/soft", - "oneway", - "nvt/uef", - "deform/kk", - "property/chunk", - "eam/fs", - "eam/gpu", - "harmonic/cut/omp", - "particledistribution/discrete/massbased", - "min_style fire/old", - "local/density", - "orient", - "msm", - "airebo/morse/omp", - "coul/cut/soft/omp", - "temp/partial", - "wall/gran/local", - "tip4p/long/soft/omp", - "lj/cut/coul/long/intel", - "wall/harmonic", - "tracker", - "coul/long", - "enforce2d/kk", - "cosine", - "harmonic/cut", - "quip", - "smd/ulsph/effm", - "morse/soft", - "vashishta", - "gravity/omp", - "delete_elements", - "lj/long/tip4p/long/omp", - "harmonic/kk", - "mesont/tpm", - "consistent_fe_initialization", - "gauss/cut", - "body/rounded/polyhedron", - "wall/reflect", - "zbl/gpu", - "yukawa/colloid/omp", - "table/gpu", - "edpd/temp/atom", - "saip/metal", - "flow/gauss", - "lj/cut/coul/long/soft", - "resquared/omp", - "heat/gran/conduction", - "lj/cut/dipole/long", - "temp", - "rates", - "temp/sphere", - "tip4p/long/omp", - "lj/cubic/gpu", - "nvt/kk", - "spin/dipole/cut", - "reaxff/species/kk", - "dipole/omp", - "drude", - "nphug", - "accelerate/cos", - "eam/opt", - "tad", - "temper/npt", - "tersoff/table", - "coul/dsf/gpu", - "gran/hooke/omp", - "smd/vol", - "buck/long/coul/long/omp", - "lj/sf/dipole/sf/omp", - "replicate", - "npt/body", - "mol/swap", - "gyration/shape/chunk", - "ilp/graphene/hbn", - "colloid", - "mass", - "nm/cut/coul/long", - "lj/cut/coul/cut/gpu", - "cluster/atom", - "property/global", - "numdiff/virial", - "insert/stream", - "nvt", - "lj/charmm/coul/long/soft", - "ring/omp", - "lj/cut/dipole/cut/omp", - "airebo/morse", - "initial", - "min_style quickmin", - "viscous/sphere", - "nve/dotc/langevin", - "zero", - "viscosity", - "gcmc", - "momb", - "buck/coul/long/intel", - "mvv/dpd", - "read", - "msd/molecule", - "tersoff/intel", - "coul/tt", - "molecule", - "momentum", - "hexorder/atom", - "lj/cut/coul/dsf/gpu", - "lj/charmm/coul/charmm/implicit/omp", - "tersoff/table/omp", - "reaxff", - "widom", - "mdi/qm", - "nve/sphere/kk", - "hbond/dreiding/morse/omp", - "ttm/grid", - "delete_bonds", - "drag", - "PI", - "tip4p/cut", - "sph/rhosum", - "nonlinear/omp", - "tune/kspace", - "write_coeff", - "lj/cut/coul/cut/omp", - "reset_time", - "reaxff/bonds/kk", - "poisson_solver", - "coul/long/cs/gpu", - "spring", - "opls/omp", - "print", - "lj/expand/gpu", - "morse/gpu", - "srd", - "cosine/delta", - "nve/awpmd", - "nph/asphere/omp", - "mdi", - "add_to_nodeset", - "lj/cut/tip4p/long/gpu", - "distance", - "mdpd", - "cnp/atom", - "smd", - "ehex", - "lj/class2/coul/long/omp", - "lj/cut/coul/cut/soft/omp", - "brownian/omp", - "buck/coul/cut/intel", - "drip", - "ave/atom", - "smd/hertz", - "quadrature", - "fene/nm", - "reference_potential_energy", - "nvt/omp", - "soft/gpu", - "sw/omp", - "quartic", - "oxdna2/fene", - "min_modify", - "material", - "sw/angle/table", - "sph/density/summation", - "plasticity/atom", - "cosine/omp", - "coul/cut/dielectric", - "coul/msm/omp", - "enforce2d", - "quadratic", - "kspace_modify", - "smd/contact/radius", - "class2/kk", - "lj/smooth/gpu", - "coul/long/omp", - "temp/body", - "shardlow/kk", - "fields", - "global/atom", - "oxdna2/excv", - "class2/omp", - "wall/reflect/kk", - "temp/uef", - "temp/com", - "create_box", - "lj/relres/omp", - "origin", - "nvt/body", - "python/move", - "lj/class2/coul/cut/kk", - "cvff", - "temp/rescale", - "EDGE", - "rigid/npt", - "fene", - "multi/lucy", - "fep", - "mass_matrix", - "qeq/comb", - "oxdna2/stk", - "bpm/spring", - "add_species", - "atom_modify", - "tersoff/mod/omp", - "stress/cylinder", - "angle", - "nvt/intel", - "source", - "lj/long/tip4p/long", - "sph/density/continuity", - "nve/manifold/rattle", - "smd/setvel", - "create_bonds", - "cosine/squared/omp", - "meam/spline", - "coul/cut/omp/global", - "list", - "group", - "spring/self", - "dpd", - "pour", - "peri/ves", - "qeq/dynamic", - "harmonic/intel", - "erotate/superquadric", - "particledistribution/discrete", - "displace_atoms", - "pppm/cg", - "volume_integral", - "cossq/omp", - "move", - "bond_style", - "nve/sphere", - "python/invoke", - "output", - "opls/kk", - "region", - "eam/alloy/omp", - "temp/csvr", - "ewald", - "efield/atom", - "press/berendsen", - "pimd", - "nph/body", - "torque/chunk", - "addtorque", - "lj/charmm/coul/charmm/intel", - "pace", - "rx", - "langevin/eff", - "sample_frequency", - "meam/kk", - "sw", - "charmmfsw", - "polarize/bem/gmres", - "write_data", - "smd/integrate_tlsph", - "ave/chunk", - "vector", - "lj/smooth", - "born/coul/dsf/cs", - "dpd/ext/tstat/kk", - "include", - "saed", - "lubricate/poly/omp", - "dpd/ext/tstat", - "lj/cut/coul/debye/kk", - "buck/intel", - "gauss/omp", - "hybrid/overlay/kk", - "erotate/sphere/atom", - "langevin", - "lj/cut/coul/cut/kk", - "snap/kk", - "poems", - "line/lj", - "yukawa/kk", - "sdpd/taitwater/isothermal", - "freeze", - "smd/ulsph/stress", - "sph/lj", - "table/cut", - "nve/dot", - "temperature_definition", - "python", - "pppm/disp/tip4p", - "lj/class2/coul/long", - "body/nparticle", - "nvt/sllod/omp", - "particletemplate/superquadric", - "tip4p/long", - "nve/noforce", - "beck", - "planeforce", - "change_box", - "born/coul/long/gpu", - "lj/cut/tip4p/cut/omp", - "h5md", - "lj/charmm/coul/charmm/gpu", - "kernel", - "nve/spin", - "eam/cd/old", - "born/coul/msm", - "lj/mdf", - "lj/long/coul/long/intel", - "eam/alloy", - "lj/switch3/coulgauss/long", - "sph", - "particledistribution/discrete/numberbased", - "rigid/nve", - "displace/atom", - "basal/atom", - "nve/sphere/omp", - "charmm/kk", - "improper", - "hdnnp", - "ave/spatial", - "lubricate", - "coul/long/gpu", - "buck/coul/msm/omp", - "lj/spica", - "eam/he", - "fix_modify", - "oxdna2/xstk", - "erotate", - "balance", - "lj/cut/gpu", - "mm3", - "dump", - "mie/cut", - "nph/kk", - "reaxff/species", - "ewald/disp", - "cosine/periodic/omp", - "nve/tri", - "event/displace", - "umbrella/omp", - "airebo", - "lb/viscous", - "npt/omp", - "dpd/ext", - "rigid/nvt/omp", - "image", - "sph/taitwater", - "snav/atom", - "fourier/intel", - "lj/long/coul/long/opt", - "hyper/local", - "lj/expand/coul/long", - "harmonic/shift/omp", - "store/force", - "npt/intel", - "tersoff/gpu", - "lj/gromacs/gpu", - "pe", - "true", - "erotate/multisphere", - "helix/omp", - "buck/coul/long/kk", - "nphug/omp", - "mesocnt", - "prd", - "read_data", - "oxdna/fene", - "pair_coeff", - "tersoff/mod", - "lj/class2", - "buck/coul/cut/gpu", - "lj/cut/intel", - "born/coul/wolf", - "lj/long/dipole/long", - "nvt/sllod", - "lj/cut", - "jump", - "eam/alloy/opt", - "bond_write", - "entropy/atom", - "brownian/poly", - "hybrid/scaled", - "msm/cg", - "tersoff", - "momentum/kk", - "wall/srd", - "neighbor", - "write_restart", - "lj/class2/kk", - "nve/asphere", - "dpd/intel", - "opls", - "eam/kk", - "eam/intel", - "spherical", - "comb/omp", - "min_style spin/cg", - "velocity", - "restrain", - "deform", - "fene/kk", - "coul/slater", - "echo", - "clear", - "eam/alloy/kk", - "write_atom_weights", - "multi/lucy/rx", - "chunk/spread/atom", - "lj/cut/coul/wolf/omp", - "dpd/energy", - "nve/asphere/intel", - "multi/harmonic/omp", - "evaporate", - "lj/class2/coul/long/soft", - "ave/histo", - "dielectric", - "boundary", - "sph/heatconduction", - "pair_style", - "lj/charmm/coul/long/gpu", - "restart", - "vashishta/kk", - "lj/cut/coul/long/opt", - "qeq/slater", - "remove_molecule", - "nve/superquadric", - "lj/spica/coul/msm", - "sph/stationary", - "pe/atom", - "lj/cut/tip4p/long/omp", - "fragment/atom", - "shake", - "exp6/rx/kk", - "brownian/poly/omp", - "dpd/ext/kk", - "temp/cs", - "coul/diel/omp", - "npt/sphere/omp", - "edpd", - "bond/local", - "smd/integrate_ulsph", - "heat/flux", - "shardlow", - "zbl", - "hbond/dreiding/morse", - "on", - "atom_style", - "harmonic", - "fene/expand/omp", - "equilibrium_start", - "born/omp", - "addforce", - "ti", - "temp/rescale/eff", - "snad/atom", - "lj/cut/coul/cut", - "latte", - "type", - "delete_atoms", - "timestep", - "smd/tlsph/strain/rate", - "wall/piston", - "spin", - "gayberne/gpu", - "smd/tlsph", - "slice", - "born/coul/wolf/omp", - "cosine/squared", - "lj/cut/coul/msm/dielectric", - "create_elementset", - "agni", - "dpd/fdt/energy", - "property/molecule", - "dihedral", - "ke", - "nm/cut/coul/cut/omp", - "lj/sf/dipole/sf/gpu", - "lj/cut/coul/dsf/kk", - "coul/debye/gpu", - "min_style cg", - "gyration/shape", - "time_integration", - "dpd/atom", - "tersoff/omp", - "gw", - "improper_style", - "plane", - "lj/cut/soft/omp", - "set", - "lj/cut/tip4p/long/soft/omp", - "mask_direction", - "coul/dsf/kk", - "exchange", - "temp/asphere", - "lj/cut/coul/long/gpu", - "born", - "fix_flux", - "lj/charmm/coul/long/intel", - "spin/dmi", - "box/relax", - "elif", - "charmm", - "ufm", - "class2", - "quartic/omp", - "box", - "info", - "gyration", - "couple/cfd", - "spring/chunk", - "tip4p/cut/omp", - "sna/atom", - "undump", - "nve", - "lattice", - "qeq/reaxff", - "qtb", - "wall/reflect/stochastic", - "gyration/molecule", - "buck/coul/long", - "nparticles/tracer/region", - "fourier", - "timer", - "gran/hertz/history", - "smd/wall_surface", - "soft", - "lj/spica/coul/long", - "cvff/omp", - "com/molecule", - "angle_coeff", - "lubricate/poly", - "lj/cut/tip4p/long", - "lj/charmm/coul/charmm/implicit/kk", - "lj/cut/coul/long", - "spin/neel", - "msd/nongauss", - "charmm/omp", - "INF", - "oxrna2/stk", - "massflow/mesh", - "reduce/region", - "lj/cut/coul/debye/dielectric", - "lj/class2/coul/cut", - "npt/asphere/omp", - "create_atoms", - "comb", - "bond/create", - "langevin/spin", - "hyper/global", - "lj/expand/kk", - "boundary_dynamics", - "lj/cut/tip4p/cut", - "package", - "bpm/rotational", - "third_order", - "lj/expand/coul/long/gpu", - "coul/long/kk", - "lj/smooth/omp", - "write", - "buck/mdf", - "coul/dsf", - "coord/atom/kk", - "property/atom/tracer", - "eos/table/rx", - "ave/sphere/atom", - "communicate", - "nodeset", - "comb3", - "dihedral_style", - "buck/coul/long/omp", - "coord/gran", - "sph/pressure", - "acks2/reaxff/kk", - "tfmc", - "electron/stopping", - "extep", - "rigid/small/omp", - "xrd", - "orientorder/atom/kk", - "dpd/energy/kk", - "add_molecule", - "spin/magelec", - "distharm", - "stress/atom", - "rigid/nph/omp", - "cosine/shift/exp", - "qeq/shielded", - "rigid/omp", - "dpd/omp", - "log", - "cosine/delta/omp", - "property/local", - "ke/rigid", - "acks2/reaxff", - "shell", - "hyper", - "reduce", - "coul/exclude", - "centro/atom", - "eam/alloy/intel", - "yukawa/omp", - "no", - "dipole/chunk", - "next", - "fene/omp", - "coul/cut/gpu", - "temp/profile", - "zbl/omp", - "remove_source", - "reset_mol_ids", - "adp/omp", - "ptm/atom", - "rattle", - "fourier/simple", - "cvff/intel", - "coul/debye/kk", - "sw/gpu", - "none", - "ave/correlate/long", - "inertia/chunk", - "born/gpu", - "lj/sf/dipole/sf", - "npt/cauchy", - "tmd", - "lj/cut/dipole/long/gpu", - "drude/transform/direct", - "oxrna2/coaxstk", - "nvt/asphere/omp", - "ufm/omp", - "heat/flux/tally", - "oxdna/excv", - "ufm/opt", - "gran/hooke/history/omp", - "nonlinear", - "lj/spica/gpu", - "vashishta/omp", - "npt/gpu", - "atm", - "smd/tlsph/dt", - "edpd/source", - "numdiff", - "thermo_style", - "lj/cubic/omp", - "recenter", - "table/kk", - "brownian/sphere", - "multicontact/halfspace", - "lj/charmm/coul/msm/omp", - "lb/fluid", - "thermal/conductivity", - "coord/atom", - "tri/lj", - "eos/table", - "processors", - "scale", - "lj/cut/omp", - "adp", - "nharmonic/omp", - "nvt/sllod/eff", - "oxdna2/coaxstk", - "ackland/atom", - "lj/gromacs/kk", - "nvt/eff", - "erotate/rigid", - "mesont", - "born/coul/msm/omp", - "labelmap", - "born/coul/long/omp", - "gran/hertz/history/omp", - "tersoff/kk", - "create_nodeset", - "decomposition", - "bop", - "reduce/chunk", - "sw/kk", - "property/atom/regiontracer/time", - "temp/region", - "reset_atomic_reference_positions", - "lj96/cut/omp", - "buck/coul/long/gpu", - "quadratic/omp", - "resquared/gpu", - "dt/reset", - "atom_weight", - "nvk", - "nve/limit", - "unfix_flux", - "ufm/gpu", - "fourier/omp", - "comm_style", - "rigid/nvt", - "chunk/atom", - "vashishta/table", - "nph/sphere", - "sph/t/atom", - "mscg", - "filter/corotate", - "temper/grem", - "min_style hftn", - "npt/kk", - "dpd/tstat/omp", - "dilatation/atom", - "buck6d/coul/gauss/dsf", - "mesh/surface/planar", - "langevin/drude", - "fourier/simple/omp", - "lj/smooth/linear/omp", - "dpd/tstat/gpu", - "nm/cut/omp", - "pair_interactions", - "lj/cut/tip4p/long/opt", - "netcdf", - "remove_species", - "eos/table/rx/kk", - "smatb", - "table/rx/kk", - "neighbor_skin", - "ilp/tmd", - "hma", - "buck/coul/cut/kk", - "buck/kk", - "ave/time", - "quit", - "gran/hooke/history/kk", - "harmonic/shift/cut", - "coul/wolf/cs", - "thermo_modify", - "coul/debye", - "tersoff/zbl/kk", - "gyration/chunk", - "lj/class2/coul/cut/omp", - "particletemplate/multisphere", - "oxdna/stk", - "rerun", - "fix", - "rigid/nvt/small", - "viscous", - "atom_element_map", - "temp/kk", - "born/coul/long", - "wall/gran", - "wall/region/sph", - "pe/mol/tally", - "wall/lj126", - "charmm/intel", - "temp/berendsen", - "else", - "gayberne/intel", - "false", - "oxrna2/xstk", - "wall/colloid", - "improper/local", - "npt/sphere", - "internal_quadrature", - "nve/eff", - "gromos", - ], + keywords: [ + "ave/correlate", + "lj/spica/coul/msm/omp", + "return", + "heat/gran", + "pair_write", + "fene/intel", + "temper", + "lj/charmm/coul/charmm/omp", + "cna/atom", + "nbond/atom", + "boundary_integral", + "airebo/intel", + "rdf", + "nodeset_to_elementset", + "movie", + "lb/momentum", + "local/gran/vtk", + "tersoff/mod/gpu", + "colloid/omp", + "atom/molecule", + "threebody/table", + "mesh/surface", + "kspace_style", + "plugin", + "write_dump", + "rebo/omp", + "thermo", + "qeq/point", + "mvv/edpd", + "colloid/gpu", + "neigh_settings", + "coul/shield", + "compute", + "scafacos", + "check/timestep/gran", + "lj/cut/thole/long", + "dpd/tstat", + "store/state", + "temp/deform/kk", + "group/group", + "dpd/fdt", + "edip/multi", + "setforce/kk", + "lj/charmm/coul/charmm", + "run_style", + "sph/taitwater/morris", + "born/coul/wolf/cs", + "yukawa/colloid", + "nvt/sllod/kk", + "sw/mod", + "lj/class2/coul/cut/soft", + "cross", + "damping/cundall", + "hbond/dreiding/lj/omp", + "aveforce", + "smd/damage", + "nve/asphere/noforce", + "rigid/npt/omp", + "smd/triangle/vertices", + "harmonic/shift/cut/omp", + "srp", + "lj/cut/coul/dsf/omp", + "read_dump", + "airebo/omp", + "minimize", + "voronoi/atom", + "rigid/nph", + "rigid/local", + "pair/local", + "nve/asphere/gpu", + "lj/cut/dipole/cut/gpu", + "coul/msm", + "kernel_bandwidth", + "tdpd/cc/atom", + "zbl/kk", + "lumped_lambda_solve", + "suffix", + "peri/lps/omp", + "coul/wolf", + "fene/expand", + "pafi", + "nm/cut/split", + "stress/mop", + "coul/wolf/omp", + "nve/kk", + "table/omp", + "lj/cut/coul/debye/dielectric/omp", + "group2ndx", + "brownian", + "neigh_modify", + "lj/cut/coul/cut/soft", + "hbond/dreiding/lj", + "tersoff/zbl", + "hybrid/overlay", + "dump_modify", + "bond/react", + "nve/bpm/sphere", + "lj/charmm/coul/charmm/kk", + "lj/cut/coul/long/dielectric/omp", + "pair", + "colvars", + "e3b", + "custom/vtk", + "equal", + "coul/diel", + "ke/multisphere", + "temp/deform", + "on_the_fly", + "hybrid", + "eim/omp", + "lj/charmm/coul/long/omp", + "vtk", + "heat", + "temp/chunk", + "smd/tlsph/num/neighs", + "multi/harmonic", + "bubble", + "neb/spin", + "wall/body/polyhedron", + "atom/adios", + "reaxff/omp", + "nvt/sllod/intel", + "precession/spin", + "pressure", + "partition", + "ave/sphere/atom/kk", + "buck/long/coul/long", + "bond", + "buck", + "third_order/kk", + "cfg/uef", + "rigid/nve/small", + "buck/coul/long/cs", + "damage/atom", + "coul/cut/global", + "reaxff/kk", + "multisphere", + "track_displacement", + "temp/rotate", + "sqdistharm", + "smd/ulsph", + "smd/ulsph/num/neighs", + "smd/tri_surface", + "off", + "pressure/uef", + "vcm/chunk", + "lj/cut/coul/debye", + "gravity", + "sna/grid", + "lj/charmm/coul/long/opt", + "lj/cut/coul/long/omp", + "dpd/kk", + "harmonic/shift", + "lj/cut/soft", + "sph/density/corr", + "lj/charmm/coul/long/soft/omp", + "lj/cut/coul/long/cs", + "smd/tlsph/stress", + "qeq/reaxff/omp", + "nve/line", + "thermal", + "gld", + "halt", + "reset_timestep", + "gran", + "msst", + "smtbq", + "force/tally", + "body/rounded/polygon", + "adp/kk", + "morse/smooth/linear/omp", + "bocs", + "yes", + "coul/long/soft/omp", + "born/coul/dsf", + "cosine/buck6d", + "sph/artVisc/tensCorr", + "lj/spica/coul/long/omp", + "source_integration", + "sph/rho/atom", + "born/coul/long/cs/gpu", + "setforce/spin", + "min_style fire", + "gran/hooke", + "computes", + "gauss/cut/omp", + "tersoff/mod/c", + "plumed", + "smd/move_tri_surf", + "amoeba/bitorsion", + "awpmd/cut", + "filter", + "lj/cut/coul/msm", + "localized_lambda", + "coul/cut/omp", + "wall/lj93", + "hybrid/kk", + "com", + "coul/long/soft", + "temp/deform/eff", + "lj/gromacs/omp", + "peri/pmb", + "lj/cut/coul/long/soft/omp", + "gauss", + "wall/gran/region", + "lj/charmm/coul/msm", + "unfix", + "pppm/disp", + "multi/lucy/rx/kk", + "morse/kk", + "internal_element_set", + "nve/omp", + "qeq/comb/omp", + "coul/cut/soft", + "coul/streitz", + "buck/gpu", + "angle/local", + "nph", + "gayberne", + "lj/gromacs/coul/gromacs", + "rebo", + "manifoldforce", + "lj/spica/omp", + "coul/long/dielectric", + "adf", + "smd/tlsph/strain", + "gaussian", + "compute_modify", + "shake/kk", + "lj/class2/gpu", + "msd", + "meam/spline/omp", + "while", + "reaxff/bonds", + "yukawa/gpu", + "tersoff/zbl/gpu", + "multisphere/break", + "lj/cut/opt", + "ttm", + "born/coul/long/cs", + "if", + "min_style sd", + "rebo/intel", + "dihedral/local", + "special", + "helix", + "tersoff/zbl/omp", + "body/local", + "smd/rho", + "bond_coeff", + "sw/mod/omp", + "dimension", + "smd/ulsph/strain/rate", + "smd/tlsph/defgrad", + "npt/eff", + "lj/spica/kk", + "fep/ta", + "nvt/sphere", + "propel/self", + "granular", + "vashishta/table/omp", + "special_bonds", + "meam", + "lj/cut/dipole/cut", + "erotate/asphere", + "lj/cut/coul/long/dielectric", + "lj/cut/kk", + "lcbop", + "adapt/fep", + "fabric", + "coul/long/cs", + "lubricateU", + "com/chunk", + "lj/charmm/coul/long/kk", + "lj/cut/coul/debye/gpu", + "temp/drude", + "lj/cut/coul/cut/dielectric", + "ke/atom/eff", + "buoyancy", + "lj/charmm/coul/charmm/implicit", + "oxrna2/excv", + "gayberne/omp", + "rigid/npt/small", + "lj/long/coul/long", + "gravity/kk", + "massflow/mesh/sieve", + "lj96/cut/gpu", + "snap", + "NULL", + "resquared", + "freeze/kk", + "temp/ramp", + "charge/regulation", + "pair_modify", + "spica", + "external", + "property/atom/tracer/stream", + "tersoff/mod/kk", + "lebedeva/z", + "gle", + "lj/cut/coul/debye/omp", + "ring", + "smd/internal/energy", + "stress/cartesian", + "eam/cd", + "oxdna/hbond", + "lj/cut/coul/msm/gpu", + "buck/coul/msm", + "lj/relres", + "continuum/weighted", + "ke/eff", + "oxdna/xstk", + "wall/region", + "orientorder/atom", + "boundary_faceset", + "heat/flux/virial/tally", + "temp/eff", + "pe/tally", + "angmom/chunk", + "coul/cut/kk", + "pppm/stagger", + "vashishta/gpu", + "qeq/reaxff/kk", + "nph/omp", + "gradients", + "thole", + "setforce", + "reset_atom_ids", + "momentum/chunk", + "deposit", + "dihedral_coeff", + "inversion/harmonic", + "table/rx", + "inertia/molecule", + "lj/cut/coul/long/kk", + "property/atom/kk", + "pppm", + "kolmogorov/crespi/z", + "spring/rg", + "particletemplate/sphere", + "nvt/sphere/omp", + "eam/alloy/gpu", + "lj/cut/thole/long/omp", + "smd/ulsph/strain", + "oxdna2/hbond", + "lj/class2/coul/long/kk", + "harmonic/omp", + "nve/gpu", + "coul/slater/cut", + "contour_integral", + "morse/omp", + "min_style spin", + "agni/omp", + "insert/rate/region", + "ipi", + "airebo/morse/intel", + "buck/coul/cut/omp", + "ke/atom", + "peri/lps", + "pair/gran/local", + "wall/ees", + "umbrella", + "gran/hooke/history", + "morse/smooth/linear", + "lj/smooth/linear", + "meso/move", + "rann", + "atc", + "nve/intel", + "cosine/kk", + "kolmogorov/crespi/full", + "temp/region/eff", + "rhok", + "nph/asphere", + "wall/lj93/kk", + "msd/chunk", + "opls/intel", + "then", + "ave/euler", + "run", + "smd/plastic/strain", + "yukawa", + "wall/body/polygon", + "lj/cut/tip4p/long/soft", + "cossq", + "lineforce", + "sph/idealgas", + "tgnvt/drude", + "imd", + "lj/charmm/coul/long", + "rx/kk", + "nharmonic", + "cosine/shift", + "orient/fcc", + "pppm/tip4p", + "wf/cut", + "cmap", + "nm/cut/coul/cut", + "improper_coeff", + "eam/omp", + "nm/cut/coul/long/omp", + "amoeba", + "tip4p/long/soft", + "morse", + "controller", + "spin/exchange", + "dipole", + "contact/atom", + "edip", + "mdpd/rhosum", + "wall/lj1043", + "spica/omp", + "lj/cut/coul/cut/dielectric/omp", + "nvt/manifold/rattle", + "kim", + "edip/omp", + "vacf", + "soft/omp", + "contact/atom/gran", + "units", + "qmmm", + "lj/cut/coul/wolf", + "lubricate/omp", + "exp6/rx", + "buck/coul/cut", + "coul/cut", + "move/mesh", + "comm_modify", + "lj/long/coul/long/omp", + "neb", + "erotate/sphere", + "lj/class2/omp", + "omega/chunk", + "lj/cut/coul/msm/omp", + "lj/gromacs/coul/gromacs/kk", + "sph/e/atom", + "coul/wolf/kk", + "molfile", + "born/coul/wolf/gpu", + "born/coul/wolf/cs/gpu", + "nve/body", + "amoeba/pitorsion", + "npt", + "lj/expand/omp", + "electron_integration", + "atom/swap", + "create", + "eff/cut", + "lj96/cut", + "saed/vtk", + "pace/kk", + "dsmc", + "rigid/meso", + "beck/gpu", + "coul/dsf/omp", + "rigid", + "npt/asphere", + "smd/adjust_dt", + "born/matrix", + "lj/charmmfsw/coul/charmmfsh", + "table", + "cosine/shift/exp/omp", + "langevin/kk", + "lj/expand", + "yukawa/colloid/gpu", + "insert/pack", + "rigid/small", + "newton", + "atomic_charge", + "dynamical_matrix", + "dpd/fdt/energy/kk", + "angle_style", + "eim", + "meam/sw/spline", + "cosine/shift/omp", + "variable", + "read_restart", + "mgpt", + "viscosity/cos", + "lj/gromacs", + "dpd/gpu", + "polymorphic", + "phonon", + "lj/gromacs/coul/gromacs/omp", + "cosine/periodic", + "coul/debye/omp", + "smd/tlsph/shape", + "lj/cut/coul/dsf", + "label", + "qbmsst", + "morse/opt", + "rigid/nve/omp", + "indent", + "eam", + "eos/cv", + "nb3b/harmonic", + "dpd/tstat/kk", + "gauss/gpu", + "mliap", + "polarize/bem/icc", + "oxrna2/hbond", + "property/atom", + "sw/intel", + "multisphere/single", + "bond/swap", + "nvt/asphere", + "efield", + "nm/cut", + "lj/spica/coul/long/gpu", + "mie/cut/gpu", + "adapt", + "lj/class2/coul/long/gpu", + "fe_md_boundary", + "grem", + "buck/omp", + "smd/hourglass/error", + "peri/pmb/omp", + "tersoff/mod/c/omp", + "bond/break", + "beck/omp", + "smd/plastic/strain/rate", + "append/atoms", + "lj/cubic", + "gromos/omp", + "nvt/gpu", + "nph/sphere/omp", + "ffl", + "for", + "uncompute", + "ti/spring", + "class2/p6", + "dynamical_matrix/kk", + "lj/class2/soft", + "oneway", + "nvt/uef", + "deform/kk", + "property/chunk", + "eam/fs", + "eam/gpu", + "harmonic/cut/omp", + "particledistribution/discrete/massbased", + "min_style fire/old", + "local/density", + "orient", + "msm", + "airebo/morse/omp", + "coul/cut/soft/omp", + "temp/partial", + "wall/gran/local", + "tip4p/long/soft/omp", + "lj/cut/coul/long/intel", + "wall/harmonic", + "tracker", + "coul/long", + "enforce2d/kk", + "cosine", + "harmonic/cut", + "quip", + "smd/ulsph/effm", + "morse/soft", + "vashishta", + "gravity/omp", + "delete_elements", + "lj/long/tip4p/long/omp", + "harmonic/kk", + "mesont/tpm", + "consistent_fe_initialization", + "gauss/cut", + "body/rounded/polyhedron", + "wall/reflect", + "zbl/gpu", + "yukawa/colloid/omp", + "table/gpu", + "edpd/temp/atom", + "saip/metal", + "flow/gauss", + "lj/cut/coul/long/soft", + "resquared/omp", + "heat/gran/conduction", + "lj/cut/dipole/long", + "temp", + "rates", + "temp/sphere", + "tip4p/long/omp", + "lj/cubic/gpu", + "nvt/kk", + "spin/dipole/cut", + "reaxff/species/kk", + "dipole/omp", + "drude", + "nphug", + "accelerate/cos", + "eam/opt", + "tad", + "temper/npt", + "tersoff/table", + "coul/dsf/gpu", + "gran/hooke/omp", + "smd/vol", + "buck/long/coul/long/omp", + "lj/sf/dipole/sf/omp", + "replicate", + "npt/body", + "mol/swap", + "gyration/shape/chunk", + "ilp/graphene/hbn", + "colloid", + "mass", + "nm/cut/coul/long", + "lj/cut/coul/cut/gpu", + "cluster/atom", + "property/global", + "numdiff/virial", + "insert/stream", + "nvt", + "lj/charmm/coul/long/soft", + "ring/omp", + "lj/cut/dipole/cut/omp", + "airebo/morse", + "initial", + "min_style quickmin", + "viscous/sphere", + "nve/dotc/langevin", + "zero", + "viscosity", + "gcmc", + "momb", + "buck/coul/long/intel", + "mvv/dpd", + "read", + "msd/molecule", + "tersoff/intel", + "coul/tt", + "molecule", + "momentum", + "hexorder/atom", + "lj/cut/coul/dsf/gpu", + "lj/charmm/coul/charmm/implicit/omp", + "tersoff/table/omp", + "reaxff", + "widom", + "mdi/qm", + "nve/sphere/kk", + "hbond/dreiding/morse/omp", + "ttm/grid", + "delete_bonds", + "drag", + "PI", + "tip4p/cut", + "sph/rhosum", + "nonlinear/omp", + "tune/kspace", + "write_coeff", + "lj/cut/coul/cut/omp", + "reset_time", + "reaxff/bonds/kk", + "poisson_solver", + "coul/long/cs/gpu", + "spring", + "opls/omp", + "print", + "lj/expand/gpu", + "morse/gpu", + "srd", + "cosine/delta", + "nve/awpmd", + "nph/asphere/omp", + "mdi", + "add_to_nodeset", + "lj/cut/tip4p/long/gpu", + "distance", + "mdpd", + "cnp/atom", + "smd", + "ehex", + "lj/class2/coul/long/omp", + "lj/cut/coul/cut/soft/omp", + "brownian/omp", + "buck/coul/cut/intel", + "drip", + "ave/atom", + "smd/hertz", + "quadrature", + "fene/nm", + "reference_potential_energy", + "nvt/omp", + "soft/gpu", + "sw/omp", + "quartic", + "oxdna2/fene", + "min_modify", + "material", + "sw/angle/table", + "sph/density/summation", + "plasticity/atom", + "cosine/omp", + "coul/cut/dielectric", + "coul/msm/omp", + "enforce2d", + "quadratic", + "kspace_modify", + "smd/contact/radius", + "class2/kk", + "lj/smooth/gpu", + "coul/long/omp", + "temp/body", + "shardlow/kk", + "fields", + "global/atom", + "oxdna2/excv", + "class2/omp", + "wall/reflect/kk", + "temp/uef", + "temp/com", + "create_box", + "lj/relres/omp", + "origin", + "nvt/body", + "python/move", + "lj/class2/coul/cut/kk", + "cvff", + "temp/rescale", + "EDGE", + "rigid/npt", + "fene", + "multi/lucy", + "fep", + "mass_matrix", + "qeq/comb", + "oxdna2/stk", + "bpm/spring", + "add_species", + "atom_modify", + "tersoff/mod/omp", + "stress/cylinder", + "angle", + "nvt/intel", + "source", + "lj/long/tip4p/long", + "sph/density/continuity", + "nve/manifold/rattle", + "smd/setvel", + "create_bonds", + "cosine/squared/omp", + "meam/spline", + "coul/cut/omp/global", + "list", + "group", + "spring/self", + "dpd", + "pour", + "peri/ves", + "qeq/dynamic", + "harmonic/intel", + "erotate/superquadric", + "particledistribution/discrete", + "displace_atoms", + "pppm/cg", + "volume_integral", + "cossq/omp", + "move", + "bond_style", + "nve/sphere", + "python/invoke", + "output", + "opls/kk", + "region", + "eam/alloy/omp", + "temp/csvr", + "ewald", + "efield/atom", + "press/berendsen", + "pimd", + "nph/body", + "torque/chunk", + "addtorque", + "lj/charmm/coul/charmm/intel", + "pace", + "rx", + "langevin/eff", + "sample_frequency", + "meam/kk", + "sw", + "charmmfsw", + "polarize/bem/gmres", + "write_data", + "smd/integrate_tlsph", + "ave/chunk", + "vector", + "lj/smooth", + "born/coul/dsf/cs", + "dpd/ext/tstat/kk", + "include", + "saed", + "lubricate/poly/omp", + "dpd/ext/tstat", + "lj/cut/coul/debye/kk", + "buck/intel", + "gauss/omp", + "hybrid/overlay/kk", + "erotate/sphere/atom", + "langevin", + "lj/cut/coul/cut/kk", + "snap/kk", + "poems", + "line/lj", + "yukawa/kk", + "sdpd/taitwater/isothermal", + "freeze", + "smd/ulsph/stress", + "sph/lj", + "table/cut", + "nve/dot", + "temperature_definition", + "python", + "pppm/disp/tip4p", + "lj/class2/coul/long", + "body/nparticle", + "nvt/sllod/omp", + "particletemplate/superquadric", + "tip4p/long", + "nve/noforce", + "beck", + "planeforce", + "change_box", + "born/coul/long/gpu", + "lj/cut/tip4p/cut/omp", + "h5md", + "lj/charmm/coul/charmm/gpu", + "kernel", + "nve/spin", + "eam/cd/old", + "born/coul/msm", + "lj/mdf", + "lj/long/coul/long/intel", + "eam/alloy", + "lj/switch3/coulgauss/long", + "sph", + "particledistribution/discrete/numberbased", + "rigid/nve", + "displace/atom", + "basal/atom", + "nve/sphere/omp", + "charmm/kk", + "improper", + "hdnnp", + "ave/spatial", + "lubricate", + "coul/long/gpu", + "buck/coul/msm/omp", + "lj/spica", + "eam/he", + "fix_modify", + "oxdna2/xstk", + "erotate", + "balance", + "lj/cut/gpu", + "mm3", + "dump", + "mie/cut", + "nph/kk", + "reaxff/species", + "ewald/disp", + "cosine/periodic/omp", + "nve/tri", + "event/displace", + "umbrella/omp", + "airebo", + "lb/viscous", + "npt/omp", + "dpd/ext", + "rigid/nvt/omp", + "image", + "sph/taitwater", + "snav/atom", + "fourier/intel", + "lj/long/coul/long/opt", + "hyper/local", + "lj/expand/coul/long", + "harmonic/shift/omp", + "store/force", + "npt/intel", + "tersoff/gpu", + "lj/gromacs/gpu", + "pe", + "true", + "erotate/multisphere", + "helix/omp", + "buck/coul/long/kk", + "nphug/omp", + "mesocnt", + "prd", + "read_data", + "oxdna/fene", + "pair_coeff", + "tersoff/mod", + "lj/class2", + "buck/coul/cut/gpu", + "lj/cut/intel", + "born/coul/wolf", + "lj/long/dipole/long", + "nvt/sllod", + "lj/cut", + "jump", + "eam/alloy/opt", + "bond_write", + "entropy/atom", + "brownian/poly", + "hybrid/scaled", + "msm/cg", + "tersoff", + "momentum/kk", + "wall/srd", + "neighbor", + "write_restart", + "lj/class2/kk", + "nve/asphere", + "dpd/intel", + "opls", + "eam/kk", + "eam/intel", + "spherical", + "comb/omp", + "min_style spin/cg", + "velocity", + "restrain", + "deform", + "fene/kk", + "coul/slater", + "echo", + "clear", + "eam/alloy/kk", + "write_atom_weights", + "multi/lucy/rx", + "chunk/spread/atom", + "lj/cut/coul/wolf/omp", + "dpd/energy", + "nve/asphere/intel", + "multi/harmonic/omp", + "evaporate", + "lj/class2/coul/long/soft", + "ave/histo", + "dielectric", + "boundary", + "sph/heatconduction", + "pair_style", + "lj/charmm/coul/long/gpu", + "restart", + "vashishta/kk", + "lj/cut/coul/long/opt", + "qeq/slater", + "remove_molecule", + "nve/superquadric", + "lj/spica/coul/msm", + "sph/stationary", + "pe/atom", + "lj/cut/tip4p/long/omp", + "fragment/atom", + "shake", + "exp6/rx/kk", + "brownian/poly/omp", + "dpd/ext/kk", + "temp/cs", + "coul/diel/omp", + "npt/sphere/omp", + "edpd", + "bond/local", + "smd/integrate_ulsph", + "heat/flux", + "shardlow", + "zbl", + "hbond/dreiding/morse", + "on", + "atom_style", + "harmonic", + "fene/expand/omp", + "equilibrium_start", + "born/omp", + "addforce", + "ti", + "temp/rescale/eff", + "snad/atom", + "lj/cut/coul/cut", + "latte", + "type", + "delete_atoms", + "timestep", + "smd/tlsph/strain/rate", + "wall/piston", + "spin", + "gayberne/gpu", + "smd/tlsph", + "slice", + "born/coul/wolf/omp", + "cosine/squared", + "lj/cut/coul/msm/dielectric", + "create_elementset", + "agni", + "dpd/fdt/energy", + "property/molecule", + "dihedral", + "ke", + "nm/cut/coul/cut/omp", + "lj/sf/dipole/sf/gpu", + "lj/cut/coul/dsf/kk", + "coul/debye/gpu", + "min_style cg", + "gyration/shape", + "time_integration", + "dpd/atom", + "tersoff/omp", + "gw", + "improper_style", + "plane", + "lj/cut/soft/omp", + "set", + "lj/cut/tip4p/long/soft/omp", + "mask_direction", + "coul/dsf/kk", + "exchange", + "temp/asphere", + "lj/cut/coul/long/gpu", + "born", + "fix_flux", + "lj/charmm/coul/long/intel", + "spin/dmi", + "box/relax", + "elif", + "charmm", + "ufm", + "class2", + "quartic/omp", + "box", + "info", + "gyration", + "couple/cfd", + "spring/chunk", + "tip4p/cut/omp", + "sna/atom", + "undump", + "nve", + "lattice", + "qeq/reaxff", + "qtb", + "wall/reflect/stochastic", + "gyration/molecule", + "buck/coul/long", + "nparticles/tracer/region", + "fourier", + "timer", + "gran/hertz/history", + "smd/wall_surface", + "soft", + "lj/spica/coul/long", + "cvff/omp", + "com/molecule", + "angle_coeff", + "lubricate/poly", + "lj/cut/tip4p/long", + "lj/charmm/coul/charmm/implicit/kk", + "lj/cut/coul/long", + "spin/neel", + "msd/nongauss", + "charmm/omp", + "INF", + "oxrna2/stk", + "massflow/mesh", + "reduce/region", + "lj/cut/coul/debye/dielectric", + "lj/class2/coul/cut", + "npt/asphere/omp", + "create_atoms", + "comb", + "bond/create", + "langevin/spin", + "hyper/global", + "lj/expand/kk", + "boundary_dynamics", + "lj/cut/tip4p/cut", + "package", + "bpm/rotational", + "third_order", + "lj/expand/coul/long/gpu", + "coul/long/kk", + "lj/smooth/omp", + "write", + "buck/mdf", + "coul/dsf", + "coord/atom/kk", + "property/atom/tracer", + "eos/table/rx", + "ave/sphere/atom", + "communicate", + "nodeset", + "comb3", + "dihedral_style", + "buck/coul/long/omp", + "coord/gran", + "sph/pressure", + "acks2/reaxff/kk", + "tfmc", + "electron/stopping", + "extep", + "rigid/small/omp", + "xrd", + "orientorder/atom/kk", + "dpd/energy/kk", + "add_molecule", + "spin/magelec", + "distharm", + "stress/atom", + "rigid/nph/omp", + "cosine/shift/exp", + "qeq/shielded", + "rigid/omp", + "dpd/omp", + "log", + "cosine/delta/omp", + "property/local", + "ke/rigid", + "acks2/reaxff", + "shell", + "hyper", + "reduce", + "coul/exclude", + "centro/atom", + "eam/alloy/intel", + "yukawa/omp", + "no", + "dipole/chunk", + "next", + "fene/omp", + "coul/cut/gpu", + "temp/profile", + "zbl/omp", + "remove_source", + "reset_mol_ids", + "adp/omp", + "ptm/atom", + "rattle", + "fourier/simple", + "cvff/intel", + "coul/debye/kk", + "sw/gpu", + "none", + "ave/correlate/long", + "inertia/chunk", + "born/gpu", + "lj/sf/dipole/sf", + "npt/cauchy", + "tmd", + "lj/cut/dipole/long/gpu", + "drude/transform/direct", + "oxrna2/coaxstk", + "nvt/asphere/omp", + "ufm/omp", + "heat/flux/tally", + "oxdna/excv", + "ufm/opt", + "gran/hooke/history/omp", + "nonlinear", + "lj/spica/gpu", + "vashishta/omp", + "npt/gpu", + "atm", + "smd/tlsph/dt", + "edpd/source", + "numdiff", + "thermo_style", + "lj/cubic/omp", + "recenter", + "table/kk", + "brownian/sphere", + "multicontact/halfspace", + "lj/charmm/coul/msm/omp", + "lb/fluid", + "thermal/conductivity", + "coord/atom", + "tri/lj", + "eos/table", + "processors", + "scale", + "lj/cut/omp", + "adp", + "nharmonic/omp", + "nvt/sllod/eff", + "oxdna2/coaxstk", + "ackland/atom", + "lj/gromacs/kk", + "nvt/eff", + "erotate/rigid", + "mesont", + "born/coul/msm/omp", + "labelmap", + "born/coul/long/omp", + "gran/hertz/history/omp", + "tersoff/kk", + "create_nodeset", + "decomposition", + "bop", + "reduce/chunk", + "sw/kk", + "property/atom/regiontracer/time", + "temp/region", + "reset_atomic_reference_positions", + "lj96/cut/omp", + "buck/coul/long/gpu", + "quadratic/omp", + "resquared/gpu", + "dt/reset", + "atom_weight", + "nvk", + "nve/limit", + "unfix_flux", + "ufm/gpu", + "fourier/omp", + "comm_style", + "rigid/nvt", + "chunk/atom", + "vashishta/table", + "nph/sphere", + "sph/t/atom", + "mscg", + "filter/corotate", + "temper/grem", + "min_style hftn", + "npt/kk", + "dpd/tstat/omp", + "dilatation/atom", + "buck6d/coul/gauss/dsf", + "mesh/surface/planar", + "langevin/drude", + "fourier/simple/omp", + "lj/smooth/linear/omp", + "dpd/tstat/gpu", + "nm/cut/omp", + "pair_interactions", + "lj/cut/tip4p/long/opt", + "netcdf", + "remove_species", + "eos/table/rx/kk", + "smatb", + "table/rx/kk", + "neighbor_skin", + "ilp/tmd", + "hma", + "buck/coul/cut/kk", + "buck/kk", + "ave/time", + "quit", + "gran/hooke/history/kk", + "harmonic/shift/cut", + "coul/wolf/cs", + "thermo_modify", + "coul/debye", + "tersoff/zbl/kk", + "gyration/chunk", + "lj/class2/coul/cut/omp", + "particletemplate/multisphere", + "oxdna/stk", + "rerun", + "fix", + "rigid/nvt/small", + "viscous", + "atom_element_map", + "temp/kk", + "born/coul/long", + "wall/gran", + "wall/region/sph", + "pe/mol/tally", + "wall/lj126", + "charmm/intel", + "temp/berendsen", + "else", + "gayberne/intel", + "false", + "oxrna2/xstk", + "wall/colloid", + "improper/local", + "npt/sphere", + "internal_quadrature", + "nve/eff", + "gromos", + ], - typeKeywords: [], + typeKeywords: [], - operators: [ - "=", - ">", - "<", - "!", - "~", - "?", - ":", - "==", - "<=", - ">=", - "!=", - "&&", - "||", - "++", - "--", - "+", - "-", - "*", - "/", - "&", - "|", - "^", - "%", - "<<", - ">>", - ">>>", - "+=", - "-=", - "*=", - "/=", - "&=", - "|=", - "^=", - "%=", - "<<=", - ">>=", - ">>>=", - ], + operators: [ + "=", + ">", + "<", + "!", + "~", + "?", + ":", + "==", + "<=", + ">=", + "!=", + "&&", + "||", + "++", + "--", + "+", + "-", + "*", + "/", + "&", + "|", + "^", + "%", + "<<", + ">>", + ">>>", + "+=", + "-=", + "*=", + "/=", + "&=", + "|=", + "^=", + "%=", + "<<=", + ">>=", + ">>>=", + ], - // we include these common regular expressions - symbols: /[=>](?!@symbols)/, "@brackets"], - [/@symbols/, { cases: { "@operators": "operator", "@default": "" } }], + // delimiters and operators + [/[{}()[\]]/, "@brackets"], + [/[<>](?!@symbols)/, "@brackets"], + [/@symbols/, { cases: { "@operators": "operator", "@default": "" } }], - // @ annotations. - // As an example, we emit a debugging log message on these tokens. - // Note: message are supressed during the first load -- change some lines to see them. + // @ annotations. + // As an example, we emit a debugging log message on these tokens. + // Note: message are supressed during the first load -- change some lines to see them. - // numbers - [/\d*\.\d+([eE][-+]?\d+)?/, "number.float"], - [/0[xX][0-9a-fA-F]+/, "number.hex"], - [/\d+/, "number"], + // numbers + [/\d*\.\d+([eE][-+]?\d+)?/, "number.float"], + [/0[xX][0-9a-fA-F]+/, "number.hex"], + [/\d+/, "number"], - // delimiter: after number because of .\d floats - [/[;,.]/, "delimiter"], + // delimiter: after number because of .\d floats + [/[;,.]/, "delimiter"], - // strings - [/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string - [/"/, { token: "string.quote", bracket: "@open", next: "@string" }], + // strings + [/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string + [/"/, { token: "string.quote", bracket: "@open", next: "@string" }], - // characters - [/'[^\\']'/, "string"], - [/(')(@escapes)(')/, ["string", "string.escape", "string"]], - [/'/, "string.invalid"], - ], + // characters + [/'[^\\']'/, "string"], + [/(')(@escapes)(')/, ["string", "string.escape", "string"]], + [/'/, "string.invalid"], + ], - comment: [ - [/[^/*]+/, "comment"], - [/\/\*/, "comment", "@push"], // nested comment - ["\\*/", "comment", "@pop"], - [/[/*]/, "comment"], - [/^#/, "comment"], - ], + comment: [ + [/[^/*]+/, "comment"], + [/\/\*/, "comment", "@push"], // nested comment + ["\\*/", "comment", "@pop"], + [/[/*]/, "comment"], + [/^#/, "comment"], + ], - string: [ - [/[^\\"]+/, "string"], - [/@escapes/, "string.escape"], - [/\\./, "string.escape.invalid"], - [/"/, { token: "string.quote", bracket: "@close", next: "@pop" }], - ], + string: [ + [/[^\\"]+/, "string"], + [/@escapes/, "string.escape"], + [/\\./, "string.escape.invalid"], + [/"/, { token: "string.quote", bracket: "@close", next: "@pop" }], + ], - whitespace: [ - [/[ \t\r\n]+/, "white"], - [/\/\*/, "comment", "@comment"], - [/\/\/.*$/, "comment"], - ], - }, + whitespace: [ + [/[ \t\r\n]+/, "white"], + [/\/\*/, "comment", "@comment"], + [/\/\/.*$/, "comment"], + ], + }, }); }); @@ -1419,24 +1418,19 @@ const Edit = () => { selectOnLineNumbers: true, }; - const handleEditorDidMount = useCallback( - (editor: Monaco.editor.IStandaloneCodeEditor) => { - editor.focus(); - }, - [], - ); + const handleEditorDidMount = useCallback((editor: Monaco.editor.IStandaloneCodeEditor) => { + editor.focus(); + }, []); const onEditorChange = useCallback( (newValue: string | undefined) => { if (!newValue) return; - const file = simulation?.files.find( - (file) => file.fileName === selectedFile?.fileName, - ); + const file = simulation?.files.find((file) => file.fileName === selectedFile?.fileName); if (file) { file.content = newValue; } }, - [selectedFile?.fileName, simulation?.files], + [selectedFile?.fileName, simulation?.files] ); if (!selectedFile) { diff --git a/src/containers/Examples.css b/src/containers/Examples.css index a178e6f7..918035b6 100644 --- a/src/containers/Examples.css +++ b/src/containers/Examples.css @@ -2,9 +2,14 @@ .modern-card { border-radius: 16px; overflow: hidden; - transition: transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease; + transition: + transform 0.3s ease, + box-shadow 0.3s ease, + border-color 0.3s ease; border: 1px solid rgba(255, 255, 255, 0.1); - box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.2), 0 8px 32px rgba(0, 0, 0, 0.1); + box-shadow: + inset 0 0 20px rgba(0, 0, 0, 0.2), + 0 8px 32px rgba(0, 0, 0, 0.1); background: rgba(42, 41, 47, 0.6); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); @@ -20,11 +25,7 @@ left: 0; width: 100%; height: 100%; - background: linear-gradient( - to bottom, - rgba(255, 255, 255, 0.15) 0%, - transparent 50% - ); + background: linear-gradient(to bottom, rgba(255, 255, 255, 0.15) 0%, transparent 50%); pointer-events: none; opacity: 0.5; z-index: 1; @@ -34,7 +35,9 @@ .modern-card:hover { transform: translateY(-4px); - box-shadow: inset 0 0 30px rgba(0, 0, 0, 0.3), 0 12px 40px rgba(0, 0, 0, 0.2); + box-shadow: + inset 0 0 30px rgba(0, 0, 0, 0.3), + 0 12px 40px rgba(0, 0, 0, 0.2); border-color: rgba(255, 255, 255, 0.2); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); @@ -220,7 +223,7 @@ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 16px; } - + .card-image { height: 160px; } diff --git a/src/containers/Examples.tsx b/src/containers/Examples.tsx index 6096bffe..a4defcfe 100644 --- a/src/containers/Examples.tsx +++ b/src/containers/Examples.tsx @@ -1,15 +1,14 @@ -import { useCallback, useState, useEffect } from "react"; -import { Select, Divider } from "antd"; -import { Simulation } from "../store/simulation"; -import { SimulationFile } from "../store/app"; -import { useStoreActions, useStoreState } from "../hooks"; import { CaretRightOutlined, EditOutlined } from "@ant-design/icons"; -import { Layout, Skeleton, notification } from "antd"; -import { track } from "../utils/metrics"; +import { Divider, Layout, notification, Select, Skeleton } from "antd"; +import { useCallback, useEffect, useState } from "react"; import ReactMarkdown from "react-markdown"; -import remarkMath from "remark-math"; -import rehypeKatex from "rehype-katex"; import rehypeExternalLinks from "rehype-external-links"; +import rehypeKatex from "rehype-katex"; +import remarkMath from "remark-math"; +import { useStoreActions, useStoreState } from "../hooks"; +import type { SimulationFile } from "../store/app"; +import type { Simulation } from "../store/simulation"; +import { track } from "../utils/metrics"; import "katex/dist/katex.min.css"; import "./Examples.css"; @@ -36,14 +35,10 @@ const Examples = () => { const [examples, setExamples] = useState([]); const [filterKeywords, setFilterKeywords] = useState([]); // Width measurement no longer needed with CSS Grid - const setNewSimulation = useStoreActions( - (actions) => actions.simulation.newSimulation, - ); + const setNewSimulation = useStoreActions((actions) => actions.simulation.newSimulation); const simulation = useStoreState((state) => state.simulation.simulation); const running = useStoreState((state) => state.simulation.running); - const setPreferredView = useStoreActions( - (actions) => actions.app.setPreferredView, - ); + const setPreferredView = useStoreActions((actions) => actions.app.setPreferredView); useEffect(() => { const fetchExamples = async (examplesUrl: string) => { @@ -75,7 +70,7 @@ const Examples = () => { const urlSearchParams = new URLSearchParams(window.location.search); const params = Object.fromEntries(urlSearchParams.entries()); - let defaultExamplesUrl = "examples/examples.json"; + const defaultExamplesUrl = "examples/examples.json"; let examplesUrl = defaultExamplesUrl; if (params["examplesUrl"] != null) { examplesUrl = params["examplesUrl"]; @@ -106,15 +101,14 @@ const Examples = () => { if (running) { notification.info({ message: "Simulation already running", - description: - "You can't start a new simulation while another one is running.", + description: "You can't start a new simulation while another one is running.", }); } else { setNewSimulation(newSimulation); setPreferredView("view"); } }, - [running, setNewSimulation, setPreferredView], + [running, setNewSimulation, setPreferredView] ); const onEdit = useCallback( @@ -132,10 +126,10 @@ const Examples = () => { setPreferredView("file" + newSimulation.inputScript); } }, - [setNewSimulation, setPreferredView, simulation?.id], + [setNewSimulation, setPreferredView, simulation?.id] ); - let keywordsSet: Set = new Set(); + const keywordsSet: Set = new Set(); examples.forEach((example) => { if (example.keywords) { example.keywords.forEach((keyword) => keywordsSet.add(keyword)); @@ -145,19 +139,11 @@ const Examples = () => { keywords.sort(); const renderCard = (example: Example) => ( -
onPlay(example)} - > +
onPlay(example)}>
- {example.title} + {example.title}
- -
-
{example.title}
-
{example.description}
+
+ {example.title} +
+
+ {example.description} +
{example.author && ( diff --git a/src/containers/Main.tsx b/src/containers/Main.tsx index da93d355..ecacbb55 100644 --- a/src/containers/Main.tsx +++ b/src/containers/Main.tsx @@ -1,13 +1,14 @@ -import { Modal, Tabs, Progress, Button, Layout } from "antd"; -import { useState, useEffect, useMemo } from "react"; -import View from "./View"; -import Notebook from "./Notebook"; -import Edit from "./Edit"; +import { Button, Layout, Modal, Progress, Tabs } from "antd"; +import { useEffect, useMemo, useState } from "react"; +import LoadingSimulationScreen from "../components/LoadingSimulationScreen"; +import { useStoreActions, useStoreState } from "../hooks"; import Console from "./Console"; +import Edit from "./Edit"; import Examples from "./Examples"; +import Notebook from "./Notebook"; import RunInCloud from "./RunInCloud"; -import LoadingSimulationScreen from "../components/LoadingSimulationScreen"; -import { useStoreActions, useStoreState } from "../hooks"; +import View from "./View"; + const { Content } = Layout; const Main = ({ isEmbedded }: { isEmbedded: boolean }) => { @@ -15,17 +16,13 @@ const Main = ({ isEmbedded }: { isEmbedded: boolean }) => { const showConsole = useStoreState((state) => state.simulation.showConsole); const [consoleKey, setConsoleKey] = useState(0); const [hasStarted, setHasStarted] = useState(false); - const setShowConsole = useStoreActions( - (actions) => actions.simulation.setShowConsole, - ); + const setShowConsole = useStoreActions((actions) => actions.simulation.setShowConsole); const selectedMenu = useStoreState((state) => state.app.selectedMenu); const running = useStoreState((state) => state.simulation.running); - const setPreferredView = useStoreActions( - (actions) => actions.app.setPreferredView, - ); + const setPreferredView = useStoreActions((actions) => actions.app.setPreferredView); const status = useStoreState((state) => state.app.status); - + // Update console key when modal opens useEffect(() => { if (showConsole) { @@ -46,11 +43,12 @@ const Main = ({ isEmbedded }: { isEmbedded: boolean }) => { { key: "view", label: "View", - children: isEmbedded && !hasStarted ? ( - - ) : ( - - ), + children: + isEmbedded && !hasStarted ? ( + + ) : ( + + ), }, { key: "console", @@ -80,9 +78,7 @@ const Main = ({ isEmbedded }: { isEmbedded: boolean }) => { ]; // Filter out Examples tab in embedded mode - return isEmbedded - ? allTabs.filter(tab => tab.key !== "examples") - : allTabs; + return isEmbedded ? allTabs.filter((tab) => tab.key !== "examples") : allTabs; }, [isEmbedded, selectedMenu, hasStarted, status, wasm]); return ( diff --git a/src/containers/NewSimulation.tsx b/src/containers/NewSimulation.tsx index 1724975f..52f41185 100644 --- a/src/containers/NewSimulation.tsx +++ b/src/containers/NewSimulation.tsx @@ -1,21 +1,11 @@ import { InboxOutlined } from "@ant-design/icons"; +import type { UploadFile, UploadProps } from "antd"; +import { Button, Checkbox, Divider, Input, Modal, message, Select, Tooltip, Upload } from "antd"; +import type { UploadChangeParam } from "antd/es/upload"; import { useCallback, useEffect, useState } from "react"; import { useStoreActions } from "../hooks"; -import { - message, - Upload, - Modal, - Button, - Select, - Divider, - Tooltip, - Input, - Checkbox, -} from "antd"; -import type { UploadProps, UploadFile } from "antd"; -import type { UploadChangeParam } from "antd/es/upload"; -import { Simulation } from "../store/simulation"; -import { SimulationFile } from "../store/app"; +import type { SimulationFile } from "../store/app"; +import type { Simulation } from "../store/simulation"; import { track } from "../utils/metrics"; const { Option } = Select; @@ -31,12 +21,8 @@ const NewSimulation = ({ onClose }: NewSimulationProps) => { const [files, setFiles] = useState([]); const [startImmediately, setStartImmediately] = useState(false); const [inputScript, setInputScript] = useState(); - const setNewSimulation = useStoreActions( - (actions) => actions.simulation.newSimulation, - ); - const setPreferredView = useStoreActions( - (actions) => actions.app.setPreferredView, - ); + const setNewSimulation = useStoreActions((actions) => actions.simulation.newSimulation); + const setPreferredView = useStoreActions((actions) => actions.app.setPreferredView); const validSimulation = name != null && @@ -52,9 +38,7 @@ const NewSimulation = ({ onClose }: NewSimulationProps) => { async (info: UploadChangeParam) => { // Need to use window.files because these async functions can't seem to agree on the state if (info.file.status === "removed") { - const newFiles = files.filter( - (file) => file.fileName !== info.file.name, - ); + const newFiles = files.filter((file) => file.fileName !== info.file.name); window.files = newFiles; setFiles(newFiles); return; @@ -72,7 +56,7 @@ const NewSimulation = ({ onClose }: NewSimulationProps) => { setFiles(window.files); message.success(`${file.fileName} uploaded successfully.`); }, - [files, setFiles], + [files, setFiles] ); const props: UploadProps = { @@ -105,15 +89,7 @@ const NewSimulation = ({ onClose }: NewSimulationProps) => { setPreferredView("view"); } onClose(); - }, [ - files, - inputScript, - name, - onClose, - startImmediately, - setPreferredView, - setNewSimulation, - ]); + }, [files, inputScript, name, onClose, startImmediately, setPreferredView, setNewSimulation]); return ( {

Name

- setName(e.target.value)} - placeholder="Simulation name" - > + setName(e.target.value)} placeholder="Simulation name">

Files @@ -162,12 +135,10 @@ const NewSimulation = ({ onClose }: NewSimulationProps) => {

-

- Click or drag file to this area to upload -

+

Click or drag file to this area to upload

- Upload the files you need for your simulation. Note that these files - are not stored across Atomify sessions yet. + Upload the files you need for your simulation. Note that these files are not stored across + Atomify sessions yet.

{files.length > 0 && ( @@ -176,10 +147,7 @@ const NewSimulation = ({ onClose }: NewSimulationProps) => {

Select input script

- setInputScript(value)}> {files.map((file) => ( ))} diff --git a/src/containers/Notebook.tsx b/src/containers/Notebook.tsx index 4dfe95d1..43c6778d 100644 --- a/src/containers/Notebook.tsx +++ b/src/containers/Notebook.tsx @@ -1,7 +1,7 @@ +import localforage from "localforage"; +import { useEffect, useState } from "react"; import Iframe from "react-iframe"; import { useStoreState } from "../hooks"; -import { useEffect, useState } from "react"; -import localforage from "localforage"; const Notebook = () => { const simulation = useStoreState((state) => state.simulation.simulation); @@ -15,7 +15,9 @@ const Notebook = () => { let path = "analyze.ipynb"; if (simulation?.analysisScript) { - const scriptName = simulation.analysisScript.substring(simulation.analysisScript.lastIndexOf('/') + 1); + const scriptName = simulation.analysisScript.substring( + simulation.analysisScript.lastIndexOf("/") + 1 + ); path = `${simulation.id}/${scriptName}`; } diff --git a/src/containers/RunInCloud.tsx b/src/containers/RunInCloud.tsx index 81ac11e1..14c9271d 100644 --- a/src/containers/RunInCloud.tsx +++ b/src/containers/RunInCloud.tsx @@ -1,14 +1,7 @@ -import { - Layout, - Checkbox, - Select, - Input, - Form, - Button, - notification, -} from "antd"; -import { useState, useCallback } from "react"; +import { Button, Checkbox, Form, Input, Layout, notification, Select } from "antd"; +import { useCallback, useState } from "react"; import { track } from "../utils/metrics"; + const { Header } = Layout; const RunInCloud = () => { @@ -46,18 +39,16 @@ const RunInCloud = () => { Run in cloud
- Running simulations in the browser is a fantastic way to get started - with LAMMPS and can be used to run smaller simulations. However, - simulations often require more powerful compute power than what is - available in the browser. We are considering adding cloud support, so - that you can test your simulations in the browser and then run them on - high performance computers in the cloud. Before adding this feature, we - want to gather more information about the interest. + Running simulations in the browser is a fantastic way to get started with LAMMPS and can be + used to run smaller simulations. However, simulations often require more powerful compute + power than what is available in the browser. We are considering adding cloud support, so + that you can test your simulations in the browser and then run them on high performance + computers in the cloud. Before adding this feature, we want to gather more information about + the interest.

- We would appreaciate if you could fill in this form. It would be of - great help to decide on whether or not this is a good idea, and how to - do it right. + We would appreaciate if you could fill in this form. It would be of great help to decide on + whether or not this is a good idea, and how to do it right.

{ style={{ maxWidth: 600 }} > - setWouldUse(e.target.checked)}> - I would buy this - + setWouldUse(e.target.checked)}>I would buy this How many CPU cores would you want? @@ -97,18 +86,10 @@ const RunInCloud = () => { defaultValue={"n/a"} > Does not matter - - Low end GPU (NVIDIA K80) - - - Medium end GPU (NVIDIA P100) - - - High end GPU (NVIDIA V100) - - - Very high end GPU (NVIDIA A100) - + Low end GPU (NVIDIA K80) + Medium end GPU (NVIDIA P100) + High end GPU (NVIDIA V100) + Very high end GPU (NVIDIA A100) {gpuType !== "nogpu" && ( diff --git a/src/containers/Settings.tsx b/src/containers/Settings.tsx index 405c42b0..833c20ca 100644 --- a/src/containers/Settings.tsx +++ b/src/containers/Settings.tsx @@ -1,6 +1,6 @@ -import { Modal, Checkbox, Slider, Tabs, Table, Collapse } from "antd"; -import { useStoreState, useStoreActions } from "../hooks"; +import { Checkbox, Collapse, Modal, Slider, Table, Tabs } from "antd"; import type { ColumnsType } from "antd/es/table"; +import { useStoreActions, useStoreState } from "../hooks"; import { track } from "../utils/metrics"; interface SettingsProps { @@ -10,9 +10,7 @@ interface SettingsProps { const Settings = ({ open, onClose }: SettingsProps) => { const renderSettings = useStoreState((state) => state.settings.render); - const setRenderSettings = useStoreActions( - (actions) => actions.settings.setRender, - ); + const setRenderSettings = useStoreActions((actions) => actions.settings.setRender); interface KeyboardShortcutsDataType { key: React.Key; @@ -128,11 +126,7 @@ const Settings = ({ open, onClose }: SettingsProps) => { - handleSettingChange( - "orthographic", - e.target.checked, - "Settings.Render.Orthographic" - ) + handleSettingChange("orthographic", e.target.checked, "Settings.Render.Orthographic") } > Orthographic camera @@ -148,8 +142,7 @@ const Settings = ({ open, onClose }: SettingsProps) => { children: ( <>
- Ambient Light Intensity:{" "} - {renderSettings.ambientLightIntensity.toFixed(2)} + Ambient Light Intensity: {renderSettings.ambientLightIntensity.toFixed(2)} { />
- Point Light Intensity:{" "} - {renderSettings.pointLightIntensity.toFixed(0)} + Point Light Intensity: {renderSettings.pointLightIntensity.toFixed(0)} { - handleSettingChange( - "ssao", - e.target.checked, - "Settings.Render.SSAO" - ) + handleSettingChange("ssao", e.target.checked, "Settings.Render.SSAO") } > Enable SSAO @@ -212,11 +200,7 @@ const Settings = ({ open, onClose }: SettingsProps) => { step={0.5} value={renderSettings.ssaoRadius} onChange={(value) => - handleSettingChange( - "ssaoRadius", - value, - "Settings.Render.SSAORadius" - ) + handleSettingChange("ssaoRadius", value, "Settings.Render.SSAORadius") } />
@@ -248,13 +232,7 @@ const Settings = ({ open, onClose }: SettingsProps) => { }; return ( - onClose()} - > + onClose()}> = ({ - visible, - onClose, - simulation, -}) => { +const ShareSimulation: React.FC = ({ visible, onClose, simulation }) => { const [shareUrl, setShareUrl] = useState(""); const [loading, setLoading] = useState(false); const [copied, setCopied] = useState(false); @@ -31,33 +28,33 @@ const ShareSimulation: React.FC = ({ setError(undefined); try { const encodedData = await encodeSimulation(simulation); - + // Track with file details - const fileNames = simulation.files.map(f => f.fileName); - track("ShareSimulation.Generate", { + const fileNames = simulation.files.map((f) => f.fileName); + track("ShareSimulation.Generate", { simulationId: simulation.id, embedMode, autoStart, fileNames, fileCount: simulation.files.length, - urlLength: encodedData.length // Just the encoded data part + urlLength: encodedData.length, // Just the encoded data part }); - + // Get the base URL (without query parameters) const baseUrl = `${window.location.origin}${window.location.pathname}`; - const embedParam = embedMode ? '&embed=true' : ''; + const embedParam = embedMode ? "&embed=true" : ""; // When embedMode is true, autoStart is always true const effectiveAutoStart = embedMode || autoStart; - const autoStartParam = effectiveAutoStart ? '&autostart=true' : ''; + const autoStartParam = effectiveAutoStart ? "&autostart=true" : ""; const url = `${baseUrl}?data=${encodedData}${embedParam}${autoStartParam}`; - + setShareUrl(url); } catch (err) { console.error("Error generating share URL:", err); setError(`Failed to generate share URL: ${err instanceof Error ? err.message : String(err)}`); - track("ShareSimulation.Error", { + track("ShareSimulation.Error", { simulationId: simulation.id, - error: err instanceof Error ? err.message : String(err) + error: err instanceof Error ? err.message : String(err), }); } finally { setLoading(false); @@ -106,9 +103,8 @@ const ShareSimulation: React.FC = ({ >

- Share this simulation by copying the URL below. The entire simulation - (including all files) is encoded in the URL, so no external hosting is - required. + Share this simulation by copying the URL below. The entire simulation (including all + files) is encoded in the URL, so no external hosting is required.

@@ -123,13 +119,10 @@ const ShareSimulation: React.FC = ({
- setEmbedMode(e.target.checked)} - > - Embedded mode: Share in full screen, embedded mode. Use this for presentations, - embedding in websites, or sharing with users who just want to view - the simulation. + setEmbedMode(e.target.checked)}> + Embedded mode: Share in full screen, embedded mode. Use this for + presentations, embedding in websites, or sharing with users who just want to view the + simulation.
@@ -184,16 +177,10 @@ const ShareSimulation: React.FC = ({ )} {!loading && !shareUrl && error && ( - + )}
); }; export default ShareSimulation; - diff --git a/src/containers/View.tsx b/src/containers/View.tsx index b61e6851..6fbc96c1 100644 --- a/src/containers/View.tsx +++ b/src/containers/View.tsx @@ -1,35 +1,29 @@ -import { useEffect, useState, useRef, useCallback } from "react"; -import { Layout, Row, Col, Progress, Modal, Button } from "antd"; - -import { useStoreState, useStoreActions } from "../hooks"; -import { Particles, Bonds, Visualizer, ParticleClickEvent } from "omovi"; +import { Button, Col, Layout, Modal, Progress, Row } from "antd"; +import { type Bonds, type ParticleClickEvent, type Particles, Visualizer } from "omovi"; +import { useCallback, useEffect, useRef, useState } from "react"; +import styled from "styled-components"; +import * as THREE from "three"; +import ColorLegend from "../components/ColorLegend"; import ResponsiveSimulationSummary from "../components/ResponsiveSimulationSummary"; import SelectedAtomsInfo from "../components/SelectedAtomsInfo"; -import ColorLegend from "../components/ColorLegend"; -import ColorModifierSettings from "../modifiers/ColorModifierSettings"; -import styled from "styled-components"; -import { track } from "../utils/metrics"; +import { useStoreActions, useStoreState } from "../hooks"; import { useEmbeddedMode } from "../hooks/useEmbeddedMode"; -import ColorModifier from "../modifiers/colormodifier"; -import * as THREE from "three"; +import ColorModifierSettings from "../modifiers/ColorModifierSettings"; +import type ColorModifier from "../modifiers/colormodifier"; import { - createBoxGeometry, calculateBoxRadius, + createBoxGeometry, getSimulationBoxBounds, } from "../utils/boxGeometry"; +import { track } from "../utils/metrics"; // Type guard for Visualizer with updateCameraPlanes method interface VisualizerWithCameraPlanes extends Visualizer { updateCameraPlanes: (box: THREE.Box3) => void; } -function visualizerHasCameraPlanes( - v: Visualizer, -): v is VisualizerWithCameraPlanes { - return ( - "updateCameraPlanes" in v && - typeof (v as any).updateCameraPlanes === "function" - ); +function visualizerHasCameraPlanes(v: Visualizer): v is VisualizerWithCameraPlanes { + return "updateCameraPlanes" in v && typeof (v as any).updateCameraPlanes === "function"; } const { Header } = Layout; @@ -46,7 +40,7 @@ const VisualizerWrapper = styled.div` `; const MOBILE_BREAKPOINT = 900; -const SIMULATION_SUMMARY_DRAWER_VISIBLE_KEY = 'simulationSummaryDrawerVisible'; +const SIMULATION_SUMMARY_DRAWER_VISIBLE_KEY = "simulationSummaryDrawerVisible"; const View = ({ visible, isEmbeddedMode = false }: ViewProps) => { const [loading, setLoading] = useState(false); @@ -58,13 +52,13 @@ const View = ({ visible, isEmbeddedMode = false }: ViewProps) => { const [isOverlayCollapsed, setIsOverlayCollapsed] = useState(isMobile); const [selectedAtoms, setSelectedAtoms] = useState>(new Set()); const { embedConfig } = useEmbeddedMode(); - + // Initialize from localStorage, defaulting to false (show overlay, not expanded) // Embedded mode override is handled in useEffect below const [showAnalyze, setShowAnalyze] = useState(() => { - if (typeof window !== 'undefined' && window.localStorage) { + if (typeof window !== "undefined" && window.localStorage) { const stored = localStorage.getItem(SIMULATION_SUMMARY_DRAWER_VISIBLE_KEY); - return stored !== null ? stored === 'true' : false; + return stored !== null ? stored === "true" : false; } return false; }); @@ -79,8 +73,8 @@ const View = ({ visible, isEmbeddedMode = false }: ViewProps) => { setIsOverlayCollapsed(newIsMobile); } }; - window.addEventListener('resize', handleResize); - return () => window.removeEventListener('resize', handleResize); + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); }, [isMobile]); // Handle embedded mode override for drawer visibility @@ -93,58 +87,44 @@ const View = ({ visible, isEmbeddedMode = false }: ViewProps) => { // Persist drawer visibility to localStorage (only when not in embedded mode) useEffect(() => { - if (!isEmbeddedMode && typeof window !== 'undefined' && window.localStorage) { + if (!isEmbeddedMode && typeof window !== "undefined" && window.localStorage) { localStorage.setItem(SIMULATION_SUMMARY_DRAWER_VISIBLE_KEY, showAnalyze.toString()); } }, [showAnalyze, isEmbeddedMode]); // const simulationBox = useStoreState(state => state.simulation.simulationBox) // const simulationOrigo = useStoreState(state => state.simulation.simulationOrigo) - const cameraPosition = useStoreState( - (state) => state.simulation.cameraPosition, - ); + const cameraPosition = useStoreState((state) => state.simulation.cameraPosition); const cameraTarget = useStoreState((state) => state.simulation.cameraTarget); const particles = useStoreState((state) => state.render.particles); const bonds = useStoreState((state) => state.render.bonds); const visualizer = useStoreState((state) => state.render.visualizer); - const setVisualizer = useStoreActions( - (actions) => actions.render.setVisualizer, - ); + const setVisualizer = useStoreActions((actions) => actions.render.setVisualizer); const renderSettings = useStoreState((state) => state.settings.render); - const setRenderSettings = useStoreActions( - (actions) => actions.settings.setRender, - ); + const setRenderSettings = useStoreActions((actions) => actions.settings.setRender); const domElement = useRef(null); const running = useStoreState((state) => state.simulation.running); const simulation = useStoreState((state) => state.simulation.simulation); - const runTotalTimesteps = useStoreState( - (state) => state.simulationStatus.runTotalTimesteps, - ); - const runTimesteps = useStoreState( - (state) => state.simulationStatus.runTimesteps, - ); - const timesteps = useStoreState( - (state) => state.simulationStatus.timesteps, - ); + const runTotalTimesteps = useStoreState((state) => state.simulationStatus.runTotalTimesteps); + const runTimesteps = useStoreState((state) => state.simulationStatus.runTimesteps); + const timesteps = useStoreState((state) => state.simulationStatus.timesteps); const simulationBox = useStoreState((state) => state.simulationStatus.box); - const simulationOrigo = useStoreState( - (state) => state.simulationStatus.origo, - ); - const postTimestepModifiers = useStoreState( - (state) => state.processing.postTimestepModifiers, - ); + const simulationOrigo = useStoreState((state) => state.simulationStatus.origo); + const postTimestepModifiers = useStoreState((state) => state.processing.postTimestepModifiers); const computes = useStoreState((state) => state.simulationStatus.computes); const fixes = useStoreState((state) => state.simulationStatus.fixes); const variables = useStoreState((state) => state.simulationStatus.variables); const boxGroupRef = useRef(null); - + // Get color modifier for legend display - const colorModifier = postTimestepModifiers.find( - (modifier) => modifier.name === "Colors", - ) as ColorModifier | undefined; + const colorModifier = postTimestepModifiers.find((modifier) => modifier.name === "Colors") as + | ColorModifier + | undefined; // Determine the type of the computeName (compute, fix, or variable) - const getModifierType = (name: string | undefined): "compute" | "fix" | "variable" | undefined => { + const getModifierType = ( + name: string | undefined + ): "compute" | "fix" | "variable" | undefined => { if (!name) return undefined; if (computes[name]) return "compute"; if (fixes[name]) return "fix"; @@ -162,18 +142,17 @@ const View = ({ visible, isEmbeddedMode = false }: ViewProps) => { // Add Esc key handler to clear selection useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { - if (event.key === 'Escape' && selectedAtoms.size > 0) { + if (event.key === "Escape" && selectedAtoms.size > 0) { handleClearSelection(); } }; - window.addEventListener('keydown', handleKeyDown); + window.addEventListener("keydown", handleKeyDown); return () => { - window.removeEventListener('keydown', handleKeyDown); + window.removeEventListener("keydown", handleKeyDown); }; }, [selectedAtoms, handleClearSelection]); - const disposeBoxGroup = useCallback(() => { if (boxGroupRef.current && visualizer) { // Dispose of all cylinders in the group @@ -198,10 +177,10 @@ const View = ({ visible, isEmbeddedMode = false }: ViewProps) => { // onCameraChanged: (position: THREE.Vector3, target: THREE.Vector3) => {console.log(position, target)} onParticleClick: (event: ParticleClickEvent) => { const { particleIndex, shiftKey } = event; - + setSelectedAtoms((prevSelection) => { const newSelection = new Set(prevSelection); - + if (shiftKey) { // Shift+click: toggle selection if (newSelection.has(particleIndex)) { @@ -218,21 +197,21 @@ const View = ({ visible, isEmbeddedMode = false }: ViewProps) => { newSelection.add(particleIndex); } } - + // Update visualizer selection newVisualizer.clearSelection(); newSelection.forEach((idx) => { newVisualizer.setSelected(idx, true); }); - + return newSelection; }); - } + }, }); setVisualizer(newVisualizer); setLoading(false); newVisualizer.materials.particles.shininess = 50; - + // Initialize post-processing with settings newVisualizer.initPostProcessing({ ssao: { @@ -241,7 +220,7 @@ const View = ({ visible, isEmbeddedMode = false }: ViewProps) => { intensity: renderSettings.ssaoIntensity, }, }); - + // Apply lighting settings newVisualizer.pointLight.intensity = renderSettings.pointLightIntensity; newVisualizer.ambientLight.intensity = renderSettings.ambientLightIntensity; @@ -324,7 +303,7 @@ const View = ({ visible, isEmbeddedMode = false }: ViewProps) => { }, }); } - + // Update lighting visualizer.pointLight.intensity = renderSettings.pointLightIntensity; visualizer.ambientLight.intensity = renderSettings.ambientLightIntensity; @@ -339,7 +318,7 @@ const View = ({ visible, isEmbeddedMode = false }: ViewProps) => { if (visualizer) { // Apply camera controls setting visualizer.setControlsEnabled(embedConfig.enableCameraControls); - + // Apply particle picking setting visualizer.setPickingEnabled(embedConfig.enableParticlePicking); } @@ -422,7 +401,7 @@ const View = ({ visible, isEmbeddedMode = false }: ViewProps) => { }} > - {title} + {title} {running && ( { style={{ marginTop: "-15px" }} strokeColor={{ "0%": "#108ee9", "100%": "#87d068" }} size={8} - percent={Math.round( - 100 * (runTimesteps / (runTotalTimesteps + 1)), - )} + percent={Math.round(100 * (runTimesteps / (runTotalTimesteps + 1)))} /> )} @@ -441,10 +418,7 @@ const View = ({ visible, isEmbeddedMode = false }: ViewProps) => {
{showColorSettings && ( - setShowColorSettings(false)} - /> + setShowColorSettings(false)} /> )} {(!isEmbeddedMode || embedConfig.showSimulationSummary) && ( { setHideNoSimulation(true)} - footer={[ - , - ]} + footer={[]} title="No simulation" > You can create a new simulation or run one of the built-in examples. diff --git a/src/global.d.ts b/src/global.d.ts index 9a874dd6..cf16d018 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -18,6 +18,3 @@ declare global { files?: SimulationFile[]; } } - -export {}; - diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 132cca80..932a54fc 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -1,5 +1,5 @@ import { createTypedHooks } from "easy-peasy"; // 👈import the helper -import { StoreModel } from "../store/model"; +import type { StoreModel } from "../store/model"; // Provide our model to the helper 👇 const typedHooks = createTypedHooks(); diff --git a/src/hooks/useEmbeddedMode.ts b/src/hooks/useEmbeddedMode.ts index 9a1956c8..2c2fdb0f 100644 --- a/src/hooks/useEmbeddedMode.ts +++ b/src/hooks/useEmbeddedMode.ts @@ -1,5 +1,5 @@ +import type { EmbedConfig } from "../types"; import { isEmbeddedMode } from "../utils/embeddedMode"; -import { EmbedConfig } from "../types"; export interface EmbeddedModeResult { embeddedSimulationUrl: string | null; @@ -18,10 +18,10 @@ export interface EmbeddedModeResult { */ function parseVars(varsString: string | null): Record { if (!varsString) return {}; - + const vars: Record = {}; - varsString.split(',').forEach(varDef => { - const parts = varDef.trim().split(':'); + varsString.split(",").forEach((varDef) => { + const parts = varDef.trim().split(":"); if (parts.length >= 2) { const name = parts[0]; const value = parseFloat(parts[1]); @@ -55,16 +55,19 @@ function parseEmbedConfig(configString: string | null): EmbedConfig { try { const decoded = atob(configString); const parsed = JSON.parse(decoded) as Partial; - + // Apply defaults for any missing properties return { - showSimulationSummary: parsed.showSimulationSummary ?? DEFAULT_EMBED_CONFIG.showSimulationSummary, + showSimulationSummary: + parsed.showSimulationSummary ?? DEFAULT_EMBED_CONFIG.showSimulationSummary, showSimulationBox: parsed.showSimulationBox ?? DEFAULT_EMBED_CONFIG.showSimulationBox, - enableCameraControls: parsed.enableCameraControls ?? DEFAULT_EMBED_CONFIG.enableCameraControls, - enableParticlePicking: parsed.enableParticlePicking ?? DEFAULT_EMBED_CONFIG.enableParticlePicking, + enableCameraControls: + parsed.enableCameraControls ?? DEFAULT_EMBED_CONFIG.enableCameraControls, + enableParticlePicking: + parsed.enableParticlePicking ?? DEFAULT_EMBED_CONFIG.enableParticlePicking, }; } catch (error) { - console.warn('Failed to parse embed config:', error); + console.warn("Failed to parse embed config:", error); // Return defaults on parse error return DEFAULT_EMBED_CONFIG; } @@ -72,14 +75,14 @@ function parseEmbedConfig(configString: string | null): EmbedConfig { export function useEmbeddedMode(): EmbeddedModeResult { const urlSearchParams = new URLSearchParams(window.location.search); - const embeddedSimulationUrl = urlSearchParams.get('embeddedSimulationUrl'); - const simulationIndex = parseInt(urlSearchParams.get('simulationIndex') || '0', 10); - const embeddedData = urlSearchParams.get('data'); - const autoStartParam = urlSearchParams.get('autostart'); - const autoStart = autoStartParam === 'true'; - const vars = parseVars(urlSearchParams.get('vars')); - const embedConfig = parseEmbedConfig(urlSearchParams.get('config')); - + const embeddedSimulationUrl = urlSearchParams.get("embeddedSimulationUrl"); + const simulationIndex = parseInt(urlSearchParams.get("simulationIndex") || "0", 10); + const embeddedData = urlSearchParams.get("data"); + const autoStartParam = urlSearchParams.get("autostart"); + const autoStart = autoStartParam === "true"; + const vars = parseVars(urlSearchParams.get("vars")); + const embedConfig = parseEmbedConfig(urlSearchParams.get("config")); + // Use shared utility function to determine embedded mode const embeddedMode = isEmbeddedMode(urlSearchParams); @@ -92,4 +95,4 @@ export function useEmbeddedMode(): EmbeddedModeResult { vars, embedConfig, }; -} \ No newline at end of file +} diff --git a/src/index.css b/src/index.css index 6f6dafc9..1926d6ac 100644 --- a/src/index.css +++ b/src/index.css @@ -1,10 +1,9 @@ /* Four-corner gradient background - optimized (static, no animation) */ html { /* Simplified to 3 layers for better performance while maintaining four-corner effect */ - background: - /* Top-left corner */ - radial-gradient(circle at 0% 0%, rgb(57, 76, 93) 0%, transparent 60%), - /* Bottom-right corner */ + /* Top-left corner */ + background: + radial-gradient(circle at 0% 0%, rgb(57, 76, 93) 0%, transparent 60%), /* Bottom-right corner */ radial-gradient(circle at 100% 100%, rgb(41, 63, 86) 0%, transparent 60%), /* Base gradient blend */ linear-gradient(135deg, rgb(42, 44, 52) 0%, rgb(31, 36, 47) 50%, rgb(41, 63, 86) 100%); @@ -19,12 +18,7 @@ html::before { left: 0; width: 100%; height: 100%; - background: linear-gradient( - 135deg, - transparent 0%, - rgba(30, 35, 45, 0.3) 50%, - transparent 100% - ); + background: linear-gradient(135deg, transparent 0%, rgba(30, 35, 45, 0.3) 50%, transparent 100%); pointer-events: none; z-index: 0; } @@ -32,9 +26,9 @@ html::before { body { margin: 0; padding: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; + font-family: + -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", + "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; height: 100%; @@ -43,8 +37,7 @@ body { } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", - monospace; + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } html, @@ -104,11 +97,7 @@ body, left: 0; width: 100%; height: 100%; - background: linear-gradient( - to bottom, - rgba(255, 255, 255, 0.15) 0%, - transparent 50% - ); + background: linear-gradient(to bottom, rgba(255, 255, 255, 0.15) 0%, transparent 50%); pointer-events: none; opacity: 0.6; z-index: 0; @@ -215,7 +204,9 @@ body, right: 10px; color: #fff; opacity: 1; - box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.2), 0 8px 32px rgba(0, 0, 0, 0.1); + box-shadow: + inset 0 0 20px rgba(0, 0, 0, 0.2), + 0 8px 32px rgba(0, 0, 0, 0.1); position: relative; overflow: hidden; z-index: 1000; @@ -229,11 +220,7 @@ body, left: 0; width: 100%; height: 100%; - background: linear-gradient( - to bottom, - rgba(255, 255, 255, 0.15) 0%, - transparent 50% - ); + background: linear-gradient(to bottom, rgba(255, 255, 255, 0.15) 0%, transparent 50%); pointer-events: none; opacity: 0.5; transform: translateZ(0); @@ -313,7 +300,6 @@ body, z-index: 1; } - .selected-atoms-info { position: absolute; background: rgba(42, 41, 47, 0.6); @@ -327,7 +313,9 @@ body, left: 20px; color: #fff; z-index: 1000; - box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.2), 0 8px 32px rgba(0, 0, 0, 0.1); + box-shadow: + inset 0 0 20px rgba(0, 0, 0, 0.2), + 0 8px 32px rgba(0, 0, 0, 0.1); overflow: hidden; contain: layout style paint; } @@ -339,11 +327,7 @@ body, left: 0; width: 100%; height: 100%; - background: linear-gradient( - to bottom, - rgba(255, 255, 255, 0.15) 0%, - transparent 50% - ); + background: linear-gradient(to bottom, rgba(255, 255, 255, 0.15) 0%, transparent 50%); pointer-events: none; opacity: 0.5; transform: translateZ(0); @@ -362,7 +346,9 @@ body, left: 20px; color: #fff; z-index: 1000; - box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.2), 0 8px 32px rgba(0, 0, 0, 0.1); + box-shadow: + inset 0 0 20px rgba(0, 0, 0, 0.2), + 0 8px 32px rgba(0, 0, 0, 0.1); overflow: hidden; contain: layout style paint; font-family: monospace; @@ -376,11 +362,7 @@ body, left: 0; width: 100%; height: 100%; - background: linear-gradient( - to bottom, - rgba(255, 255, 255, 0.15) 0%, - transparent 50% - ); + background: linear-gradient(to bottom, rgba(255, 255, 255, 0.15) 0%, transparent 50%); pointer-events: none; opacity: 0.5; transform: translateZ(0); @@ -472,7 +454,9 @@ body, -webkit-backdrop-filter: blur(8px); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 16px; - box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.2), 0 8px 32px rgba(0, 0, 0, 0.1); + box-shadow: + inset 0 0 20px rgba(0, 0, 0, 0.2), + 0 8px 32px rgba(0, 0, 0, 0.1); position: relative; overflow: hidden; padding: 16px; @@ -486,11 +470,7 @@ body, left: 0; width: 100%; height: 100%; - background: linear-gradient( - to bottom, - rgba(255, 255, 255, 0.15) 0%, - transparent 50% - ); + background: linear-gradient(to bottom, rgba(255, 255, 255, 0.15) 0%, transparent 50%); pointer-events: none; opacity: 0.5; transform: translateZ(0); diff --git a/src/index.tsx b/src/index.tsx index 6a5d6ae1..4703916e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,12 +1,12 @@ import "./index.css"; import "dygraphs/dist/dygraph.css"; +import { StoreProvider } from "easy-peasy"; +import mixpanel from "mixpanel-browser"; import React from "react"; import { createRoot } from "react-dom/client"; import App from "./App"; -import { StoreProvider } from "easy-peasy"; import store from "./store"; -import mixpanel from "mixpanel-browser"; -import { track, getEmbeddingParams } from "./utils/metrics"; +import { getEmbeddingParams, track } from "./utils/metrics"; mixpanel.init("b5022dd7fe5b3cd0396d84284ae647e6", { debug: false }); @@ -14,7 +14,9 @@ track("Page.Load", getEmbeddingParams()); const container = document.getElementById("root"); if (!container) { - throw new Error("Failed to find the root element. Please ensure an element with id 'root' exists in your index.html."); + throw new Error( + "Failed to find the root element. Please ensure an element with id 'root' exists in your index.html." + ); } const root = createRoot(container); diff --git a/src/modifiers/ColorModifierSettings.tsx b/src/modifiers/ColorModifierSettings.tsx index 7d59e402..8d91b5a4 100644 --- a/src/modifiers/ColorModifierSettings.tsx +++ b/src/modifiers/ColorModifierSettings.tsx @@ -1,35 +1,27 @@ -import { Modal, Select, Divider, InputNumber, Checkbox, Button, Space } from "antd"; -import { useStoreState, useStoreActions } from "../hooks"; +import { Button, Checkbox, Divider, InputNumber, Modal, Select, Space } from "antd"; import { useCallback, useState } from "react"; +import { useStoreActions, useStoreState } from "../hooks"; import { track } from "../utils/metrics"; -import ColorModifier from "./colormodifier"; +import type ColorModifier from "./colormodifier"; + const { Option, OptGroup } = Select; const ColorModifierSettings = ({ open, onClose }: { open: boolean; onClose: () => void }) => { const computes = useStoreState((state) => state.simulationStatus.computes); - const postTimestepModifiers = useStoreState( - (state) => state.processing.postTimestepModifiers, - ); + const postTimestepModifiers = useStoreState((state) => state.processing.postTimestepModifiers); const setParticleStylesUpdated = useStoreActions( - (actions) => actions.render.setParticleStylesUpdated, + (actions) => actions.render.setParticleStylesUpdated ); const colorModifier = postTimestepModifiers.filter( - (modifier) => modifier.name === "Colors", + (modifier) => modifier.name === "Colors" )[0] as ColorModifier; - const perAtomComputes = Object.values(computes).filter( - (compute) => compute.isPerAtom, - ); + const perAtomComputes = Object.values(computes).filter((compute) => compute.isPerAtom); const [useCustomRange, setUseCustomRange] = useState( - colorModifier.customMinValue !== undefined || - colorModifier.customMaxValue !== undefined - ); - const [customMin, setCustomMin] = useState( - colorModifier.customMinValue ?? null - ); - const [customMax, setCustomMax] = useState( - colorModifier.customMaxValue ?? null + colorModifier.customMinValue !== undefined || colorModifier.customMaxValue !== undefined ); + const [customMin, setCustomMin] = useState(colorModifier.customMinValue ?? null); + const [customMax, setCustomMax] = useState(colorModifier.customMaxValue ?? null); const onChange = useCallback( (value: string) => { @@ -45,7 +37,7 @@ const ColorModifierSettings = ({ open, onClose }: { open: boolean; onClose: () = setParticleStylesUpdated(true); } }, - [colorModifier, setParticleStylesUpdated], + [colorModifier, setParticleStylesUpdated] ); const handleCustomRangeToggle = useCallback( @@ -64,7 +56,7 @@ const ColorModifierSettings = ({ open, onClose }: { open: boolean; onClose: () = setCustomMax(colorModifier.customMaxValue); } }, - [colorModifier, customMin, customMax], + [colorModifier, customMin, customMax] ); const handleMinChange = useCallback( @@ -74,7 +66,7 @@ const ColorModifierSettings = ({ open, onClose }: { open: boolean; onClose: () = colorModifier.customMinValue = value; } }, - [colorModifier, useCustomRange], + [colorModifier, useCustomRange] ); const handleMaxChange = useCallback( @@ -84,7 +76,7 @@ const ColorModifierSettings = ({ open, onClose }: { open: boolean; onClose: () = colorModifier.customMaxValue = value; } }, - [colorModifier, useCustomRange], + [colorModifier, useCustomRange] ); const handleResetRange = useCallback(() => { @@ -96,10 +88,8 @@ const ColorModifierSettings = ({ open, onClose }: { open: boolean; onClose: () = setUseCustomRange(false); }, [colorModifier]); - const defaultValue = colorModifier.computeName - ? colorModifier.computeName - : "type"; - + const defaultValue = colorModifier.computeName ? colorModifier.computeName : "type"; + const showRangeControls = colorModifier.computeName !== undefined; const colormapOptions = [ @@ -124,25 +114,15 @@ const ColorModifierSettings = ({ open, onClose }: { open: boolean; onClose: () = colorModifier.colormap = value; track("Settings.Render.Colormap", { colormap: value }); }, - [colorModifier], + [colorModifier] ); - + return ( - +
Color by:
- {perAtomComputes.map((compute) => ( @@ -178,12 +158,20 @@ const ColorModifierSettings = ({ open, onClose }: { open: boolean; onClose: () = {showRangeControls && ( <> - +
Value Range:
- Global: [{isFinite(colorModifier.globalMinValue) ? colorModifier.globalMinValue.toExponential(2) : "N/A"}, {isFinite(colorModifier.globalMaxValue) ? colorModifier.globalMaxValue.toExponential(2) : "N/A"}] + Global: [ + {isFinite(colorModifier.globalMinValue) + ? colorModifier.globalMinValue.toExponential(2) + : "N/A"} + ,{" "} + {isFinite(colorModifier.globalMaxValue) + ? colorModifier.globalMaxValue.toExponential(2) + : "N/A"} + ]
@@ -191,7 +179,9 @@ const ColorModifierSettings = ({ open, onClose }: { open: boolean; onClose: () = checked={useCustomRange} onChange={(e) => handleCustomRangeToggle(e.target.checked)} style={{ marginBottom: 12 }} - disabled={!isFinite(colorModifier.globalMinValue) || !isFinite(colorModifier.globalMaxValue)} + disabled={ + !isFinite(colorModifier.globalMinValue) || !isFinite(colorModifier.globalMaxValue) + } > Use custom range @@ -219,11 +209,7 @@ const ColorModifierSettings = ({ open, onClose }: { open: boolean; onClose: () = )} -
diff --git a/src/modifiers/SyncBondsSettings.tsx b/src/modifiers/SyncBondsSettings.tsx index a0b02050..caf6a79f 100644 --- a/src/modifiers/SyncBondsSettings.tsx +++ b/src/modifiers/SyncBondsSettings.tsx @@ -1,7 +1,7 @@ -import { Modal, Table, Slider } from "antd"; +import { Modal, Slider, Table } from "antd"; import type { ColumnsType } from "antd/es/table"; -import { useStoreState, useStoreActions } from "../hooks"; import { useCallback } from "react"; +import { useStoreActions, useStoreState } from "../hooks"; interface SettingsType { key: React.ReactNode; @@ -11,9 +11,7 @@ interface SettingsType { const SyncBondsSettings = ({ onClose }: { onClose: () => void }) => { const bondRadius = useStoreState((state) => state.render.bondRadius); - const setBondRadius = useStoreActions( - (actions) => actions.render.setBondRadius, - ); + const setBondRadius = useStoreActions((actions) => actions.render.setBondRadius); const bonds = useStoreState((state) => state.render.bonds); const onBondRadiusChanged = useCallback( @@ -24,7 +22,7 @@ const SyncBondsSettings = ({ onClose }: { onClose: () => void }) => { bonds.markNeedsUpdate(); } }, - [setBondRadius, bonds], + [setBondRadius, bonds] ); const columns: ColumnsType = [ @@ -39,13 +37,7 @@ const SyncBondsSettings = ({ onClose }: { onClose: () => void }) => { key: "value", width: "80%", render: (value, record) => ( - + ), }, ]; diff --git a/src/modifiers/SyncParticlesSettings.tsx b/src/modifiers/SyncParticlesSettings.tsx index 746dcc33..eaaeb7ec 100644 --- a/src/modifiers/SyncParticlesSettings.tsx +++ b/src/modifiers/SyncParticlesSettings.tsx @@ -1,7 +1,7 @@ -import { Modal, Table, Slider } from "antd"; +import { Modal, Slider, Table } from "antd"; import type { ColumnsType } from "antd/es/table"; -import { useStoreState, useStoreActions } from "../hooks"; import { useCallback } from "react"; +import { useStoreActions, useStoreState } from "../hooks"; interface SettingsType { key: React.ReactNode; @@ -11,11 +11,9 @@ interface SettingsType { const SyncParticlesSettings = ({ onClose }: { onClose: () => void }) => { const particleRadius = useStoreState((state) => state.render.particleRadius); - const setParticleRadius = useStoreActions( - (actions) => actions.render.setParticleRadius, - ); + const setParticleRadius = useStoreActions((actions) => actions.render.setParticleRadius); const setParticleStylesUpdated = useStoreActions( - (actions) => actions.render.setParticleStylesUpdated, + (actions) => actions.render.setParticleStylesUpdated ); const onParticleRadiusChanged = useCallback( @@ -23,7 +21,7 @@ const SyncParticlesSettings = ({ onClose }: { onClose: () => void }) => { setParticleRadius(value); setParticleStylesUpdated(true); }, - [setParticleStylesUpdated, setParticleRadius], + [setParticleStylesUpdated, setParticleRadius] ); const columns: ColumnsType = [ @@ -38,13 +36,7 @@ const SyncParticlesSettings = ({ onClose }: { onClose: () => void }) => { key: "value", width: "80%", render: (value, record) => ( - + ), }, ]; diff --git a/src/modifiers/colormodifier.test.ts b/src/modifiers/colormodifier.test.ts index 83f61c03..a3e9bd3b 100644 --- a/src/modifiers/colormodifier.test.ts +++ b/src/modifiers/colormodifier.test.ts @@ -1,6 +1,6 @@ -import { describe, it, expect, beforeEach, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import ColorModifier from "./colormodifier"; -import { ModifierInput, ModifierOutput } from "./types"; +import type { ModifierInput, ModifierOutput } from "./types"; describe("ColorModifier", () => { let colorModifier: ColorModifier; @@ -119,7 +119,7 @@ describe("ColorModifier", () => { visualizer: mockVisualizer, } as unknown as ModifierInput["renderState"], computes: { - "c_test": mockCompute as unknown as ModifierInput["computes"][string], + c_test: mockCompute as unknown as ModifierInput["computes"][string], }, wasm: mockWasm as unknown as ModifierInput["wasm"], }; @@ -134,10 +134,7 @@ describe("ColorModifier", () => { colorModifier.computeName = "c_test"; // Act - colorModifier.runByProperty( - input as ModifierInput, - output as ModifierOutput - ); + colorModifier.runByProperty(input as ModifierInput, output as ModifierOutput); // Assert expect(colorModifier.globalMinValue).toBe(1.0); @@ -164,7 +161,7 @@ describe("ColorModifier", () => { visualizer: mockVisualizer, } as unknown as ModifierInput["renderState"], computes: { - "c_test": mockCompute as unknown as ModifierInput["computes"][string], + c_test: mockCompute as unknown as ModifierInput["computes"][string], }, wasm: {} as unknown as ModifierInput["wasm"], }; @@ -185,10 +182,7 @@ describe("ColorModifier", () => { }, } as unknown as ModifierInput["wasm"]; - colorModifier.runByProperty( - input as ModifierInput, - output as ModifierOutput - ); + colorModifier.runByProperty(input as ModifierInput, output as ModifierOutput); expect(colorModifier.globalMinValue).toBe(1.0); expect(colorModifier.globalMaxValue).toBe(3.0); @@ -201,10 +195,7 @@ describe("ColorModifier", () => { } as unknown as ModifierInput["wasm"]; // Act - colorModifier.runByProperty( - input as ModifierInput, - output as ModifierOutput - ); + colorModifier.runByProperty(input as ModifierInput, output as ModifierOutput); // Assert - should keep the extremes from both timesteps expect(colorModifier.globalMinValue).toBe(0.5); @@ -237,7 +228,7 @@ describe("ColorModifier", () => { visualizer: mockVisualizer, } as unknown as ModifierInput["renderState"], computes: { - "c_test": mockCompute as unknown as ModifierInput["computes"][string], + c_test: mockCompute as unknown as ModifierInput["computes"][string], }, wasm: mockWasm as unknown as ModifierInput["wasm"], }; @@ -255,10 +246,7 @@ describe("ColorModifier", () => { colorModifier.resetMinMax = true; // Act - colorModifier.runByProperty( - input as ModifierInput, - output as ModifierOutput - ); + colorModifier.runByProperty(input as ModifierInput, output as ModifierOutput); // Assert expect(colorModifier.globalMinValue).toBe(10.0); @@ -292,7 +280,7 @@ describe("ColorModifier", () => { visualizer: mockVisualizer, } as unknown as ModifierInput["renderState"], computes: { - "c_test": mockCompute as unknown as ModifierInput["computes"][string], + c_test: mockCompute as unknown as ModifierInput["computes"][string], }, wasm: mockWasm as unknown as ModifierInput["wasm"], }; @@ -309,10 +297,7 @@ describe("ColorModifier", () => { colorModifier.customMaxValue = 20; // Act - colorModifier.runByProperty( - input as ModifierInput, - output as ModifierOutput - ); + colorModifier.runByProperty(input as ModifierInput, output as ModifierOutput); // Assert - should have called setColor for each particle expect(mockVisualizer.setColor).toHaveBeenCalledTimes(3); diff --git a/src/modifiers/colormodifier.ts b/src/modifiers/colormodifier.ts index a6759d98..97b899a8 100644 --- a/src/modifiers/colormodifier.ts +++ b/src/modifiers/colormodifier.ts @@ -1,8 +1,8 @@ -import Modifier from "./modifier"; -import { ModifierInput, ModifierOutput } from "./types"; import colormap from "colormap"; -import { AtomType } from "../utils/atomtypes"; import * as THREE from "three"; +import type { AtomType } from "../utils/atomtypes"; +import Modifier from "./modifier"; +import type { ModifierInput, ModifierOutput } from "./types"; const defaultAtomTypes: AtomType[] = [ { @@ -123,15 +123,15 @@ interface ColorModifierProps { class ColorModifier extends Modifier { public computeName?: string; private previousColoringMethod?: string; - + // Min/max tracking over time public globalMinValue: number = Infinity; public globalMaxValue: number = -Infinity; - + // Custom min/max values (if set, override computed values) public customMinValue?: number; public customMaxValue?: number; - + // Flag to reset global min/max public resetMinMax: boolean = false; @@ -142,16 +142,16 @@ class ColorModifier extends Modifier { super({ name, active }); this.computeName = undefined; } - + // Get the effective min/max values (custom or global) public getEffectiveMinValue(): number { return this.customMinValue !== undefined ? this.customMinValue : this.globalMinValue; } - + public getEffectiveMaxValue(): number { return this.customMaxValue !== undefined ? this.customMaxValue : this.globalMaxValue; } - + // Reset global min/max values public resetGlobalMinMax(): void { this.globalMinValue = Infinity; @@ -169,7 +169,7 @@ class ColorModifier extends Modifier { const particles = output.particles; const visualizer = input.renderState.visualizer; - let colors = colormap({ + const colors = colormap({ colormap: this.colormap, nshades: 72, format: "float", @@ -189,14 +189,14 @@ class ColorModifier extends Modifier { const perAtomDataPtr = compute.lmpCompute.getPerAtomData() / 8; const perAtomArray = input.wasm.HEAPF64.subarray( perAtomDataPtr, - perAtomDataPtr + particles.count, + perAtomDataPtr + particles.count ) as Float64Array; - + // Reset global min/max if requested if (this.resetMinMax) { this.resetGlobalMinMax(); } - + // Calculate min/max for current timestep let currentMinValue = Infinity; let currentMaxValue = -Infinity; @@ -205,27 +205,27 @@ class ColorModifier extends Modifier { currentMinValue = Math.min(currentMinValue, value); currentMaxValue = Math.max(currentMaxValue, value); } - + // Update global min/max values this.globalMinValue = Math.min(this.globalMinValue, currentMinValue); this.globalMaxValue = Math.max(this.globalMaxValue, currentMaxValue); - + // Use effective min/max for coloring const effectiveMinValue = this.getEffectiveMinValue(); const effectiveMaxValue = this.getEffectiveMaxValue(); - + // Avoid division by zero const range = effectiveMaxValue - effectiveMinValue; const validRange = range > 0 ? range : 1; - + perAtomArray.forEach((value, index) => { const realIndex = particles.indices[index]; - + // Clamp value to effective range const clampedValue = Math.max(effectiveMinValue, Math.min(effectiveMaxValue, value)); - + const colorIndex = Math.floor( - ((clampedValue - effectiveMinValue) / validRange) * (colors.length - 1), + ((clampedValue - effectiveMinValue) / validRange) * (colors.length - 1) ); const color = colors[colorIndex]; @@ -239,11 +239,7 @@ class ColorModifier extends Modifier { return; }; - runByType = ( - input: ModifierInput, - output: ModifierOutput, - everything: boolean = false, - ) => { + runByType = (input: ModifierInput, output: ModifierOutput, everything: boolean = false) => { if ( (this.previousColoringMethod === "type" && !output.colorsDirty) || !input.renderState.visualizer diff --git a/src/modifiers/modifier.ts b/src/modifiers/modifier.ts index 4c0e43bf..060ebe8f 100644 --- a/src/modifiers/modifier.ts +++ b/src/modifiers/modifier.ts @@ -1,4 +1,4 @@ -import { ModifierInput, ModifierOutput } from "./types"; +import type { ModifierInput, ModifierOutput } from "./types"; interface ModifierProps { name: string; @@ -16,10 +16,6 @@ class Modifier { this.active = active; } - run = ( - input: ModifierInput, - output: ModifierOutput, - everything: boolean = false, - ) => {}; + run = (input: ModifierInput, output: ModifierOutput, everything: boolean = false) => {}; } export default Modifier; diff --git a/src/modifiers/syncbondsmodifier.ts b/src/modifiers/syncbondsmodifier.ts index 16257a2c..be4d9d12 100644 --- a/src/modifiers/syncbondsmodifier.ts +++ b/src/modifiers/syncbondsmodifier.ts @@ -1,6 +1,6 @@ -import Modifier from "./modifier"; -import { ModifierInput, ModifierOutput } from "./types"; import { Bonds } from "omovi"; +import Modifier from "./modifier"; +import type { ModifierInput, ModifierOutput } from "./types"; interface SyncBondsModifierProps { name: string; @@ -27,7 +27,7 @@ class SyncBondsModifier extends Modifier { const bondRadius = input.renderState.bondRadius; let newBonds = output.bonds; if (!newBonds || newBonds.capacity < numBonds) { - let newCapacity = numBonds; + const newCapacity = numBonds; if (newBonds) { newBonds.dispose(); } @@ -50,11 +50,11 @@ class SyncBondsModifier extends Modifier { const bonds2Ptr = input.lammps.getBondsPosition2Pointer() / 4; const positions1Subarray = input.wasm.HEAPF32.subarray( bonds1Ptr, - bonds1Ptr + 3 * numBonds, + bonds1Ptr + 3 * numBonds ) as Float32Array; const positions2Subarray = input.wasm.HEAPF32.subarray( bonds2Ptr, - bonds2Ptr + 3 * numBonds, + bonds2Ptr + 3 * numBonds ) as Float32Array; newBonds.positions1.set(positions1Subarray); newBonds.positions2.set(positions2Subarray); diff --git a/src/modifiers/synccomputesmodifier.ts b/src/modifiers/synccomputesmodifier.ts index 069df3c9..c7cd05ce 100644 --- a/src/modifiers/synccomputesmodifier.ts +++ b/src/modifiers/synccomputesmodifier.ts @@ -1,5 +1,5 @@ import Modifier from "./modifier"; -import { ModifierInput, ModifierOutput } from "./types"; +import type { ModifierInput, ModifierOutput } from "./types"; interface SyncComputesModifierProps { name: string; @@ -11,11 +11,7 @@ class SyncComputesModifier extends Modifier { super({ name, active }); } - run = ( - input: ModifierInput, - output: ModifierOutput, - everything: boolean = false, - ) => { + run = (input: ModifierInput, output: ModifierOutput, everything: boolean = false) => { if (!this.active || !input.hasSynchronized) { return; } @@ -84,7 +80,7 @@ class SyncComputesModifier extends Modifier { // Get data1DVector once before the loop for better performance const data1DVector = compute.lmpCompute.getData1D(); - + for (let j = 0; j < data1DNamesSize; j++) { const lmpData = data1DVector.get(j); @@ -97,11 +93,11 @@ class SyncComputesModifier extends Modifier { const yValuesPointer = lmpData.getYValuesPointer() / 4; const xValues = input.wasm.HEAPF32.subarray( xValuesPointer, - xValuesPointer + lmpData.getNumPoints(), + xValuesPointer + lmpData.getNumPoints() ) as Float32Array; const yValues = input.wasm.HEAPF32.subarray( yValuesPointer, - yValuesPointer + lmpData.getNumPoints(), + yValuesPointer + lmpData.getNumPoints() ) as Float32Array; for (let k = lengthBeforeWeStart; k < xValues.length; k++) { if (j === 0) { @@ -109,11 +105,11 @@ class SyncComputesModifier extends Modifier { } compute.data1D.data[k].push(yValues[k]); } - + // Delete the Data1D copy to prevent memory leak lmpData.delete(); } - + // Delete the vector wrapper after the loop to prevent memory leak data1DVector.delete(); } diff --git a/src/modifiers/syncfixesmodifier.ts b/src/modifiers/syncfixesmodifier.ts index d1d54716..e5287c39 100644 --- a/src/modifiers/syncfixesmodifier.ts +++ b/src/modifiers/syncfixesmodifier.ts @@ -1,5 +1,5 @@ import Modifier from "./modifier"; -import { ModifierInput, ModifierOutput } from "./types"; +import type { ModifierInput, ModifierOutput } from "./types"; interface SyncFixesModifierProps { name: string; @@ -11,11 +11,7 @@ class SyncFixesModifier extends Modifier { super({ name, active }); } - run = ( - input: ModifierInput, - output: ModifierOutput, - everything: boolean = false, - ) => { + run = (input: ModifierInput, output: ModifierOutput, everything: boolean = false) => { if (!this.active || !input.hasSynchronized) { return; } @@ -80,7 +76,7 @@ class SyncFixesModifier extends Modifier { // Get data1DVector once before the loop for better performance const data1DVector = fix.lmpFix.getData1D(); - + for (let j = 0; j < data1DNamesSize; j++) { const lmpData = data1DVector.get(j); @@ -93,11 +89,11 @@ class SyncFixesModifier extends Modifier { const yValuesPointer = lmpData.getYValuesPointer() / 4; const xValues = input.wasm.HEAPF32.subarray( xValuesPointer, - xValuesPointer + lmpData.getNumPoints(), + xValuesPointer + lmpData.getNumPoints() ) as Float32Array; const yValues = input.wasm.HEAPF32.subarray( yValuesPointer, - yValuesPointer + lmpData.getNumPoints(), + yValuesPointer + lmpData.getNumPoints() ) as Float32Array; for (let k = lengthBeforeWeStart; k < xValues.length; k++) { if (j === 0) { @@ -105,11 +101,11 @@ class SyncFixesModifier extends Modifier { } fix.data1D.data[k].push(yValues[k]); } - + // Delete the Data1D copy to prevent memory leak lmpData.delete(); } - + // Delete the vector wrapper after the loop to prevent memory leak data1DVector.delete(); } diff --git a/src/modifiers/syncparticlesmodifier.ts b/src/modifiers/syncparticlesmodifier.ts index 7a4ad840..01cfc4ee 100644 --- a/src/modifiers/syncparticlesmodifier.ts +++ b/src/modifiers/syncparticlesmodifier.ts @@ -1,6 +1,6 @@ -import Modifier from "./modifier"; -import { ModifierInput, ModifierOutput } from "./types"; import { Particles } from "omovi"; +import Modifier from "./modifier"; +import type { ModifierInput, ModifierOutput } from "./types"; interface SyncParticlesModifierProps { name: string; @@ -12,11 +12,7 @@ class SyncParticlesModifier extends Modifier { super({ name, active }); } - run = ( - input: ModifierInput, - output: ModifierOutput, - everything: boolean = false, - ) => { + run = (input: ModifierInput, output: ModifierOutput, everything: boolean = false) => { if (!this.active) { if (output.particles) { output.particles.count = 0; @@ -51,16 +47,10 @@ class SyncParticlesModifier extends Modifier { const idPtr = input.lammps.getIdPointer() / 4; const positionsSubarray = input.wasm.HEAPF32.subarray( positionsPtr, - positionsPtr + 3 * numParticles, + positionsPtr + 3 * numParticles ) as Float32Array; - const typeSubarray = input.wasm.HEAP32.subarray( - typePtr, - typePtr + numParticles, - ) as Int32Array; - const idSubarray = input.wasm.HEAP32.subarray( - idPtr, - idPtr + numParticles, - ) as Int32Array; + const typeSubarray = input.wasm.HEAP32.subarray(typePtr, typePtr + numParticles) as Int32Array; + const idSubarray = input.wasm.HEAP32.subarray(idPtr, idPtr + numParticles) as Int32Array; newParticles.positions.set(positionsSubarray); newParticles.types.set(typeSubarray); diff --git a/src/modifiers/syncvariablesmodifier.ts b/src/modifiers/syncvariablesmodifier.ts index f84923d6..b73c5859 100644 --- a/src/modifiers/syncvariablesmodifier.ts +++ b/src/modifiers/syncvariablesmodifier.ts @@ -1,5 +1,5 @@ import Modifier from "./modifier"; -import { ModifierInput, ModifierOutput } from "./types"; +import type { ModifierInput, ModifierOutput } from "./types"; interface SyncVariablesModifierProps { name: string; @@ -11,11 +11,7 @@ class SyncVariablesModifier extends Modifier { super({ name, active }); } - run = ( - input: ModifierInput, - output: ModifierOutput, - everything: boolean = false, - ) => { + run = (input: ModifierInput, output: ModifierOutput, everything: boolean = false) => { if (!this.active || !input.hasSynchronized) { return; } @@ -56,7 +52,7 @@ class SyncVariablesModifier extends Modifier { variable.hasData1D = data1DNamesWrapper.size() > 0; const data1DNamesSize = data1DNamesWrapper.size(); data1DNamesWrapper.delete(); // Delete WASM wrapper to prevent memory leak - + if (data1DNamesSize > 0) { variable.clearPerSync = variable.lmpVariable.getClearPerSync(); if (variable.data1D == null) { @@ -82,7 +78,7 @@ class SyncVariablesModifier extends Modifier { // Get data1DVector once before the loop for better performance const data1DVector = variable.lmpVariable.getData1D(); - + for (let j = 0; j < data1DNamesSize; j++) { const lmpData = data1DVector.get(j); @@ -95,11 +91,11 @@ class SyncVariablesModifier extends Modifier { const yValuesPointer = lmpData.getYValuesPointer() / 4; const xValues = input.wasm.HEAPF32.subarray( xValuesPointer, - xValuesPointer + lmpData.getNumPoints(), + xValuesPointer + lmpData.getNumPoints() ) as Float32Array; const yValues = input.wasm.HEAPF32.subarray( yValuesPointer, - yValuesPointer + lmpData.getNumPoints(), + yValuesPointer + lmpData.getNumPoints() ) as Float32Array; for (let k = lengthBeforeWeStart; k < xValues.length; k++) { if (j === 0) { @@ -107,11 +103,11 @@ class SyncVariablesModifier extends Modifier { } variable.data1D.data[k].push(yValues[k]); } - + // Delete the Data1D copy to prevent memory leak lmpData.delete(); } - + // Delete the vector wrapper after the loop to prevent memory leak data1DVector.delete(); } diff --git a/src/modifiers/types.ts b/src/modifiers/types.ts index e9d08d79..2911d80a 100644 --- a/src/modifiers/types.ts +++ b/src/modifiers/types.ts @@ -1,8 +1,7 @@ -import { LammpsWeb } from "../types"; -import { Particles, Bonds } from "omovi"; -import { Compute, Fix, Variable } from "../types"; -import { AtomifyWasmModule } from "../wasm/types"; -import { RenderModel } from "../store/render"; +import type { Bonds, Particles } from "omovi"; +import type { RenderModel } from "../store/render"; +import type { Compute, Fix, LammpsWeb, Variable } from "../types"; +import type { AtomifyWasmModule } from "../wasm/types"; type RenderState = Pick< RenderModel, diff --git a/src/setupTests.ts b/src/setupTests.ts index 42502e35..48e2b02d 100644 --- a/src/setupTests.ts +++ b/src/setupTests.ts @@ -24,16 +24,17 @@ Object.defineProperty(window, "matchMedia", { vi.mock("@monaco-editor/react", () => ({ default: vi.fn(() => null), loader: { - init: vi.fn(() => Promise.resolve({ - languages: { - register: vi.fn(), - setMonarchTokensProvider: vi.fn(), - }, - editor: { - create: vi.fn(), - }, - })), + init: vi.fn(() => + Promise.resolve({ + languages: { + register: vi.fn(), + setMonarchTokensProvider: vi.fn(), + }, + editor: { + create: vi.fn(), + }, + }) + ), config: vi.fn(), }, })); - diff --git a/src/store/app.ts b/src/store/app.ts index ed6bcf13..05ef328b 100644 --- a/src/store/app.ts +++ b/src/store/app.ts @@ -1,4 +1,4 @@ -import { action, Action } from "easy-peasy"; +import { type Action, action } from "easy-peasy"; import { isEmbeddedMode } from "../utils/embeddedMode"; interface Status { diff --git a/src/store/index.ts b/src/store/index.ts index 6ccf6ada..35999ba7 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,5 +1,5 @@ import { createStore } from "easy-peasy"; -import { storeModel, StoreModel } from "./model"; +import { type StoreModel, storeModel } from "./model"; const store = createStore(storeModel); diff --git a/src/store/model.ts b/src/store/model.ts index 9195c1a4..8b9ccbf1 100644 --- a/src/store/model.ts +++ b/src/store/model.ts @@ -1,13 +1,10 @@ -import { SimulationModel, simulationModel } from "./simulation"; -import { SettingsModel, settingsModel } from "./settings"; -import { - SimulationStatusModel, - simulationStatusModel, -} from "./simulationstatus"; -import { ProcessingModel, processingModel } from "./processing"; -import { RenderModel, renderModel } from "./render"; -import { AppModel, appModel } from "./app"; import { persist } from "easy-peasy"; +import { type AppModel, appModel } from "./app"; +import { type ProcessingModel, processingModel } from "./processing"; +import { type RenderModel, renderModel } from "./render"; +import { type SettingsModel, settingsModel } from "./settings"; +import { type SimulationModel, simulationModel } from "./simulation"; +import { type SimulationStatusModel, simulationStatusModel } from "./simulationstatus"; export interface StoreModel { simulation: SimulationModel; diff --git a/src/store/processing.ts b/src/store/processing.ts index 01596d51..7250dd47 100644 --- a/src/store/processing.ts +++ b/src/store/processing.ts @@ -1,16 +1,16 @@ -import { action, thunk, Action, Thunk, State, Actions } from "easy-peasy"; -import Modifier from "../modifiers/modifier"; -import SyncParticlesModifier from "../modifiers/syncparticlesmodifier"; -import SyncBondsModifier from "../modifiers/syncbondsmodifier"; +import { type Action, type Actions, action, type State, type Thunk, thunk } from "easy-peasy"; +import * as THREE from "three"; import ColorModifier from "../modifiers/colormodifier"; +import type Modifier from "../modifiers/modifier"; +import SyncBondsModifier from "../modifiers/syncbondsmodifier"; import SyncComputesModifier from "../modifiers/synccomputesmodifier"; -import { ModifierInput, ModifierOutput } from "../modifiers/types"; -import { LammpsWeb } from "../types"; -import { AtomifyWasmModule } from "../wasm/types"; -import * as THREE from "three"; import SyncFixesModifier from "../modifiers/syncfixesmodifier"; +import SyncParticlesModifier from "../modifiers/syncparticlesmodifier"; import SyncVariablesModifier from "../modifiers/syncvariablesmodifier"; -import { StoreModel } from "./model"; +import type { ModifierInput, ModifierOutput } from "../modifiers/types"; +import type { LammpsWeb } from "../types"; +import type { AtomifyWasmModule } from "../wasm/types"; +import type { StoreModel } from "./model"; const cellMatrix = new THREE.Matrix3(); const origo = new THREE.Vector3(); @@ -18,14 +18,14 @@ const origo = new THREE.Vector3(); const getSimulationBox = ( lammps: LammpsWeb, wasm: AtomifyWasmModule, - currentBox?: THREE.Matrix3, + currentBox?: THREE.Matrix3 ) => { const cellMatrixPointer = lammps.getCellMatrixPointer() / 8; const cellMatrixSubArray = wasm.HEAPF64.subarray( cellMatrixPointer, - cellMatrixPointer + 9, + cellMatrixPointer + 9 ) as Float64Array; - + // Check if values changed if (currentBox) { const elements = currentBox.elements; @@ -41,7 +41,7 @@ const getSimulationBox = ( return currentBox; } } - + // Values changed, return new Matrix3 return new THREE.Matrix3().set( cellMatrixSubArray[0], @@ -52,21 +52,21 @@ const getSimulationBox = ( cellMatrixSubArray[5], cellMatrixSubArray[6], cellMatrixSubArray[7], - cellMatrixSubArray[8], + cellMatrixSubArray[8] ); }; const getSimulationOrigo = ( lammps: LammpsWeb, wasm: AtomifyWasmModule, - currentOrigo?: THREE.Vector3, + currentOrigo?: THREE.Vector3 ) => { const origoPointer = lammps.getOrigoPointer() / 8; const origoPointerSubArray = wasm.HEAPF64.subarray( origoPointer, - origoPointer + 3, + origoPointer + 3 ) as Float64Array; - + // Check if values changed if (currentOrigo) { if ( @@ -78,18 +78,18 @@ const getSimulationOrigo = ( return currentOrigo; } } - + // Values changed, return new Vector3 return new THREE.Vector3( origoPointerSubArray[0], origoPointerSubArray[1], - origoPointerSubArray[2], + origoPointerSubArray[2] ); }; const getModifierContext = ( getStoreState: () => State, - getStoreActions: () => Actions, + getStoreActions: () => Actions ) => { const wasm = window.wasm; const lammps = getStoreState().simulation.lammps; @@ -173,23 +173,12 @@ export const processingModel: ProcessingModel = { state.postTimestepModifiers = value; }), runPostTimestep: thunk( - async ( - actions, - everything: boolean, - { getStoreState, getStoreActions }, - ) => { - const { - modifierInput, - modifierOutput, - allActions, - particles, - bonds, - lammps, - wasm, - } = getModifierContext(getStoreState, getStoreActions); + async (actions, everything: boolean, { getStoreState, getStoreActions }) => { + const { modifierInput, modifierOutput, allActions, particles, bonds, lammps, wasm } = + getModifierContext(getStoreState, getStoreActions); getStoreState().processing.postTimestepModifiers.forEach((modifier) => - modifier.run(modifierInput, modifierOutput, true), + modifier.run(modifierInput, modifierOutput, true) ); allActions.render.setParticleStylesUpdated(false); @@ -212,58 +201,43 @@ export const processingModel: ProcessingModel = { } allActions.simulationStatus.setBox( - getSimulationBox(lammps, wasm, getStoreState().simulationStatus.box), + getSimulationBox(lammps, wasm, getStoreState().simulationStatus.box) ); allActions.simulationStatus.setOrigo( - getSimulationOrigo(lammps, wasm, getStoreState().simulationStatus.origo), + getSimulationOrigo(lammps, wasm, getStoreState().simulationStatus.origo) ); allActions.simulationStatus.setTimesteps(lammps.getTimesteps()); allActions.simulationStatus.setNumAtoms(lammps.getNumAtoms()); allActions.simulationStatus.setRunTimesteps(lammps.getRunTimesteps()); - allActions.simulationStatus.setRunTotalTimesteps( - lammps.getRunTotalTimesteps(), - ); + allActions.simulationStatus.setRunTotalTimesteps(lammps.getRunTotalTimesteps()); allActions.simulationStatus.setLastCommand(lammps.getLastCommand()); allActions.simulationStatus.setMemoryUsage(lammps.getMemoryUsage()); const whichFlag = lammps.getWhichFlag(); - allActions.simulationStatus.setRunType( - whichFlag === 1 ? "Dynamics" : "Minimization", - ); + allActions.simulationStatus.setRunType(whichFlag === 1 ? "Dynamics" : "Minimization"); if (whichFlag !== 0) { // We are not allowed to ask for these values unless whichFlag is 0 - allActions.simulationStatus.setTimestepsPerSecond( - lammps.getTimestepsPerSecond(), - ); + allActions.simulationStatus.setTimestepsPerSecond(lammps.getTimestepsPerSecond()); allActions.simulationStatus.setRemainingTime(lammps.getCPURemain()); } - }, + } ), runPostTimestepRendering: thunk( - async ( - actions, - payload: void, - { getStoreState, getStoreActions }, - ) => { - const { - modifierInput, - modifierOutput, - allActions, - particles, - bonds, - } = getModifierContext(getStoreState, getStoreActions); - + async (actions, payload: void, { getStoreState, getStoreActions }) => { + const { modifierInput, modifierOutput, allActions, particles, bonds } = getModifierContext( + getStoreState, + getStoreActions + ); + // Only run rendering-related modifiers (Particles, Bonds, Colors) const renderingModifiers = getStoreState().processing.postTimestepModifiers.filter( - (modifier: Modifier) => - modifier.name === "Particles" || - modifier.name === "Bonds" || - modifier.name === "Colors" + (modifier: Modifier) => + modifier.name === "Particles" || modifier.name === "Bonds" || modifier.name === "Colors" ) as Modifier[]; - + renderingModifiers.forEach((modifier: Modifier) => - modifier.run(modifierInput, modifierOutput, true), + modifier.run(modifierInput, modifierOutput, true) ); allActions.render.setParticleStylesUpdated(false); @@ -276,6 +250,6 @@ export const processingModel: ProcessingModel = { allActions.render.setBonds(modifierOutput.bonds); } } - }, + } ), }; diff --git a/src/store/render.ts b/src/store/render.ts index 39b30a70..68708fd2 100644 --- a/src/store/render.ts +++ b/src/store/render.ts @@ -1,6 +1,6 @@ -import { action, Action } from "easy-peasy"; -import { Particles, Bonds, Visualizer } from "omovi"; -import { AtomType } from "../utils/atomtypes"; +import { type Action, action } from "easy-peasy"; +import type { Bonds, Particles, Visualizer } from "omovi"; +import type { AtomType } from "../utils/atomtypes"; import { track } from "../utils/metrics"; interface ParticleStyle { diff --git a/src/store/settings.ts b/src/store/settings.ts index 9f6710ba..92a5de93 100644 --- a/src/store/settings.ts +++ b/src/store/settings.ts @@ -1,4 +1,4 @@ -import { action, Action } from "easy-peasy"; +import { type Action, action } from "easy-peasy"; export interface RenderSettings { ssao: boolean; diff --git a/src/store/simulation.ts b/src/store/simulation.ts index 4dd55c01..a52986c5 100644 --- a/src/store/simulation.ts +++ b/src/store/simulation.ts @@ -1,22 +1,22 @@ -import { action, Action, thunk, Thunk, Actions, State } from "easy-peasy"; -import { StoreModel } from "./model"; -import { LammpsWeb } from "../types"; import { notification } from "antd"; -import { AtomTypes, AtomType, hexToRgb } from "../utils/atomtypes"; -import AnalyzeNotebook from "../utils/AnalyzeNotebook"; -import { track, time_event, getEmbeddingParams } from "../utils/metrics"; -import * as THREE from "three"; +import { type Action, type Actions, action, type State, type Thunk, thunk } from "easy-peasy"; import localforage from "localforage"; -import ColorModifier from "../modifiers/colormodifier"; -import Modifier from "../modifiers/modifier"; -import { SimulationFile } from "./app"; +import * as THREE from "three"; +import type ColorModifier from "../modifiers/colormodifier"; +import type Modifier from "../modifiers/modifier"; +import type { LammpsWeb } from "../types"; +import AnalyzeNotebook from "../utils/AnalyzeNotebook"; +import { type AtomType, AtomTypes, hexToRgb } from "../utils/atomtypes"; +import { getEmbeddingParams, time_event, track } from "../utils/metrics"; import { - parseCameraPosition, - parseCameraTarget, + parseAtomSizeAndColor, parseAtomType, parseBond, - parseAtomSizeAndColor, + parseCameraPosition, + parseCameraTarget, } from "../utils/parsers"; +import type { SimulationFile } from "./app"; +import type { StoreModel } from "./model"; localforage.config({ driver: localforage.INDEXEDDB, @@ -104,7 +104,10 @@ export const simulationModel: SimulationModel = { ( actions, inputScript: string, - { getStoreActions, getStoreState }: { getStoreActions: () => Actions; getStoreState: () => State } + { + getStoreActions, + getStoreState, + }: { getStoreActions: () => Actions; getStoreState: () => State } ) => { const lines = inputScript.split("\n"); @@ -118,7 +121,7 @@ export const simulationModel: SimulationModel = { const bondsDistanceMapPointer = lammps.getBondsDistanceMapPointer() / 4; const bondsDistanceMapSubarray = wasm.HEAPF32.subarray( bondsDistanceMapPointer, - bondsDistanceMapPointer + 10000, + bondsDistanceMapPointer + 10000 ) as Float32Array; lines.forEach((line) => { @@ -129,7 +132,7 @@ export const simulationModel: SimulationModel = { const parsedAtomType = parseAtomType(line); if (parsedAtomType) { const atomType: AtomType | undefined = AtomTypes.filter( - (at) => at.fullname === parsedAtomType.atomName, + (at) => at.fullname === parsedAtomType.atomName )[0]; if (atomType) { @@ -159,10 +162,8 @@ export const simulationModel: SimulationModel = { const bond = parseBond(line); if (bond) { // we map the 2D coordinate into 1D - bondsDistanceMapSubarray[100 * bond.atomType1 + bond.atomType2] = - bond.distance; - bondsDistanceMapSubarray[100 * bond.atomType2 + bond.atomType1] = - bond.distance; + bondsDistanceMapSubarray[100 * bond.atomType1 + bond.atomType2] = bond.distance; + bondsDistanceMapSubarray[100 * bond.atomType2 + bond.atomType1] = bond.distance; lammps.setBuildNeighborlist(true); } const cameraPosition = parseCameraPosition(line); @@ -175,10 +176,14 @@ export const simulationModel: SimulationModel = { } } }); - }, + } ), syncFilesWasm: thunk( - async (actions, fileName: string | undefined, { getStoreState }: { getStoreState: () => State }) => { + async ( + actions, + fileName: string | undefined, + { getStoreState }: { getStoreState: () => State } + ) => { const simulation = getStoreState().simulation.simulation; if (!simulation) { return; @@ -191,10 +196,14 @@ export const simulationModel: SimulationModel = { wasm.FS.writeFile(`/${simulation.id}/${file.fileName}`, file.content); } } - }, + } ), syncFilesJupyterLite: thunk( - async (actions, dummy: undefined, { getStoreState }: { getStoreState: () => State }) => { + async ( + actions, + dummy: undefined, + { getStoreState }: { getStoreState: () => State } + ) => { const simulation = getStoreState().simulation.simulation; if (!simulation) { return; @@ -221,18 +230,14 @@ export const simulationModel: SimulationModel = { name: string, path: string, type: JupyterFileType, - content?: string | Object, + content?: string | Object ) => { let mimetype = "text/plain"; let format = "text"; let size = 0; const now = new Date().toISOString(); - if ( - type === "directory" || - type === "notebook" || - typeof content === "object" - ) { + if (type === "directory" || type === "notebook" || typeof content === "object") { mimetype = "application/json"; format = "json"; } @@ -267,13 +272,13 @@ export const simulationModel: SimulationModel = { analyzeFileName, analyzeFileName, "notebook", - AnalyzeNotebook(simulation), - ), + AnalyzeNotebook(simulation) + ) ); await localforage.setItem( simulation.id, - createLocalForageObject(simulation.id, simulation.id, "directory"), + createLocalForageObject(simulation.id, simulation.id, "directory") ); for (const file of Object.values(files)) { let type: JupyterFileType = "file"; @@ -291,134 +296,148 @@ export const simulationModel: SimulationModel = { } await localforage.setItem( filePath, - createLocalForageObject(file.fileName, filePath, type, content), + createLocalForageObject(file.fileName, filePath, type, content) ); } - }, - ), - run: thunk(async (actions, payload, { getStoreState, getStoreActions }: { getStoreState: () => State; getStoreActions: () => Actions }) => { - const simulation = getStoreState().simulation.simulation; - if (!simulation) { - return; - } - const lammps = getStoreState().simulation.lammps; - if (!lammps || lammps.getIsRunning()) { - return; } + ), + run: thunk( + async ( + actions, + payload, + { + getStoreState, + getStoreActions, + }: { getStoreState: () => State; getStoreActions: () => Actions } + ) => { + const simulation = getStoreState().simulation.simulation; + if (!simulation) { + return; + } + const lammps = getStoreState().simulation.lammps; + if (!lammps || lammps.getIsRunning()) { + return; + } - const allActions = getStoreActions() as Actions; - - allActions.render.resetParticleStyles(); - allActions.simulationStatus.reset(); - actions.setShowConsole(false); - actions.resetLammpsOutput(); + const allActions = getStoreActions() as Actions; - await actions.syncFilesWasm(undefined); + allActions.render.resetParticleStyles(); + allActions.simulationStatus.reset(); + actions.setShowConsole(false); + actions.resetLammpsOutput(); - lammps.start(); - actions.setRunning(true); - track("Simulation.Start", { - simulationId: simulation?.id, - ...getEmbeddingParams() - }); - time_event("Simulation.Stop"); + await actions.syncFilesWasm(undefined); - const inputScriptFile = simulation.files.filter( - (file) => file.fileName === simulation.inputScript, - )[0]; - if (inputScriptFile.content) { - actions.extractAndApplyAtomifyCommands(inputScriptFile.content); - } + lammps.start(); + actions.setRunning(true); + track("Simulation.Start", { + simulationId: simulation?.id, + ...getEmbeddingParams(), + }); + time_event("Simulation.Stop"); - // Inject URL variables if provided - if (simulation.vars && Object.keys(simulation.vars).length > 0) { - const varsScript = Object.entries(simulation.vars) - .map(([name, value]) => `variable ${name} equal ${value}`) - .join('\n') + '\n\n'; - - const wasm = (window as any).wasm; - const varsFileName = `_vars_${simulation.inputScript}`; - wasm.FS.writeFile(`/${simulation.id}/${varsFileName}`, varsScript); - - // Create a wrapper script that includes vars then the original script - const wrapperScript = varsScript + inputScriptFile.content; - const wrapperFileName = `_wrapper_${simulation.inputScript}`; - wasm.FS.writeFile(`/${simulation.id}/${wrapperFileName}`, wrapperScript); - } + const inputScriptFile = simulation.files.filter( + (file) => file.fileName === simulation.inputScript + )[0]; + if (inputScriptFile.content) { + actions.extractAndApplyAtomifyCommands(inputScriptFile.content); + } - let errorMessage: string | undefined = undefined; - const startTime = performance.now(); - try { - // Use wrapper script if we have vars, otherwise use original - const scriptToRun = simulation.vars && Object.keys(simulation.vars).length > 0 - ? `_wrapper_${simulation.inputScript}` - : simulation.inputScript; - await lammps.runFile(`/${simulation.id}/${scriptToRun}`); - } catch (exception: any) { - console.log("Got exception: ", exception); - errorMessage = lammps.getExceptionMessage(exception); - console.log("Got error running LAMMPS: ", errorMessage); - } + // Inject URL variables if provided + if (simulation.vars && Object.keys(simulation.vars).length > 0) { + const varsScript = + Object.entries(simulation.vars) + .map(([name, value]) => `variable ${name} equal ${value}`) + .join("\n") + "\n\n"; + + const wasm = (window as any).wasm; + const varsFileName = `_vars_${simulation.inputScript}`; + wasm.FS.writeFile(`/${simulation.id}/${varsFileName}`, varsScript); + + // Create a wrapper script that includes vars then the original script + const wrapperScript = varsScript + inputScriptFile.content; + const wrapperFileName = `_wrapper_${simulation.inputScript}`; + wasm.FS.writeFile(`/${simulation.id}/${wrapperFileName}`, wrapperScript); + } - if (!errorMessage) { - errorMessage = lammps.getErrorMessage(); - } + let errorMessage: string | undefined; + const startTime = performance.now(); + try { + // Use wrapper script if we have vars, otherwise use original + const scriptToRun = + simulation.vars && Object.keys(simulation.vars).length > 0 + ? `_wrapper_${simulation.inputScript}` + : simulation.inputScript; + await lammps.runFile(`/${simulation.id}/${scriptToRun}`); + } catch (exception: any) { + console.log("Got exception: ", exception); + errorMessage = lammps.getExceptionMessage(exception); + console.log("Got error running LAMMPS: ", errorMessage); + } - const computes = getStoreState().simulationStatus.computes; + if (!errorMessage) { + errorMessage = lammps.getErrorMessage(); + } - const endTime = performance.now(); - const duration = (endTime - startTime) / 1000; // seconds - const metricsData = { - timesteps: lammps.getTimesteps(), - timestepsPerSecond: (lammps.getTimesteps() / duration).toFixed(3), - numAtoms: lammps.getNumAtoms(), - computes: Object.keys(computes), - }; - actions.setPaused(false); + const computes = getStoreState().simulationStatus.computes; - if (errorMessage) { - if (errorMessage.includes("Atomify::canceled")) { - allActions.processing.runPostTimestep(true); - // Simulation got canceled. - actions.setRunning(false); - actions.setShowConsole(true); - track("Simulation.Stop", { - simulationId: simulation?.id, - stopReason: "canceled", - ...metricsData, - }); + const endTime = performance.now(); + const duration = (endTime - startTime) / 1000; // seconds + const metricsData = { + timesteps: lammps.getTimesteps(), + timestepsPerSecond: (lammps.getTimesteps() / duration).toFixed(3), + numAtoms: lammps.getNumAtoms(), + computes: Object.keys(computes), + }; + actions.setPaused(false); + + if (errorMessage) { + if (errorMessage.includes("Atomify::canceled")) { + allActions.processing.runPostTimestep(true); + // Simulation got canceled. + actions.setRunning(false); + actions.setShowConsole(true); + track("Simulation.Stop", { + simulationId: simulation?.id, + stopReason: "canceled", + ...metricsData, + }); + } else { + notification.error({ + message: errorMessage, + duration: 5, + }); + actions.setRunning(false); + actions.setShowConsole(true); + track("Simulation.Stop", { + simulationId: simulation?.id, + stopReason: "failed", + errorMessage, + ...metricsData, + }); + } } else { - notification.error({ - message: errorMessage, - duration: 5, - }); + allActions.processing.runPostTimestep(true); actions.setRunning(false); actions.setShowConsole(true); track("Simulation.Stop", { simulationId: simulation?.id, - stopReason: "failed", - errorMessage, + stopReason: "completed", ...metricsData, }); } - } else { - allActions.processing.runPostTimestep(true); - actions.setRunning(false); - actions.setShowConsole(true); - track("Simulation.Stop", { - simulationId: simulation?.id, - stopReason: "completed", - ...metricsData, - }); + actions.syncFilesJupyterLite(); + allActions.simulationStatus.setLastCommand(undefined); } - actions.syncFilesJupyterLite(); - allActions.simulationStatus.setLastCommand(undefined); - }), + ), newSimulation: thunk( async ( actions, simulation: Simulation, - { getStoreState, getStoreActions }: { getStoreState: () => State; getStoreActions: () => Actions }, + { + getStoreState, + getStoreActions, + }: { getStoreState: () => State; getStoreActions: () => Actions } ) => { const allActions = getStoreActions() as Actions; @@ -429,10 +448,9 @@ export const simulationModel: SimulationModel = { actions.resetLammpsOutput(); // Reset potentially chosen per atom coloring - const postTimestepModifiers = - getStoreState().processing.postTimestepModifiers; + const postTimestepModifiers = getStoreState().processing.postTimestepModifiers; const colorModifier = postTimestepModifiers.filter( - (modifier: Modifier) => modifier.name === "Colors", + (modifier: Modifier) => modifier.name === "Colors" )[0] as ColorModifier; if (colorModifier) { colorModifier.computeName = undefined; @@ -454,7 +472,7 @@ export const simulationModel: SimulationModel = { const bondsDistanceMapPointer = lammps.getBondsDistanceMapPointer() / 4; const bondsDistanceMapSubarray = wasm.HEAPF32.subarray( bondsDistanceMapPointer, - bondsDistanceMapPointer + 10000, + bondsDistanceMapPointer + 10000 ) as Float32Array; bondsDistanceMapSubarray.fill(0); lammps.setBuildNeighborlist(false); @@ -489,21 +507,21 @@ export const simulationModel: SimulationModel = { actions.setSimulation(simulation); // Set it again now that files are updated wasm.FS.chdir(`/${simulation.id}`); - + // Sync files to JupyterLite storage now that they're available in WASM filesystem await actions.syncFilesJupyterLite(); - + await allActions.app.setStatus(undefined); if (simulation.start) { actions.run(); } else { const inputScriptFile = simulation.files.filter( - (file) => file.fileName === simulation.inputScript, + (file) => file.fileName === simulation.inputScript )[0]; allActions.app.setSelectedFile(inputScriptFile); } track("Simulation.New", { simulationId: simulation?.id }); - }, + } ), reset: action((state) => { state.files = []; diff --git a/src/store/simulationstatus.ts b/src/store/simulationstatus.ts index 4aaa0d44..c5921a7f 100644 --- a/src/store/simulationstatus.ts +++ b/src/store/simulationstatus.ts @@ -1,6 +1,6 @@ -import { action, Action } from "easy-peasy"; -import { Compute, Fix, Variable } from "../types"; -import * as THREE from "three"; +import { type Action, action } from "easy-peasy"; +import type * as THREE from "three"; +import type { Compute, Fix, Variable } from "../types"; export interface SimulationStatusModel { timesteps: number; @@ -107,7 +107,7 @@ export const simulationStatusModel: SimulationStatusModel = { setModifierSyncDataPoints: action( ( state, - { name, type, value }: { name: string; type: "compute" | "fix" | "variable"; value: boolean }, + { name, type, value }: { name: string; type: "compute" | "fix" | "variable"; value: boolean } ) => { switch (type) { case "compute": @@ -126,7 +126,7 @@ export const simulationStatusModel: SimulationStatusModel = { } break; } - }, + } ), reset: action((state) => { state.hasSynchronized = false; diff --git a/src/types.ts b/src/types.ts index 94a189b5..8440a8ee 100644 --- a/src/types.ts +++ b/src/types.ts @@ -175,7 +175,7 @@ export type GithubFile = { /** * Configuration options for embedded simulations. * These can be passed via base64-encoded JSON in the 'config' URL parameter. - * + * * Note: When parsed from URL, all properties are guaranteed to have values (defaults applied). */ export interface EmbedConfig { diff --git a/src/utils/AnalyzeNotebook.test.ts b/src/utils/AnalyzeNotebook.test.ts index 8c56b285..598f2661 100644 --- a/src/utils/AnalyzeNotebook.test.ts +++ b/src/utils/AnalyzeNotebook.test.ts @@ -1,6 +1,6 @@ -import { describe, it, expect } from "vitest"; +import { describe, expect, it } from "vitest"; +import type { Simulation } from "../store/simulation"; import AnalyzeNotebook from "./AnalyzeNotebook"; -import { Simulation } from "../store/simulation"; // Helper function to create mock simulation with defaults const createMockSimulation = (overrides: Partial = {}): Simulation => ({ @@ -71,9 +71,7 @@ describe("AnalyzeNotebook", () => { // Assert expect(notebook.cells).toHaveLength(4); // markdown + 3 default cells expect(notebook.cells[0].cell_type).toBe("markdown"); - expect(notebook.cells[0].source).toBe( - "# Analysis\nThis is a test analysis description" - ); + expect(notebook.cells[0].source).toBe("# Analysis\nThis is a test analysis description"); }); it("should place markdown cell before pip install cell", () => { @@ -155,4 +153,3 @@ describe("AnalyzeNotebook", () => { expect(notebook.cells[3].source).toBe(""); }); }); - diff --git a/src/utils/AnalyzeNotebook.ts b/src/utils/AnalyzeNotebook.ts index e68faa34..b7a776ac 100644 --- a/src/utils/AnalyzeNotebook.ts +++ b/src/utils/AnalyzeNotebook.ts @@ -1,9 +1,5 @@ -import { Simulation } from "../store/simulation"; -import type { - INotebookContent, - ICodeCell, - IMarkdownCell, -} from "@jupyterlab/nbformat"; +import type { ICodeCell, IMarkdownCell, INotebookContent } from "@jupyterlab/nbformat"; +import type { Simulation } from "../store/simulation"; const AnalyzeNotebook = (simulation: Simulation): INotebookContent => { const cells: ICodeCell[] = [ diff --git a/src/utils/atomtypes.test.ts b/src/utils/atomtypes.test.ts index f1a97723..55ee1d16 100644 --- a/src/utils/atomtypes.test.ts +++ b/src/utils/atomtypes.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect } from "vitest"; +import { describe, expect, it } from "vitest"; import { hexToRgb } from "./atomtypes"; describe("hexToRgb", () => { @@ -112,4 +112,3 @@ describe("hexToRgb", () => { expect(() => hexToRgb(empty)).toThrow("Invalid hex color string"); }); }); - diff --git a/src/utils/atomtypes.ts b/src/utils/atomtypes.ts index 4bf76586..0ce56771 100644 --- a/src/utils/atomtypes.ts +++ b/src/utils/atomtypes.ts @@ -6,11 +6,7 @@ export const hexToRgb = (hex: string) => { if (!result) { throw new Error(`Invalid hex color string: ${hex}`); } - return [ - parseInt(result[1], 16), - parseInt(result[2], 16), - parseInt(result[3], 16), - ]; + return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)]; }; export interface AtomType { diff --git a/src/utils/boxGeometry.test.ts b/src/utils/boxGeometry.test.ts index c70582c3..9a790f32 100644 --- a/src/utils/boxGeometry.test.ts +++ b/src/utils/boxGeometry.test.ts @@ -1,8 +1,8 @@ -import { describe, it, expect } from "vitest"; import * as THREE from "three"; +import { describe, expect, it } from "vitest"; import { - createBoxGeometry, calculateBoxRadius, + createBoxGeometry, extractBasisVectors, getSimulationBoxBounds, } from "./boxGeometry"; diff --git a/src/utils/boxGeometry.ts b/src/utils/boxGeometry.ts index aef911f1..87438b90 100644 --- a/src/utils/boxGeometry.ts +++ b/src/utils/boxGeometry.ts @@ -15,9 +15,9 @@ const MIN_NORMALIZED_LENGTH = 0.001; * @returns Object containing the three basis vectors { a, b, c } */ export function extractBasisVectors(cellMatrix: THREE.Matrix3): { - a: THREE.Vector3 - b: THREE.Vector3 - c: THREE.Vector3 + a: THREE.Vector3; + b: THREE.Vector3; + c: THREE.Vector3; } { // LAMMPS stores vectors as rows, but extractBasis gets columns - transpose first const transposed = cellMatrix.clone().transpose(); @@ -26,7 +26,7 @@ export function extractBasisVectors(cellMatrix: THREE.Matrix3): { const c = new THREE.Vector3(); transposed.extractBasis(a, b, c); - return { a, b, c } + return { a, b, c }; } /** @@ -47,7 +47,7 @@ export function calculateBoxRadius(cellMatrix: THREE.Matrix3): number { * Creates a Group of cylinders for a parallelepiped wireframe from a cell matrix and origin. * Handles both orthogonal and triclinic (non-orthogonal) simulation boxes. * Uses cylinders instead of lines to ensure proper thickness across all systems. - * + * * @param cellMatrix - THREE.Matrix3 where rows represent the a, b, c vectors (LAMMPS convention) * @param origin - THREE.Vector3 representing the origin point * @param radius - Radius of the cylinder edges (default: MIN_RADIUS) @@ -56,7 +56,7 @@ export function calculateBoxRadius(cellMatrix: THREE.Matrix3): number { export function createBoxGeometry( cellMatrix: THREE.Matrix3, origin: THREE.Vector3, - radius: number = MIN_RADIUS, + radius: number = MIN_RADIUS ): THREE.Group { // Extract basis vectors using shared helper function const { a, b, c } = extractBasisVectors(cellMatrix); @@ -64,13 +64,32 @@ export function createBoxGeometry( // Compute the 8 vertices of the parallelepiped const vertices: THREE.Vector3[] = [ origin.clone(), // v0 - origin.clone().add(a), // v1 - origin.clone().add(b), // v2 - origin.clone().add(c), // v3 - origin.clone().add(a).add(b), // v4 - origin.clone().add(a).add(c), // v5 - origin.clone().add(b).add(c), // v6 - origin.clone().add(a).add(b).add(c), // v7 + origin + .clone() + .add(a), // v1 + origin + .clone() + .add(b), // v2 + origin + .clone() + .add(c), // v3 + origin + .clone() + .add(a) + .add(b), // v4 + origin + .clone() + .add(a) + .add(c), // v5 + origin + .clone() + .add(b) + .add(c), // v6 + origin + .clone() + .add(a) + .add(b) + .add(c), // v7 ]; // Define the 12 edges of the parallelepiped @@ -94,7 +113,7 @@ export function createBoxGeometry( ]; const group = new THREE.Group(); - + // Create material once and reuse for all edges const material = new THREE.MeshBasicMaterial({ color: 0xffffff, @@ -113,12 +132,7 @@ export function createBoxGeometry( } // Create cylinder geometry (default orientation is along Y-axis) - const geometry = new THREE.CylinderGeometry( - radius, - radius, - length, - CYLINDER_RADIAL_SEGMENTS, - ); + const geometry = new THREE.CylinderGeometry(radius, radius, length, CYLINDER_RADIAL_SEGMENTS); // Create mesh const cylinder = new THREE.Mesh(geometry, material); @@ -126,10 +140,10 @@ export function createBoxGeometry( // Position at midpoint const midpoint = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5); cylinder.position.copy(midpoint); - + // Orient cylinder along the edge direction const targetAxis = direction.clone().normalize(); - + // Validate targetAxis is valid if ( !isFinite(targetAxis.x) || @@ -141,13 +155,10 @@ export function createBoxGeometry( geometry.dispose(); return; } - + // Align cylinder to edge direction - cylinder.quaternion.setFromUnitVectors( - new THREE.Vector3(0, 1, 0), - targetAxis - ); - + cylinder.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), targetAxis); + // Validate final quaternion if ( !isFinite(cylinder.quaternion.x) || @@ -168,14 +179,14 @@ export function createBoxGeometry( /** * Calculates the axis-aligned bounding box (AABB) of a simulation box parallelepiped. * Handles both orthogonal and triclinic (non-orthogonal) simulation boxes. - * + * * @param cellMatrix - THREE.Matrix3 where rows represent the a, b, c vectors (LAMMPS convention) * @param origin - THREE.Vector3 representing the origin point * @returns THREE.Box3 representing the axis-aligned bounding box */ export function getSimulationBoxBounds( cellMatrix: THREE.Matrix3, - origin: THREE.Vector3, + origin: THREE.Vector3 ): THREE.Box3 { // Extract basis vectors using shared helper function const { a, b, c } = extractBasisVectors(cellMatrix); @@ -183,13 +194,32 @@ export function getSimulationBoxBounds( // Compute the 8 vertices of the parallelepiped const vertices: THREE.Vector3[] = [ origin.clone(), // v0 - origin.clone().add(a), // v1 - origin.clone().add(b), // v2 - origin.clone().add(c), // v3 - origin.clone().add(a).add(b), // v4 - origin.clone().add(a).add(c), // v5 - origin.clone().add(b).add(c), // v6 - origin.clone().add(a).add(b).add(c), // v7 + origin + .clone() + .add(a), // v1 + origin + .clone() + .add(b), // v2 + origin + .clone() + .add(c), // v3 + origin + .clone() + .add(a) + .add(b), // v4 + origin + .clone() + .add(a) + .add(c), // v5 + origin + .clone() + .add(b) + .add(c), // v6 + origin + .clone() + .add(a) + .add(b) + .add(c), // v7 ]; // Create bounding box from all vertices @@ -197,5 +227,3 @@ export function getSimulationBoxBounds( return box; } - - diff --git a/src/utils/embed/codec.test.ts b/src/utils/embed/codec.test.ts index 3b0e164b..2dfa14c8 100644 --- a/src/utils/embed/codec.test.ts +++ b/src/utils/embed/codec.test.ts @@ -1,5 +1,5 @@ -import { describe, it, expect, beforeEach, afterEach } from "vitest"; -import { u8aToBase64, base64ToU8A } from "./codec"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { base64ToU8A, u8aToBase64 } from "./codec"; // Store original implementations for restoration let originalBtoa: typeof global.btoa; @@ -207,4 +207,3 @@ describe("base64 round-trip", () => { expect(decoded).toEqual(original); }); }); - diff --git a/src/utils/embed/codec.ts b/src/utils/embed/codec.ts index ef43f6a8..14079300 100644 --- a/src/utils/embed/codec.ts +++ b/src/utils/embed/codec.ts @@ -1,9 +1,9 @@ // Encoding/decoding utilities for embedding simulations in URLs // Based on https://github.com/whitphx/stlite/tree/main/packages/sharing-common -import { SimulationData } from './proto'; -import { Simulation } from '../../store/simulation'; -import { SimulationFile } from '../../store/app'; +import type { SimulationFile } from "../../store/app"; +import type { Simulation } from "../../store/simulation"; +import { SimulationData } from "./proto"; /** * Ad-hoc value that works at least on Chromium: 105.0.5195.102(Official Build) (arm64). @@ -15,7 +15,7 @@ export function u8aToBase64(buf: Uint8Array, applyMax?: number): string { // If `buf` is too long, `String.fromCharCode.apply(null, buf)` // throws `RangeError: Maximum call stack size exceeded`, // so we split the buffer into chunks and process them one by one. - let str = ''; + let str = ""; const chunkSize = applyMax ?? DEFAULT_APPLY_MAX; const nChunks = Math.ceil(buf.length / chunkSize); for (let i = 0; i < nChunks; ++i) { @@ -41,11 +41,11 @@ export function base64ToU8A(base64: string): Uint8Array { // * https://en.wikipedia.org/wiki/Base64 // * https://datatracker.ietf.org/doc/html/rfc4648#section-5 function b64ToB64url(base64: string): string { - return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ','); + return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ","); } function b64urlToB64(hashSafe: string): string { - return hashSafe.replace(/-/g, '+').replace(/_/g, '/').replace(/,/g, '='); + return hashSafe.replace(/-/g, "+").replace(/_/g, "/").replace(/,/g, "="); } /** @@ -55,12 +55,12 @@ async function ensureFileContent(file: SimulationFile): Promise { if (file.content) { return file.content; } - + if (file.url) { const response = await fetch(file.url); return await response.text(); } - + throw new Error(`File ${file.fileName} has neither content nor url`); } @@ -72,19 +72,19 @@ export async function encodeSimulation(simulation: Simulation): Promise id: simulation.id, inputScript: simulation.inputScript, analysisScript: simulation.analysisScript, - files: {} + files: {}, }; // Fetch all file contents for (const file of simulation.files) { const content = await ensureFileContent(file); simulationData.files[file.fileName] = { - content: { $case: 'text', text: content } + content: { $case: "text", text: content }, }; } const encodedProto = SimulationData.encode(simulationData).finish(); - + // NOTE: Both `u8aToBase64(encodedProto)` and `u8aToBase64(new Uint8Array(encodedProto))` causes an error: https://github.com/whitphx/stlite/issues/235 // Creating a new array buffer with `Uint8Array.from(encodedProto)` and passing it as below is necessary. // @@ -109,18 +109,18 @@ export function decodeSimulation(base64url: string, autoStart: boolean = true): const simulationData = SimulationData.decode(buf); const files: SimulationFile[] = Object.entries(simulationData.files).map(([fileName, file]) => { - let content = ''; - if (file.content?.$case === 'text') { + let content = ""; + if (file.content?.$case === "text") { content = file.content.text; - } else if (file.content?.$case === 'data') { + } else if (file.content?.$case === "data") { // Convert binary data to text if needed const decoder = new TextDecoder(); content = decoder.decode(file.content.data); } - + return { fileName, - content + content, }; }); @@ -129,7 +129,6 @@ export function decodeSimulation(base64url: string, autoStart: boolean = true): inputScript: simulationData.inputScript, analysisScript: simulationData.analysisScript, files, - start: autoStart + start: autoStart, }; } - diff --git a/src/utils/embed/proto.ts b/src/utils/embed/proto.ts index 1574de81..6564a513 100644 --- a/src/utils/embed/proto.ts +++ b/src/utils/embed/proto.ts @@ -2,9 +2,9 @@ // Most of this is borrowed from https://github.com/whitphx/stlite/tree/main/packages/sharing-common // Adapted for Atomify simulations -import _m0 from 'protobufjs/minimal'; +import _m0 from "protobufjs/minimal"; -export const protobufPackage = ''; +export const protobufPackage = ""; export interface SimulationData { id: string; @@ -19,27 +19,22 @@ export interface SimulationData_FilesEntry { } export interface File { - content?: - | { $case: 'text'; text: string } - | { $case: 'data'; data: Uint8Array }; + content?: { $case: "text"; text: string } | { $case: "data"; data: Uint8Array }; } function createBaseSimulationData(): SimulationData { - return { id: '', inputScript: '', analysisScript: undefined, files: {} }; + return { id: "", inputScript: "", analysisScript: undefined, files: {} }; } export const SimulationData = { - encode( - message: SimulationData, - writer: _m0.Writer = _m0.Writer.create() - ): _m0.Writer { - if (message.id !== '') { + encode(message: SimulationData, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.id !== "") { writer.uint32(10).string(message.id); } - if (message.inputScript !== '') { + if (message.inputScript !== "") { writer.uint32(18).string(message.inputScript); } - if (message.analysisScript !== undefined && message.analysisScript !== '') { + if (message.analysisScript !== undefined && message.analysisScript !== "") { writer.uint32(26).string(message.analysisScript); } Object.entries(message.files).forEach(([key, value]) => { @@ -53,7 +48,7 @@ export const SimulationData = { decode(input: _m0.Reader | Uint8Array, length?: number): SimulationData { const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); - let end = length === undefined ? reader.len : reader.pos + length; + const end = length === undefined ? reader.len : reader.pos + length; const message = createBaseSimulationData(); while (reader.pos < end) { const tag = reader.uint32(); @@ -67,12 +62,13 @@ export const SimulationData = { case 3: message.analysisScript = reader.string(); break; - case 4: + case 4: { const entry4 = SimulationData_FilesEntry.decode(reader, reader.uint32()); if (entry4.value !== undefined) { message.files[entry4.key] = entry4.value; } break; + } default: reader.skipType(tag & 7); break; @@ -83,18 +79,15 @@ export const SimulationData = { fromJSON(object: any): SimulationData { return { - id: isSet(object.id) ? String(object.id) : '', - inputScript: isSet(object.inputScript) ? String(object.inputScript) : '', + id: isSet(object.id) ? String(object.id) : "", + inputScript: isSet(object.inputScript) ? String(object.inputScript) : "", analysisScript: isSet(object.analysisScript) ? String(object.analysisScript) : undefined, files: isObject(object.files) - ? Object.entries(object.files).reduce<{ [key: string]: File }>( - (acc, [key, value]) => { - acc[key] = File.fromJSON(value); - return acc; - }, - {} - ) - : {} + ? Object.entries(object.files).reduce<{ [key: string]: File }>((acc, [key, value]) => { + acc[key] = File.fromJSON(value); + return acc; + }, {}) + : {}, }; }, @@ -114,8 +107,8 @@ export const SimulationData = { fromPartial, I>>(object: I): SimulationData { const message = createBaseSimulationData(); - message.id = object.id ?? ''; - message.inputScript = object.inputScript ?? ''; + message.id = object.id ?? ""; + message.inputScript = object.inputScript ?? ""; message.analysisScript = object.analysisScript ?? undefined; message.files = Object.entries(object.files ?? {}).reduce<{ [key: string]: File; @@ -126,19 +119,16 @@ export const SimulationData = { return acc; }, {}); return message; - } + }, }; function createBaseSimulationData_FilesEntry(): SimulationData_FilesEntry { - return { key: '', value: undefined }; + return { key: "", value: undefined }; } export const SimulationData_FilesEntry = { - encode( - message: SimulationData_FilesEntry, - writer: _m0.Writer = _m0.Writer.create() - ): _m0.Writer { - if (message.key !== '') { + encode(message: SimulationData_FilesEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.key !== "") { writer.uint32(10).string(message.key); } if (message.value !== undefined) { @@ -149,7 +139,7 @@ export const SimulationData_FilesEntry = { decode(input: _m0.Reader | Uint8Array, length?: number): SimulationData_FilesEntry { const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); - let end = length === undefined ? reader.len : reader.pos + length; + const end = length === undefined ? reader.len : reader.pos + length; const message = createBaseSimulationData_FilesEntry(); while (reader.pos < end) { const tag = reader.uint32(); @@ -170,8 +160,8 @@ export const SimulationData_FilesEntry = { fromJSON(object: any): SimulationData_FilesEntry { return { - key: isSet(object.key) ? String(object.key) : '', - value: isSet(object.value) ? File.fromJSON(object.value) : undefined + key: isSet(object.key) ? String(object.key) : "", + value: isSet(object.value) ? File.fromJSON(object.value) : undefined, }; }, @@ -187,13 +177,13 @@ export const SimulationData_FilesEntry = { object: I ): SimulationData_FilesEntry { const message = createBaseSimulationData_FilesEntry(); - message.key = object.key ?? ''; + message.key = object.key ?? ""; message.value = object.value !== undefined && object.value !== null ? File.fromPartial(object.value) : undefined; return message; - } + }, }; function createBaseFile(): File { @@ -202,10 +192,10 @@ function createBaseFile(): File { export const File = { encode(message: File, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.content?.$case === 'text') { + if (message.content?.$case === "text") { writer.uint32(10).string(message.content.text); } - if (message.content?.$case === 'data') { + if (message.content?.$case === "data") { writer.uint32(18).bytes(message.content.data); } return writer; @@ -213,16 +203,16 @@ export const File = { decode(input: _m0.Reader | Uint8Array, length?: number): File { const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); - let end = length === undefined ? reader.len : reader.pos + length; + const end = length === undefined ? reader.len : reader.pos + length; const message = createBaseFile(); while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { case 1: - message.content = { $case: 'text', text: reader.string() }; + message.content = { $case: "text", text: reader.string() }; break; case 2: - message.content = { $case: 'data', data: reader.bytes() }; + message.content = { $case: "data", data: reader.bytes() }; break; default: reader.skipType(tag & 7); @@ -235,66 +225,64 @@ export const File = { fromJSON(object: any): File { return { content: isSet(object.text) - ? { $case: 'text', text: String(object.text) } + ? { $case: "text", text: String(object.text) } : isSet(object.data) - ? { $case: 'data', data: bytesFromBase64(object.data) } - : undefined + ? { $case: "data", data: bytesFromBase64(object.data) } + : undefined, }; }, toJSON(message: File): unknown { const obj: any = {}; - message.content?.$case === 'text' && (obj.text = message.content?.text); - message.content?.$case === 'data' && + message.content?.$case === "text" && (obj.text = message.content?.text); + message.content?.$case === "data" && (obj.data = - message.content?.data !== undefined - ? base64FromBytes(message.content?.data) - : undefined); + message.content?.data !== undefined ? base64FromBytes(message.content?.data) : undefined); return obj; }, fromPartial, I>>(object: I): File { const message = createBaseFile(); if ( - object.content?.$case === 'text' && + object.content?.$case === "text" && object.content?.text !== undefined && object.content?.text !== null ) { - message.content = { $case: 'text', text: object.content.text }; + message.content = { $case: "text", text: object.content.text }; } if ( - object.content?.$case === 'data' && + object.content?.$case === "data" && object.content?.data !== undefined && object.content?.data !== null ) { - message.content = { $case: 'data', data: object.content.data }; + message.content = { $case: "data", data: object.content.data }; } return message; - } + }, }; declare var self: any | undefined; declare var window: any | undefined; declare var global: any | undefined; var globalThis: any = (() => { - if (typeof globalThis !== 'undefined') { + if (typeof globalThis !== "undefined") { return globalThis; } - if (typeof self !== 'undefined') { + if (typeof self !== "undefined") { return self; } - if (typeof window !== 'undefined') { + if (typeof window !== "undefined") { return window; } - if (typeof global !== 'undefined') { + if (typeof global !== "undefined") { return global; } - throw 'Unable to locate global object'; + throw "Unable to locate global object"; })(); function bytesFromBase64(b64: string): Uint8Array { if (globalThis.Buffer) { - return Uint8Array.from(globalThis.Buffer.from(b64, 'base64')); + return Uint8Array.from(globalThis.Buffer.from(b64, "base64")); } else { const bin = globalThis.atob(b64); const arr = new Uint8Array(bin.length); @@ -307,38 +295,31 @@ function bytesFromBase64(b64: string): Uint8Array { function base64FromBytes(arr: Uint8Array): string { if (globalThis.Buffer) { - return globalThis.Buffer.from(arr).toString('base64'); + return globalThis.Buffer.from(arr).toString("base64"); } else { const bin: string[] = []; - arr.forEach(byte => { + arr.forEach((byte) => { bin.push(String.fromCharCode(byte)); }); - return globalThis.btoa(bin.join('')); + return globalThis.btoa(bin.join("")); } } -type Builtin = - | Date - | Function - | Uint8Array - | string - | number - | boolean - | undefined; +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; export type DeepPartial = T extends Builtin ? T : T extends Array - ? Array> - : T extends ReadonlyArray - ? ReadonlyArray> - : T extends { $case: string } - ? { [K in keyof Omit]?: DeepPartial } & { - $case: T['$case']; - } - : T extends {} - ? { [K in keyof T]?: DeepPartial } - : Partial; + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends { $case: string } + ? { [K in keyof Omit]?: DeepPartial } & { + $case: T["$case"]; + } + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; type KeysOfUnion = T extends T ? keyof T : never; @@ -349,10 +330,9 @@ export type Exact = P extends Builtin }; function isObject(value: any): boolean { - return typeof value === 'object' && value !== null; + return typeof value === "object" && value !== null; } function isSet(value: any): boolean { return value !== null && value !== undefined; } - diff --git a/src/utils/embeddedMode.test.ts b/src/utils/embeddedMode.test.ts index 8ac550b8..5a467212 100644 --- a/src/utils/embeddedMode.test.ts +++ b/src/utils/embeddedMode.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { isEmbeddedMode } from "./embeddedMode"; describe("isEmbeddedMode", () => { @@ -130,7 +130,9 @@ describe("isEmbeddedMode", () => { it("should return true when both embeddedSimulationUrl and data params are present (prioritizes embeddedSimulationUrl)", () => { // Arrange - mockWindowLocation("?embeddedSimulationUrl=https://example.com/sim.json&simulationIndex=0&data=someData&embed=true"); + mockWindowLocation( + "?embeddedSimulationUrl=https://example.com/sim.json&simulationIndex=0&data=someData&embed=true" + ); // Act const result = isEmbeddedMode(); @@ -152,7 +154,9 @@ describe("isEmbeddedMode", () => { it("should handle complex URL with mixed params", () => { // Arrange - mockWindowLocation("?foo=bar&embeddedSimulationUrl=https://example.com/file.json&simulationIndex=2&baz=qux"); + mockWindowLocation( + "?foo=bar&embeddedSimulationUrl=https://example.com/file.json&simulationIndex=2&baz=qux" + ); // Act const result = isEmbeddedMode(); @@ -163,7 +167,9 @@ describe("isEmbeddedMode", () => { it("should accept URLSearchParams as parameter", () => { // Arrange - const params = new URLSearchParams("?embeddedSimulationUrl=https://example.com/sim.json&simulationIndex=0"); + const params = new URLSearchParams( + "?embeddedSimulationUrl=https://example.com/sim.json&simulationIndex=0" + ); // Act const result = isEmbeddedMode(params); @@ -186,7 +192,9 @@ describe("isEmbeddedMode", () => { it("should handle invalid simulationIndex gracefully", () => { // Arrange - mockWindowLocation("?embeddedSimulationUrl=https://example.com/sim.json&simulationIndex=invalid"); + mockWindowLocation( + "?embeddedSimulationUrl=https://example.com/sim.json&simulationIndex=invalid" + ); // Act const result = isEmbeddedMode(); @@ -220,4 +228,3 @@ describe("isEmbeddedMode", () => { expect(result).toBe(false); }); }); - diff --git a/src/utils/embeddedMode.ts b/src/utils/embeddedMode.ts index 642a6985..0ba19d38 100644 --- a/src/utils/embeddedMode.ts +++ b/src/utils/embeddedMode.ts @@ -1,28 +1,26 @@ /** * Check if the application is running in embedded mode based on URL parameters. - * + * * Embedded mode is true when: * 1. Using embeddedSimulationUrl method (with valid simulationIndex >= 0), OR * 2. Using data parameter WITH explicit embed=true - * + * * @param urlSearchParams - URLSearchParams object (defaults to window.location.search) * @returns true if in embedded mode, false otherwise */ export function isEmbeddedMode(urlSearchParams?: URLSearchParams): boolean { // Handle server-side rendering or missing window - if (typeof window === 'undefined') { + if (typeof window === "undefined") { return false; } const params = urlSearchParams || new URLSearchParams(window.location.search); - const embeddedSimulationUrl = params.get('embeddedSimulationUrl'); - const simulationIndex = parseInt(params.get('simulationIndex') || '0', 10); - const embeddedData = params.get('data'); - const embedParam = params.get('embed'); + const embeddedSimulationUrl = params.get("embeddedSimulationUrl"); + const simulationIndex = parseInt(params.get("simulationIndex") || "0", 10); + const embeddedData = params.get("data"); + const embedParam = params.get("embed"); return Boolean( - (embeddedSimulationUrl && simulationIndex >= 0) || - (embeddedData && embedParam === 'true') + (embeddedSimulationUrl && simulationIndex >= 0) || (embeddedData && embedParam === "true") ); } - diff --git a/src/utils/metrics.test.ts b/src/utils/metrics.test.ts index 920fba9e..76250ef6 100644 --- a/src/utils/metrics.test.ts +++ b/src/utils/metrics.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { getEmbeddingParams } from "./metrics"; describe("getEmbeddingParams", () => { @@ -167,4 +167,3 @@ describe("getEmbeddingParams", () => { expect(result.embedAutoStart).toBe(false); }); }); - diff --git a/src/utils/metrics.ts b/src/utils/metrics.ts index c21978b4..390b03f2 100644 --- a/src/utils/metrics.ts +++ b/src/utils/metrics.ts @@ -1,14 +1,15 @@ -import mixpanel, { Dict } from "mixpanel-browser"; +import mixpanel, { type Dict } from "mixpanel-browser"; import { v4 as uuidv4 } from "uuid"; + let userId: string | null = null; export function getEmbeddingParams() { const urlParams = new URLSearchParams(window.location.search); - const embeddedSimulationUrl = urlParams.get('embeddedSimulationUrl'); - const embeddedData = urlParams.get('data'); - const embedFullscreen = urlParams.get('embed') === 'true'; - const embedAutoStart = urlParams.get('autostart') === 'true'; - const embedMode = embeddedSimulationUrl ? 'url' : embeddedData ? 'data' : false; + const embeddedSimulationUrl = urlParams.get("embeddedSimulationUrl"); + const embeddedData = urlParams.get("data"); + const embedFullscreen = urlParams.get("embed") === "true"; + const embedAutoStart = urlParams.get("autostart") === "true"; + const embedMode = embeddedSimulationUrl ? "url" : embeddedData ? "data" : false; return { embedMode, embedFullscreen, embedAutoStart }; } @@ -22,10 +23,7 @@ export const track = (event_name: string, properties?: Dict) => { } } - if ( - window.location.hostname === "localhost" || - window.location.hostname === "127.0.0.1" - ) { + if (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1") { console.log("Tracking ", event_name, properties); return; } @@ -33,10 +31,7 @@ export const track = (event_name: string, properties?: Dict) => { mixpanel.track(event_name, { ...properties, distinct_id: userId }); }; export const time_event = (event_name: string) => { - if ( - window.location.hostname === "localhost" || - window.location.hostname === "127.0.0.1" - ) { + if (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1") { //eslint-disable-line no-restricted-globals return; } diff --git a/src/utils/parsers.test.ts b/src/utils/parsers.test.ts index 0756fe52..97956f20 100644 --- a/src/utils/parsers.test.ts +++ b/src/utils/parsers.test.ts @@ -1,11 +1,11 @@ -import { describe, it, expect } from "vitest"; import * as THREE from "three"; +import { describe, expect, it } from "vitest"; import { - parseCameraPosition, - parseCameraTarget, + parseAtomSizeAndColor, parseAtomType, parseBond, - parseAtomSizeAndColor, + parseCameraPosition, + parseCameraTarget, } from "./parsers"; describe("parseCameraPosition", () => { @@ -391,4 +391,3 @@ describe("parseAtomSizeAndColor", () => { expect(result).toBeUndefined(); }); }); - diff --git a/src/utils/parsers.ts b/src/utils/parsers.ts index 41505640..bd8065d2 100644 --- a/src/utils/parsers.ts +++ b/src/utils/parsers.ts @@ -7,11 +7,7 @@ import * as THREE from "three"; */ export const parseCameraPosition = (line: string) => { const splitted = line.split(/\s+/); - if ( - splitted[0] === "camera" && - splitted[1] === "position" && - splitted.length === 5 - ) { + if (splitted[0] === "camera" && splitted[1] === "position" && splitted.length === 5) { const x = parseFloat(splitted[2]); const y = parseFloat(splitted[3]); const z = parseFloat(splitted[4]); @@ -26,11 +22,7 @@ export const parseCameraPosition = (line: string) => { */ export const parseCameraTarget = (line: string) => { const splitted = line.split(/\s+/); - if ( - splitted[0] === "camera" && - splitted[1] === "target" && - splitted.length === 5 - ) { + if (splitted[0] === "camera" && splitted[1] === "target" && splitted.length === 5) { const x = parseFloat(splitted[2]); const y = parseFloat(splitted[3]); const z = parseFloat(splitted[4]); @@ -87,4 +79,3 @@ export const parseAtomSizeAndColor = (line: string) => { }; } }; - diff --git a/vite.config.ts b/vite.config.ts index b6e4ab94..2a0a8531 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,23 +1,22 @@ -import { defineConfig } from 'vite'; -import react from '@vitejs/plugin-react'; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], - base: '/atomify/', + base: "/atomify/", build: { - outDir: 'dist', + outDir: "dist", }, server: { host: true, // Allow access from local network (0.0.0.0) port: 3000, proxy: { - '/chapters': { - target: 'http://localhost:5350', - changeOrigin: true - } - } + "/chapters": { + target: "http://localhost:5350", + changeOrigin: true, + }, + }, }, // WASM files served from public/ folder, no special config needed }); - diff --git a/vitest.config.mts b/vitest.config.mts index c537d0f8..4d3bca14 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -18,5 +18,3 @@ export default defineConfig({ }, }, }); - - From 1bc09b33beb0fd4fee291ea5831f47f727409c1d Mon Sep 17 00:00:00 2001 From: Anders Hafreager Date: Sun, 7 Dec 2025 01:15:22 +0000 Subject: [PATCH 03/20] ci: replace Prettier check with Biome lint in CI - Update deploy workflow to run npm run lint instead of Prettier - Ensures linting rules (including unused variables) are enforced in CI --- .github/workflows/deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 47000ea3..9426e18c 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -32,8 +32,8 @@ jobs: run: npm run typecheck - name: Run Tests run: npm run test:run - - name: Run Prettier - run: npx prettier . + - name: Run Lint + run: npm run lint - name: Build app run: npm run predeploy - name: Upload artifact From d4bbbd5a97e34d983666c59412bee87b5ad74102 Mon Sep 17 00:00:00 2001 From: Anders Hafreager Date: Sun, 7 Dec 2025 01:19:36 +0000 Subject: [PATCH 04/20] fix: resolve CSS positioning issue with simulation summary overlay - Remove conflicting position: relative from .simulationsummary class - Add explicit position: fixed to .simulationsummary-expanded - Fixes issue where expanded summary was hiding canvas instead of overlaying - Also fix Simulation component fragment and Figure test accessibility --- src/components/Figure.test.tsx | 12 +++- src/components/Simulation.tsx | 14 +---- src/index.css | 112 ++++++++++++++++----------------- 3 files changed, 69 insertions(+), 69 deletions(-) diff --git a/src/components/Figure.test.tsx b/src/components/Figure.test.tsx index 01ab8fd7..abe5c8cc 100644 --- a/src/components/Figure.test.tsx +++ b/src/components/Figure.test.tsx @@ -38,7 +38,17 @@ vi.mock("antd", () => ({ onCancel: () => void; }) => open ? ( -
+
{ + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + onCancel(); + } + }} + > {children}
) : null, diff --git a/src/components/Simulation.tsx b/src/components/Simulation.tsx index b5c44b1c..34fc2457 100644 --- a/src/components/Simulation.tsx +++ b/src/components/Simulation.tsx @@ -1,10 +1,8 @@ import { notification } from "antd"; import { useCallback, useEffect, useRef } from "react"; import { useStoreActions, useStoreState } from "../hooks"; -import { LammpsWeb } from "../types"; import { time_event, track } from "../utils/metrics"; import createModule from "../wasm/lammps.mjs"; -import { AtomifyWasmModule } from "../wasm/types"; const SimulationComponent = () => { const wasm = window.wasm; @@ -80,16 +78,7 @@ const SimulationComponent = () => { }); } }; - }, [ - lammps, - running, - selectedMenu, - paused, - setPaused, - setSimulationSettings, - simulation, - simulationSettings, - ]); + }, [running, selectedMenu, paused, setPaused, setSimulationSettings, simulationSettings]); useEffect(() => { window.postStepCallback = () => { @@ -170,6 +159,7 @@ const SimulationComponent = () => { }, 100); } }, [onPrint, setLammps, setStatus]); + // biome-ignore lint/complexity/noUselessFragments: Component only has side effects, needs to return valid JSX return <>; }; export default SimulationComponent; diff --git a/src/index.css b/src/index.css index 1926d6ac..0e7525b0 100644 --- a/src/index.css +++ b/src/index.css @@ -80,7 +80,7 @@ body, /* Liquid Glass Sidebar - optimized */ .ant-layout-sider { - background: rgba(41, 40, 45, 0.3) !important; + background: rgba(41, 40, 45, 0.3); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border-right: 1px solid rgba(255, 255, 255, 0.08); @@ -105,43 +105,43 @@ body, } .ant-menu-dark { - background: transparent !important; + background: transparent; } /* Remove padding/margin/borders from ant-menu-sub that make items look smaller */ .ant-menu-sub, .ant-menu-sub.ant-menu-inline { - padding: 0 !important; - margin: 0 !important; - border: none !important; - background: transparent !important; + padding: 0; + margin: 0; + border: none; + background: transparent; } .ant-menu-item { - margin: 4px 8px !important; - width: calc(100% - 16px) !important; + margin: 4px 8px; + width: calc(100% - 16px); border-radius: 8px; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); - background: transparent !important; + background: transparent; position: relative; - border: none !important; - outline: none !important; - box-shadow: none !important; + border: none; + outline: none; + box-shadow: none; } .ant-menu-dark .ant-menu-item-selected { - background: linear-gradient(135deg, rgb(73, 107, 187), rgb(100, 173, 230)) !important; + background: linear-gradient(135deg, rgb(73, 107, 187), rgb(100, 173, 230)); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); - color: #fff !important; + color: #fff; font-weight: 600; - border: none !important; - outline: none !important; - box-shadow: none !important; + border: none; + outline: none; + box-shadow: none; } .ant-menu-item:hover:not(.ant-menu-item-selected) { - background-color: rgba(255, 255, 255, 0.05) !important; + background-color: rgba(255, 255, 255, 0.05); transform: translateX(4px); } @@ -151,11 +151,11 @@ body, } .ant-layout-content { - background: transparent !important; + background: transparent; } .ant-layout { - background: transparent !important; + background: transparent; } .site-layout .site-layout-background h1 { @@ -171,11 +171,11 @@ body, } .console-modal .ant-modal-footer { - background-color: #1e1e1e !important; + background-color: #1e1e1e; } .settingsbutton { - position: fixed !important; + position: fixed; bottom: 0; right: 0; margin-right: 20px; @@ -183,7 +183,7 @@ body, } .analyzebutton { - position: fixed !important; + position: fixed; bottom: 0; right: 0; margin-right: 70px; @@ -192,7 +192,7 @@ body, /* Liquid Glass Overlay Components - optimized */ .simulationsummary { - position: fixed !important; + position: fixed; background: rgba(42, 41, 47, 0.6); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); @@ -207,7 +207,6 @@ body, box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.2), 0 8px 32px rgba(0, 0, 0, 0.1); - position: relative; overflow: hidden; z-index: 1000; contain: layout style paint; @@ -227,9 +226,9 @@ body, } .simulationsummary-collapsed { - width: auto !important; + width: auto; min-width: 200px; - padding: 8px 16px !important; + padding: 8px 16px; cursor: pointer; transition: all 0.2s ease; border: none; @@ -240,7 +239,7 @@ body, } .simulationsummary-collapsed:hover { - background: rgba(42, 41, 47, 0.8) !important; + background: rgba(42, 41, 47, 0.8); } .simulationsummary-collapsed button { @@ -259,8 +258,8 @@ body, } .show-more-button { - padding: 0 !important; - color: #fff !important; + padding: 0; + color: #fff; text-decoration: underline; font-size: 12px; } @@ -283,15 +282,16 @@ body, } .simulation-summary-minimize-button button { - pointer-events: auto !important; + pointer-events: auto; z-index: 101; - cursor: pointer !important; + cursor: pointer; } .simulationsummary-expanded { - width: 400px !important; - height: calc(100vh - 20px) !important; - top: 10px !important; + position: fixed; + width: 400px; + height: calc(100vh - 20px); + top: 10px; overflow-y: auto; } @@ -403,11 +403,11 @@ body, /* Dygraph dark theme styling */ .dygraph-legend { - background: rgba(0, 0, 0, 0.8) !important; - color: #fff !important; - border: 1px solid #333 !important; - border-radius: 4px !important; - padding: 8px !important; + background: rgba(0, 0, 0, 0.8); + color: #fff; + border: 1px solid #333; + border-radius: 4px; + padding: 8px; } .dygraph-custom-legend { @@ -422,28 +422,28 @@ body, } .dygraph-axis-label { - color: #ffffff !important; + color: #ffffff; } .dygraph-title { - color: #ffffff !important; + color: #ffffff; } .dygraph-xlabel, .dygraph-ylabel { - color: #ffffff !important; + color: #ffffff; } .dygraph-label { - color: #ffffff !important; + color: #ffffff; } /* Respect user motion preferences */ @media (prefers-reduced-motion: reduce) { * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; + animation-duration: 0.01ms; + animation-iteration-count: 1; + transition-duration: 0.01ms; } } @@ -493,25 +493,25 @@ body, /* Make tables transparent to show glass effect */ .ant-table-wrapper { - background: transparent !important; + background: transparent; } .ant-table { - background: transparent !important; + background: transparent; } .ant-table-thead > tr > th { - background: transparent !important; - border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important; - color: #ffffff !important; + background: transparent; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + color: #ffffff; } .ant-table-tbody > tr > td { - background: transparent !important; - border-bottom: 1px solid rgba(255, 255, 255, 0.05) !important; - color: #ffffff !important; + background: transparent; + border-bottom: 1px solid rgba(255, 255, 255, 0.05); + color: #ffffff; } .ant-table-tbody > tr:hover > td { - background: rgba(255, 255, 255, 0.05) !important; + background: rgba(255, 255, 255, 0.05); } From 3626e49d1fee72316734941d76d93d435bfc4fd1 Mon Sep 17 00:00:00 2001 From: Anders Hafreager Date: Sun, 7 Dec 2025 01:19:58 +0000 Subject: [PATCH 05/20] fix: apply remaining Biome lint fixes and CSS positioning fix - Fix non-null assertion in AutoStartSimulation.tsx - Fix template literals and hook dependencies in Figure.tsx - Fix isFinite usage in ColorLegend.tsx - Add button types to ResponsiveSimulationSummary.test.tsx - Fix accessibility in Figure.test.tsx - Apply Biome auto-fixes across codebase (formatting, imports, etc.) - Fix CSS positioning issue with simulation summary overlay --- src/App.test.tsx | 1 - src/App.tsx | 6 ++--- src/components/AutoStartSimulation.tsx | 6 ++--- src/components/ColorLegend.tsx | 2 +- src/components/Figure.tsx | 19 ++++----------- .../ResponsiveSimulationSummary.test.tsx | 12 ++++++---- src/components/SelectedAtomsInfo.tsx | 2 +- src/components/SimulationSummaryContent.tsx | 15 ++++++------ src/components/SimulationSummaryExpanded.tsx | 14 +++++------ src/containers/Console.tsx | 2 +- src/containers/Examples.tsx | 18 +++++++------- src/containers/NewSimulation.tsx | 2 +- src/containers/Notebook.tsx | 24 +++++++++---------- src/containers/View.tsx | 4 ++-- src/global.d.ts | 2 +- src/hooks/useEmbeddedMode.ts | 2 +- src/index.tsx | 1 - src/modifiers/ColorModifierSettings.tsx | 15 ++++++++---- src/modifiers/SyncBondsSettings.tsx | 2 +- src/modifiers/SyncParticlesSettings.tsx | 2 +- src/modifiers/colormodifier.ts | 2 +- src/modifiers/modifier.ts | 2 +- src/modifiers/syncparticlesmodifier.ts | 2 +- src/store/processing.ts | 8 +++---- src/store/simulation.ts | 15 ++++++------ src/utils/boxGeometry.ts | 14 +++++------ src/utils/parsers.ts | 8 +++---- 27 files changed, 98 insertions(+), 104 deletions(-) diff --git a/src/App.test.tsx b/src/App.test.tsx index cecdb550..76f46576 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -1,4 +1,3 @@ -import React from "react"; import { createRoot } from "react-dom/client"; import { describe, it } from "vitest"; import App from "./App"; diff --git a/src/App.tsx b/src/App.tsx index 7c224edc..590b18d8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -114,7 +114,7 @@ const App: React.FC = () => { setCollapsed(false); } }, [width]); - const editMenuLabel = "Edit " + (simulation ? simulation?.id : ""); + const editMenuLabel = `Edit ${simulation ? simulation?.id : ""}`; const runStopButtonTitle = running ? "Stop" : "Run"; const runStopButton = getItem( @@ -190,7 +190,7 @@ const App: React.FC = () => { , simulation ? simulation.files.map((file) => { - return getItem(file.fileName, "file" + file.fileName, ); + return getItem(file.fileName, `file${file.fileName}`, ); }) : [], undefined, @@ -228,7 +228,7 @@ const App: React.FC = () => { useEffect(() => { if (selectedFile) { - setSelectedMenu("file" + selectedFile.fileName); + setSelectedMenu(`file${selectedFile.fileName}`); } }, [selectedFile, setSelectedMenu]); diff --git a/src/components/AutoStartSimulation.tsx b/src/components/AutoStartSimulation.tsx index 089e79b2..bb11dc94 100644 --- a/src/components/AutoStartSimulation.tsx +++ b/src/components/AutoStartSimulation.tsx @@ -53,18 +53,18 @@ const AutoStartSimulation: React.FC = () => { if (autoStart) { setPreferredView("view"); // Visualizer showing atoms } else { - setPreferredView("file" + decodedSimulation.inputScript); // Editor with script + setPreferredView(`file${decodedSimulation.inputScript}`); // Editor with script } } return; } // Handle URL-based embedding (only in embedded mode) - if (!isEmbeddedMode) { + if (!isEmbeddedMode || !embeddedSimulationUrl) { return; } - const response = await fetch(embeddedSimulationUrl!); + const response = await fetch(embeddedSimulationUrl); const data: ExamplesData = await response.json(); // Override baseUrl for localhost development diff --git a/src/components/ColorLegend.tsx b/src/components/ColorLegend.tsx index a88d8955..629f4fb7 100644 --- a/src/components/ColorLegend.tsx +++ b/src/components/ColorLegend.tsx @@ -56,7 +56,7 @@ const ColorLegend = ({ }, [colormapName]); const formatValue = (value: number): string => { - if (!isFinite(value)) { + if (!Number.isFinite(value)) { return "N/A"; } // Format to 3 significant figures diff --git a/src/components/Figure.tsx b/src/components/Figure.tsx index 38a5c894..0d52ae68 100644 --- a/src/components/Figure.tsx +++ b/src/components/Figure.tsx @@ -32,7 +32,7 @@ const Figure = ({ onToggleSyncDataPoints, }: FigureProps) => { const [graph, setGraph] = useState(); - const timesteps = useStoreState((state) => state.simulationStatus.timesteps); + const _timesteps = useStoreState((state) => state.simulationStatus.timesteps); const graphId = useId(); const width = window.innerWidth < 1000 ? window.innerWidth * 0.8 : window.innerWidth * 0.6; const height = (width * 3) / 4; @@ -47,16 +47,7 @@ const Figure = ({ yLabel: source.yLabel, name: source.name, }; - }, [ - modifier?.name, - modifier?.xLabel, - modifier?.yLabel, - modifier?.data1D, - plotData?.name, - plotData?.xLabel, - plotData?.yLabel, - plotData?.data1D, - ]); + }, [modifier, plotData]); // Only set syncDataPoints when a modifier is provided // Use ref to track previous modifier name to detect actual changes @@ -112,8 +103,8 @@ const Figure = ({ data.series.forEach((series) => { if (!series.isVisible) return; const color = series.color; - html += ''; - html += series.labelHTML + ": " + series.yHTML + "
"; + html += ``; + html += `${series.labelHTML}: ${series.yHTML}
`; }); html += "
"; return html; @@ -127,7 +118,7 @@ const Figure = ({ if (graph && plotConfig?.data1D) { graph.updateOptions({ file: plotConfig.data1D.data }); } - }, [graph, plotConfig, timesteps]); + }, [graph, plotConfig]); return ( diff --git a/src/components/ResponsiveSimulationSummary.test.tsx b/src/components/ResponsiveSimulationSummary.test.tsx index bbdf7529..c074448f 100644 --- a/src/components/ResponsiveSimulationSummary.test.tsx +++ b/src/components/ResponsiveSimulationSummary.test.tsx @@ -17,14 +17,18 @@ vi.mock("./SimulationSummary", () => ({ }: ComponentProps) => (
{isCollapsed &&
Collapsed
} - {onShowMore && } + {onShowMore && ( + + )} {onExpand && ( - )} {onCollapse && ( - )} @@ -36,7 +40,7 @@ vi.mock("./SimulationSummaryExpanded", () => ({ default: ({ onShowLess }: ComponentProps) => (
{onShowLess && ( - )} diff --git a/src/components/SelectedAtomsInfo.tsx b/src/components/SelectedAtomsInfo.tsx index 54455380..7b31fe2f 100644 --- a/src/components/SelectedAtomsInfo.tsx +++ b/src/components/SelectedAtomsInfo.tsx @@ -2,7 +2,7 @@ import { Button } from "antd"; import type { Particles } from "omovi"; import { useEffect, useMemo, useRef, useState } from "react"; import { useStoreState } from "../hooks"; -import { type Data1D, PlotData } from "../types"; +import type { Data1D } from "../types"; import Figure from "./Figure"; interface SelectedAtomsInfoProps { diff --git a/src/components/SimulationSummaryContent.tsx b/src/components/SimulationSummaryContent.tsx index dcda0ade..094cd9d8 100644 --- a/src/components/SimulationSummaryContent.tsx +++ b/src/components/SimulationSummaryContent.tsx @@ -109,7 +109,7 @@ const SimulationSummaryContent = () => { > {value} {" "} - {" " + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} + {` ${record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : ""}`} ); } else { @@ -144,7 +144,7 @@ const SimulationSummaryContent = () => { > {value} {" "} - {" " + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} + {` ${record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : ""}`} ); } else { @@ -179,7 +179,7 @@ const SimulationSummaryContent = () => { > {value} {" "} - {" " + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} + {` ${record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : ""}`} ); } else { @@ -200,7 +200,7 @@ const SimulationSummaryContent = () => { title: "Name", dataIndex: "name", key: "name", - render: (text, record) => ( + render: (text, _record) => ( {text}{" "} @@ -212,7 +212,7 @@ const SimulationSummaryContent = () => { ]; const rowSelection: TableRowSelection = { - onChange: (selectedRowKeys, selectedRows) => { + onChange: (_selectedRowKeys, selectedRows) => { modifiers.forEach((modifier) => { modifier.active = selectedRows.indexOf(modifier) >= 0; }); @@ -302,7 +302,7 @@ const SimulationSummaryContent = () => { { key: "timeremain", name: "Remaining time", - value: Math.ceil(remainingTime).toString() + " s", + value: `${Math.ceil(remainingTime).toString()} s`, }, { key: "tsps", @@ -312,7 +312,7 @@ const SimulationSummaryContent = () => { { key: "memory", name: "Memory usage", - value: (memoryUsage / 1024 / 1024).toFixed(2).toString() + " MB", + value: `${(memoryUsage / 1024 / 1024).toFixed(2).toString()} MB`, }, { key: "simulationspeed", @@ -341,7 +341,6 @@ const SimulationSummaryContent = () => { memoryUsage, simulationSettings.speed, simulationSettings.uiUpdateFrequency, - renderSettings.showSimulationBox, ]); return ( diff --git a/src/components/SimulationSummaryExpanded.tsx b/src/components/SimulationSummaryExpanded.tsx index 9fdda92c..000d64ca 100644 --- a/src/components/SimulationSummaryExpanded.tsx +++ b/src/components/SimulationSummaryExpanded.tsx @@ -115,7 +115,7 @@ const SimulationSummaryExpanded = ({ onShowLess }: SimulationSummaryExpandedProp > {value} {" "} - {" " + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} + {` ${record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : ""}`} ); } else { @@ -150,7 +150,7 @@ const SimulationSummaryExpanded = ({ onShowLess }: SimulationSummaryExpandedProp > {value} {" "} - {" " + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} + {` ${record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : ""}`} ); } else { @@ -185,7 +185,7 @@ const SimulationSummaryExpanded = ({ onShowLess }: SimulationSummaryExpandedProp > {value} {" "} - {" " + (record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : "")} + {` ${record.hasScalarData ? record.scalarValue.toPrecision(5).toString() : ""}`} ); } else { @@ -206,7 +206,7 @@ const SimulationSummaryExpanded = ({ onShowLess }: SimulationSummaryExpandedProp title: "Name", dataIndex: "name", key: "name", - render: (text, record) => ( + render: (text, _record) => ( {text}{" "} @@ -218,7 +218,7 @@ const SimulationSummaryExpanded = ({ onShowLess }: SimulationSummaryExpandedProp ]; const rowSelection: TableRowSelection = { - onChange: (selectedRowKeys, selectedRows) => { + onChange: (_selectedRowKeys, selectedRows) => { modifiers.forEach((modifier) => { modifier.active = selectedRows.indexOf(modifier) >= 0; }); @@ -292,7 +292,7 @@ const SimulationSummaryExpanded = ({ onShowLess }: SimulationSummaryExpandedProp { key: "timeremain", name: "Remaining time", - value: Math.ceil(remainingTime).toString() + " s", + value: `${Math.ceil(remainingTime).toString()} s`, }, { key: "tsps", @@ -302,7 +302,7 @@ const SimulationSummaryExpanded = ({ onShowLess }: SimulationSummaryExpandedProp { key: "memory", name: "Memory usage", - value: (memoryUsage / 1024 / 1024).toFixed(2).toString() + " MB", + value: `${(memoryUsage / 1024 / 1024).toFixed(2).toString()} MB`, }, { key: "simulationspeed", diff --git a/src/containers/Console.tsx b/src/containers/Console.tsx index d467907d..0609d5ef 100644 --- a/src/containers/Console.tsx +++ b/src/containers/Console.tsx @@ -27,7 +27,7 @@ const Console = ({ width, height }: ConsoleProps) => { editor.revealLine(model.getLineCount()); } } - }, [lammpsOutput]); + }, []); const handleEditorDidMount = (editor: Monaco.editor.IStandaloneCodeEditor) => { editorRef.current = editor; diff --git a/src/containers/Examples.tsx b/src/containers/Examples.tsx index a4defcfe..ecf6d740 100644 --- a/src/containers/Examples.tsx +++ b/src/containers/Examples.tsx @@ -44,16 +44,16 @@ const Examples = () => { const fetchExamples = async (examplesUrl: string) => { let response = await fetch(examplesUrl, { cache: "no-store" }); const data = await response.json(); - const baseUrl = data["baseUrl"]; - const title = data["title"] || "Examples"; - const descriptionsUrl = `${baseUrl}/${data["descriptionFile"]}`; + const baseUrl = data.baseUrl; + const title = data.title || "Examples"; + const descriptionsUrl = `${baseUrl}/${data.descriptionFile}`; response = await fetch(descriptionsUrl); if (response.status !== 404) { const description = await response.text(); setDescription(description); } - const examples: Example[] = data["examples"]; + const examples: Example[] = data.examples; examples.forEach((example) => { example.imageUrl = `${baseUrl}/${example.imageUrl}`; example.files.forEach((file) => { @@ -62,7 +62,7 @@ const Examples = () => { }); setTitle(title); - setExamples(data["examples"]); + setExamples(data.examples); track("Examples.Fetch", { examplesUrl }); }; @@ -72,13 +72,13 @@ const Examples = () => { const defaultExamplesUrl = "examples/examples.json"; let examplesUrl = defaultExamplesUrl; - if (params["examplesUrl"] != null) { - examplesUrl = params["examplesUrl"]; + if (params.examplesUrl != null) { + examplesUrl = params.examplesUrl; } try { await fetchExamples(examplesUrl); - } catch (e) { + } catch (_e) { notification.error({ message: `Could not fetch examples from ${examplesUrl}. Fetching default.`, }); @@ -123,7 +123,7 @@ const Examples = () => { if (simulation?.id !== newSimulation.id) { setNewSimulation(newSimulation); } else { - setPreferredView("file" + newSimulation.inputScript); + setPreferredView(`file${newSimulation.inputScript}`); } }, [setNewSimulation, setPreferredView, simulation?.id] diff --git a/src/containers/NewSimulation.tsx b/src/containers/NewSimulation.tsx index 52f41185..2bbdda54 100644 --- a/src/containers/NewSimulation.tsx +++ b/src/containers/NewSimulation.tsx @@ -56,7 +56,7 @@ const NewSimulation = ({ onClose }: NewSimulationProps) => { setFiles(window.files); message.success(`${file.fileName} uploaded successfully.`); }, - [files, setFiles] + [files] ); const props: UploadProps = { diff --git a/src/containers/Notebook.tsx b/src/containers/Notebook.tsx index 43c6778d..d949d358 100644 --- a/src/containers/Notebook.tsx +++ b/src/containers/Notebook.tsx @@ -42,19 +42,17 @@ const Notebook = () => { }, [simulation]); return ( - <> -
-