From 8d0d27700d70991535c8f9b10cef83dae8a07ff1 Mon Sep 17 00:00:00 2001 From: superstar54 Date: Thu, 31 Oct 2024 14:12:44 +0100 Subject: [PATCH] Replace species by kind --- demo/demo.js | 12 +- demo/index.html | 2 +- ..._species.png => example_color_by_kind.png} | Bin docs/source/atoms/atoms.rst | 31 +- docs/source/atoms/color.rst | 6 +- docs/source/quickstart.rst | 2 +- src/atoms/AtomsViewer.js | 6 +- src/atoms/atoms.js | 307 +++++++++--------- src/atoms/plugins/atom.js | 24 +- src/atoms/plugins/bond.js | 64 ++-- src/atoms/plugins/boundary.js | 35 +- src/atoms/plugins/highlight.js | 2 +- src/atoms/plugins/polyhedra.js | 36 +- src/index.js | 4 +- src/io/parserCif.js | 2 +- src/io/parserCube.js | 10 +- src/io/parserPOSCAR.js | 2 +- src/io/parserXYZ.js | 10 +- src/operation/atoms.js | 6 +- tests/atoms.test.mjs | 8 +- tests/e2e/gui.spec.js | 4 +- ...inux.png => Color-kind-chromium-linux.png} | Bin tests/e2e/testSpecies.html | 2 +- tests/parserXYZ.test.mjs | 8 +- 24 files changed, 282 insertions(+), 301 deletions(-) rename docs/source/_static/images/{example_color_by_species.png => example_color_by_kind.png} (100%) rename tests/e2e/gui.spec.js-snapshots/{Color-species-chromium-linux.png => Color-kind-chromium-linux.png} (100%) diff --git a/demo/demo.js b/demo/demo.js index dfd0db6..b0f58df 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -1,10 +1,10 @@ -import { WEAS, Atoms, Species, parseXYZ, parseCIF, parseCube } from "../src/index.js"; // Adjust the path as necessary +import { WEAS, Atoms, Kind, parseXYZ, parseCIF, parseCube } from "../src/index.js"; // Adjust the path as necessary import * as THREE from "three"; window.THREE = THREE; window.WEAS = WEAS; window.Atoms = Atoms; -window.Species = Species; +window.Kind = Kind; async function fetchFile(filename) { const response = await fetch(`datas/${filename}`); @@ -68,8 +68,8 @@ async function updateAtoms(filename, fileContent = null) { const atomsList = parseXYZ(structureData); // atomsList[0].newAttribute("moment", [1, 1, 1, 1, 1, -1, -1, -1, -1, -1], "atom"); // atomsList[0].newAttribute("charge", [-1, 0.5, 1, 0.5, 0.3, 0.2, 2, 1, 0, -0.5], "atom"); - // atomsList[0].newAttribute("color", { C: "##eb4034", H: "#b434eb", O: "#34eb77", S: "#FFFF00" }, "species"); - // atomsList[0].newAttribute("radii", { C: 1.5, H: 1.0, O: 1.5, S: 1.5 }, "species"); + // atomsList[0].newAttribute("color", { C: "##eb4034", H: "#b434eb", O: "#34eb77", S: "#FFFF00" }, "kind"); + // atomsList[0].newAttribute("radii", { C: 1.5, H: 1.0, O: 1.5, S: 1.5 }, "kind"); editor.avr.atoms = atomsList; // editor.avr.bondManager.settings[1].color1 = "blue"; // editor.avr.VFManager.addSetting({ origins: "positions", vectors: "movement", color: "#ff0000", radius: 0.1 }); @@ -232,12 +232,12 @@ async function updateAtoms(filename, fileContent = null) { editor.anyMesh.fromSettings(data); editor.anyMesh.drawMesh(); break; - case "species": + case "kinds": editor.clear(); filename = "c2h6so.xyz"; structureData = fileContent || (await fetchFile(filename)); atoms = parseXYZ(structureData); - atoms[0].addSpecies("C1", "C"); + atoms[0].addKind("C1", "C"); atoms[0].symbols[3] = "C1"; editor.avr.atoms = atoms; // editor.avr.bondManager.settings[1].color1 = "blue"; diff --git a/demo/index.html b/demo/index.html index d3cab8e..3128624 100644 --- a/demo/index.html +++ b/demo/index.html @@ -34,7 +34,7 @@

WEAS (Web Environment For Atomistic Structures)

- + diff --git a/docs/source/_static/images/example_color_by_species.png b/docs/source/_static/images/example_color_by_kind.png similarity index 100% rename from docs/source/_static/images/example_color_by_species.png rename to docs/source/_static/images/example_color_by_kind.png diff --git a/docs/source/atoms/atoms.rst b/docs/source/atoms/atoms.rst index 373360f..a87ca6d 100644 --- a/docs/source/atoms/atoms.rst +++ b/docs/source/atoms/atoms.rst @@ -1,6 +1,6 @@ Atoms ========= -The Atoms object represents a collection of atoms. It is used to store the positions, species, and other properties of the atoms. The sytax is very similar to ASE `Atoms `_ object. However, there are also some key differences regarding the species and attributes. +The Atoms object represents a collection of atoms. It is used to store the positions, symbols, and other properties of the atoms. The sytax is very similar to ASE `Atoms `_ object. However, there are also some key differences regarding the kinds and attributes. Example: @@ -16,42 +16,42 @@ Example: In this example, an Atoms object is created to represent a water molecule within a cubic unit cell with periodic boundaries. The object includes definitions for the symbols, positions of the atoms. -Species +Kind ------- -Why do we use species instead of elements? Because of we want to store different properties for the same element: +Why do we use kinds instead of elements? Because of we want to store different properties for the same element: - such as: colors, bonds, etc. - In DFT calculation, different potentials, basis sets, charge, magnetic moment, etc. -A species has: +A kind has: -- symbol: the name of the species, e.g. 'O', 'H1', 'Fe_up', etc. +- symbol: the name of the kind, e.g. 'O', 'H1', 'Fe_up', etc. - element: the element symbol, e.g. 'O', 'H', 'Fe', etc. - number: the atomic number of the element, e.g. 8, 1, 26, etc. -One can define the species explicitly using the `species` attribute. +One can define the kinds explicitly using the `kinds` attribute. .. code-block:: javascript const myAtoms = new Atoms({ symbols: ['O', 'H1', 'H2'], // symbols of the atoms - species: {'H1': 'H', 'H2': 'H'}, // Defining the species + kinds: {'H1': 'H', 'H2': 'H'}, // Defining the kinds }); Attributes and domain ---------------------- One can store additional data in the Atoms object using the `attributes`. The `attributes` has two domains: -- `atoms`: store data that is specific to each atom, such as the charge or force. -- `species`: store data that is specific to each species, such as the mass, color, or radius. +- `atom`: store data that is specific to each atom, such as the charge or force. +- `kind`: store data that is specific to each kind, such as the mass, color, or radius. .. code-block:: javascript - attributes: {'atoms': {'charge': [0, 0, 0], + attributes: {'atom': {'charge': [0, 0, 0], 'force': [[0, 0, 0], [0, 0, 0], [0, 0, 0]], }, - 'species': {'mass': {'O': 15.999, 'H': 1.008}, + 'kind': {'mass': {'O': 15.999, 'H': 1.008}, "color", {C: "red", H: "#b434eb", O: "#34eb77", S: "#FFFF00" }, "radius", {C: 0.77, H: 0.37, O: 0.66, S: 1.04}, }, @@ -63,13 +63,12 @@ Methods The Atoms object has a number of methods that can be used to manipulate the data. These include methods to: -Atomic and Species Manipulation +Atomic and Kind Manipulation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- addSpecies(symbol, element = null) +- addKind(symbol, element = null) - addAtom(atom) -- removeAtom(index) -- replaceAtoms(indices, newSpeciesSymbol) +- replaceAtoms(indices, newKindSymbol) Cell and Boundary Conditions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -100,7 +99,7 @@ Information and Export ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - getAtomsCount() -- getSpeciesCount() +- getKindsCount() - getCellLengthsAndAngles() - calculateFractionalCoordinates() - toDict() diff --git a/docs/source/atoms/color.rst b/docs/source/atoms/color.rst index 46434fc..52a7066 100644 --- a/docs/source/atoms/color.rst +++ b/docs/source/atoms/color.rst @@ -21,9 +21,9 @@ Supported style are: ----------------------------- -Custom color for each species +Custom color for each kind ----------------------------- -Use can set custom color for each species. The color can be in the form of hex code or color name. +Use can set custom color for each kind. The color can be in the form of hex code or color name. .. code-block:: javascript @@ -35,7 +35,7 @@ Use can set custom color for each species. The color can be in the form of hex c weas.avr.drawModels() -.. image:: ../_static/images/example_color_by_species.png +.. image:: ../_static/images/example_color_by_kind.png :width: 6cm diff --git a/docs/source/quickstart.rst b/docs/source/quickstart.rst index 001389e..bf05f6b 100644 --- a/docs/source/quickstart.rst +++ b/docs/source/quickstart.rst @@ -77,7 +77,7 @@ Here is the result of the above code: Features -------- -- **Structure Manipulation**: Enables manipulation of the structure, such as adding, removing, translating, rotating atoms and modifying species, and cell parameters. +- **Structure Manipulation**: Enables manipulation of the structure, such as adding, removing, translating, rotating atoms and modifying kind, and cell parameters. - **Customizable Appearance**: Allows customization of atom and bond appearances, including colors, sizes, and visibility, polyhedra, and unit cell. diff --git a/src/atoms/AtomsViewer.js b/src/atoms/AtomsViewer.js index 2434934..7e4822d 100644 --- a/src/atoms/AtomsViewer.js +++ b/src/atoms/AtomsViewer.js @@ -369,7 +369,7 @@ class AtomsViewer { // Remove labels this.ALManager.settings = []; } else if (newValue === "Symbol") { - this.ALManager.settings = [{ origins: "positions", texts: "species" }]; + this.ALManager.settings = [{ origins: "positions", texts: "symbols" }]; } else if (newValue === "Index") { this.ALManager.settings = [{ origins: "positions", texts: "index" }]; } @@ -572,7 +572,7 @@ class AtomsViewer { addAtom(element, position = { x: 0, y: 0, z: 0 }) { // Remove the selected atoms from the scene and data const atom = new Atom(element, [position.x, position.y, position.z]); - this.atoms.addSpecies(element); + this.atoms.addKind(element); this.atoms.addAtom(atom); // this.logger.debug("atoms: ", this.atoms); @@ -598,7 +598,7 @@ class AtomsViewer { } const copied_atoms = this.atoms.getAtomsByIndices(indices); this.logger.debug("copied_atoms: ", copied_atoms); - this.atoms.addToSelf(copied_atoms); + this.atoms.add(copied_atoms); this.logger.debug("atoms: ", this.atoms); // also copy the properties, e.g. modelStyles, that are associated with the copied atoms diff --git a/src/atoms/atoms.js b/src/atoms/atoms.js index 7225d99..3df3607 100644 --- a/src/atoms/atoms.js +++ b/src/atoms/atoms.js @@ -1,12 +1,10 @@ import { elementAtomicNumbers } from "./atoms_data.js"; import { convertToMatrixFromABCAlphaBetaGamma, calculateInverseMatrix } from "../utils.js"; -class Species { - constructor(symbol, element = null) { - this.symbol = symbol; // Symbol of the species (e.g., 'C', 'C_1' for carbon) - this._element = null; - if (element === null) { - element = symbol; +class Kind { + constructor(element) { + if (!element) { + throw new Error("Element is required for Kind."); } this.element = element; } @@ -16,9 +14,8 @@ class Species { } set element(value) { - // if value not in elementAtomicNumbers, raise a error if (!elementAtomicNumbers[value]) { - throw new Error(`Element '${value}' is wrong.`); + throw new Error(`Element '${value}' is invalid.`); } this._element = value; } @@ -29,8 +26,8 @@ class Species { } class Atom { - constructor(species, position) { - this.species = species; // the species in the species array + constructor(symbol, position) { + this.symbol = symbol; // the symbol in the symbols array this.position = [...position]; // Position of the atom as a Float32Array } } @@ -45,48 +42,41 @@ class Atoms { [0, 0, 0], ], pbc = [true, true, true], - species = {}, - attributes = { atom: {}, species: {} }, + kinds = {}, + attributes = { atom: {}, kind: {} }, } = {}) { this.uuid = null; - // length of symbols should be the same with positions if (symbols.length !== positions.length) { throw new Error("The length of symbols should be the same with positions."); } this.symbols = symbols; this.positions = positions; - this.setSpecies(species, symbols); + this.setKinds(kinds, symbols); this.setCell(cell); this.setPBC(pbc); this.setAttributes(attributes); } - setSpecies(species, symbols = null) { - /*Initialize the Atoms instance from a dictionary of data. - */ - this.species = {}; - if (typeof species !== "object") { - throw new Error("Species should be a dictionary."); + setKinds(kinds, symbols = null) { + this.kinds = {}; + if (typeof kinds !== "object") { + throw new Error("Kinds should be a dictionary."); } - // Iterate over each key-value pair in the species dictionary - Object.entries(species).forEach(([symbol, element]) => { - this.addSpecies(symbol, element); + Object.entries(kinds).forEach(([symbol, element]) => { + this.addKind(symbol, element); }); - // find out species which not added if (symbols) { - const species = new Set(symbols); - species.forEach((s) => { - if (!this.species[s]) { - this.addSpecies(s); + const kindsSet = new Set(symbols); + kindsSet.forEach((s) => { + if (!this.kinds[s]) { + this.addKind(s); } }); } } setAttributes(attributes) { - /*Set the attributes of the Atoms instance. - */ - this.attributes = { atom: {}, species: {}, "inter-species": {} }; + this.attributes = { atom: {}, kind: {}, "inter-kind": {} }; for (const domain in attributes) { for (const name in attributes[domain]) { this.newAttribute(name, attributes[domain][name], domain); @@ -95,38 +85,30 @@ class Atoms { } newAttribute(name, values, domain = "atom") { - /*Add a new attribute to the Atoms instance.*/ if (domain === "atom") { - // Ensure the length of values matches the number of atoms if (values.length !== this.positions.length) { throw new Error("The number of values does not match the number of atoms."); } - // copy the values, values could be a array of N-d array this.attributes["atom"][name] = JSON.parse(JSON.stringify(values)); - } else if (domain === "species") { - // Ensure that values are provided for each species - for (const key of Object.keys(this.species)) { + } else if (domain === "kind") { + for (const key of Object.keys(this.kinds)) { if (!(key in values)) { - throw new Error(`Value for species '${key}' is missing.`); + throw new Error(`Value for kind '${key}' is missing.`); } } - this.attributes["species"][name] = JSON.parse(JSON.stringify(values)); - } else if (domain === "inter-species") { - // We don't require the values to be provided for each species - this.attributes["species"][name] = JSON.parse(JSON.stringify(values)); + this.attributes["kind"][name] = JSON.parse(JSON.stringify(values)); + } else if (domain === "inter-kind") { + this.attributes["inter-kind"][name] = JSON.parse(JSON.stringify(values)); } else { - throw new Error('Invalid domain. Must be either "atom", "species", or "inter-species".'); + throw new Error('Invalid domain. Must be either "atom", "kind", or "inter-kind".'); } } getAttribute(name, domain = "atom") { - /*Get the attribute of the Atoms instance. - Species attributes read from the atoms directly: positions, species, index - */ if (domain === "atom") { if (name === "positions") { return this.positions; - } else if (name === "species") { + } else if (name === "symbols") { return this.symbols; } else if (name === "index") { return Array.from({ length: this.positions.length }, (_, i) => i); @@ -135,35 +117,31 @@ class Atoms { throw new Error(`Attribute '${name}' is not defined. The available attributes are: ${Object.keys(this.attributes["atom"])}`); } return this.attributes["atom"][name]; - } else if (domain === "species") { - if (!this.attributes["species"][name]) { - throw new Error(`Attribute '${name}' is not defined. The available attributes are: ${Object.keys(this.attributes["species"])}`); + } else if (domain === "kind") { + if (!this.attributes["kind"][name]) { + throw new Error(`Attribute '${name}' is not defined. The available attributes are: ${Object.keys(this.attributes["kind"])}`); } - return this.attributes["species"][name]; - } else if (domain === "inter-species") { + return this.attributes["kind"][name]; + } else if (domain === "inter-kind") { if (!this.attributes[domain][name]) { - throw new Error(`Attribute '${name}' is not defined in inter-species domain. The available attributes are: ${Object.keys(this.attributes[domain])}`); + throw new Error(`Attribute '${name}' is not defined in inter-kind domain. The available attributes are: ${Object.keys(this.attributes[domain])}`); } return this.attributes[domain][name]; } else { - throw new Error('Invalid domain. Must be either "atom" or "species".'); + throw new Error('Invalid domain. Must be either "atom", "kind", or "inter-kind".'); } } setCell(cell) { - /*Set the unit cell of the Atoms instance.*/ if (cell.length === 9) { - // Convert 1x9 array into 3x3 matrix format this.cell = [ [cell[0], cell[1], cell[2]], [cell[3], cell[4], cell[5]], [cell[6], cell[7], cell[8]], ]; } else if (cell.length === 6) { - // 1x6 array [a, b, c, alpha, beta, gamma] this.cell = convertToMatrixFromABCAlphaBetaGamma(cell); } else if (cell.length === 3) { - // 1x3 array [a, b, c], assuming 90-degree angles if (cell[0].length === 3) { this.cell = cell; } else { @@ -176,11 +154,9 @@ class Atoms { } isUndefinedCell() { - /*Check if the unit cell is undefined.*/ return this.cell.some((row) => row.every((cell) => cell === 0)); } - // get cell length and angles getCellLengthsAndAngles() { const [a, b, c] = this.cell.map((row) => Math.sqrt(row[0] ** 2 + row[1] ** 2 + row[2] ** 2)); const alpha = (Math.acos((this.cell[1][0] * this.cell[2][0] + this.cell[1][1] * this.cell[2][1] + this.cell[1][2] * this.cell[2][2]) / (b * c)) * 180) / Math.PI; @@ -190,103 +166,119 @@ class Atoms { } setPBC(pbc) { - // if pbc is a boolean, convert it to a 3-element array if (typeof pbc === "boolean") { pbc = [pbc, pbc, pbc]; } this.pbc = pbc; } - addSpecies(symbol, element = null) { - // Create a new Species and add it to the species object - if (!this.species[symbol]) { - this.species[symbol] = new Species(symbol, element); + addKind(symbol, element = null) { + // if the kind is already defined, raise an error + if (this.kinds[symbol]) { + throw new Error(`Kind '${symbol}' is already defined.`); + } else { + if (!element) { + element = symbol; + } + // if element is a Kind, add it directly + if (element instanceof Kind) { + this.kinds[symbol] = element; + } else { + this.kinds[symbol] = new Kind(element); + } } } getSymbols() { - // Get the symbols of the species in the atoms - return this.symbols.map((key) => this.species[key].symbol); + return this.symbols; + } + + getElements() { + return this.symbols.map((symbol) => this.kinds[symbol].element); } addAtom(atom) { - // Add an atom to the atoms - if (!this.species[atom.species]) { - this.addSpecies(atom.species); + if (!this.kinds[atom.symbol]) { + throw new Error(`Kind '${atom.symbol}' is not defined.`); } this.positions.push(atom.position); - this.symbols.push(atom.species); + this.symbols.push(atom.symbol); } removeAtom(index) { - // Remove an atom from the atoms by its index - this.positions.splice(index * 4, 4); + this.positions.splice(index, 1); + this.symbols.splice(index, 1); + // Remove attributes in atom domain + for (const domain in this.attributes) { + for (const name in this.attributes["atom"]) { + this.attributes[domain][name].splice(index, 1); + } + } } - getSpeciesCount() { - // Get the number of species in the atoms - return this.species.length; + getKindsCount() { + return Object.keys(this.kinds).length; } getAtomsCount() { - // Get the number of atoms in the atoms - return this.positions.length; // Each atom uses 4 values (species index + x, y, z) + return this.positions.length; } - // Overload the "+" operator to concatenate two Atoms objects add(otherAtoms) { - const result = new Atoms(); - // Concatenate species - result.species = { ...this.species, ...otherAtoms.species }; - // Concatenate positions - result.positions = [...this.positions, ...otherAtoms.positions]; - // Additional attributes can be handled here if needed - return result; - } - - // Overload the "+=" operator to concatenate another Atoms object - addToSelf(otherAtoms) { - // Concatenate species - this.species = { ...this.species, ...otherAtoms.species }; - // Concatenate positions + // if there same kind symbol, check if the element is the same + for (const symbol in otherAtoms.kinds) { + if (this.kinds[symbol] && this.kinds[symbol].element !== otherAtoms.kinds[symbol].element) { + throw new Error(`Kind '${symbol}' is defined in both Atoms objects with different elements.`); + } + } + this.kinds = { ...this.kinds, ...otherAtoms.kinds }; this.positions = [...this.positions, ...otherAtoms.positions]; this.symbols = [...this.symbols, ...otherAtoms.symbols]; - - // Additional attributes can be handled here if needed + // Merge attributes in atom domain + for (const name in this.attributes["atom"]) { + this.attributes["atom"][name] = [...this.attributes["atom"][name], ...otherAtoms.attributes["atom"][name]]; + } + // Merge attributes in kind domain + for (const name in this.attributes["kind"]) { + this.attributes["kind"][name] = { + // the order is important, the attributes of the added atoms should not overwrite the original ones + ...otherAtoms.attributes["kind"][name], + ...this.attributes["kind"][name], + }; + } + return result; } multiply(mx, my, mz) { - // Multiply the atoms in the Atoms object by the given dimensions - console.time("multiply"); if (this.isUndefinedCell()) { throw new Error("Cell matrix is not defined."); } const newAtoms = new Atoms(); - // Copy species object - newAtoms.species = { ...this.species }; + newAtoms.kinds = { ...this.kinds }; const [[ax, ay, az], [bx, by, bz], [cx, cy, cz]] = this.cell; - newAtoms.setCell([ax * mx, ay * my, az * mz, bx * mx, by * my, bz * mz, cx * mx, cy * my, cz * mz]); + newAtoms.setCell([ + [ax * mx, ay * mx, az * mx], + [bx * my, by * my, bz * my], + [cx * mz, cy * mz, cz * mz], + ]); - // Replicate atoms for (let ix = 0; ix < mx; ix++) { for (let iy = 0; iy < my; iy++) { for (let iz = 0; iz < mz; iz++) { for (let i = 0; i < this.positions.length; i++) { const [x, y, z] = this.positions[i]; - // Calculate new position considering the unit cell dimensions const newX = x + ix * this.cell[0][0] + iy * this.cell[1][0] + iz * this.cell[2][0]; const newY = y + ix * this.cell[0][1] + iy * this.cell[1][1] + iz * this.cell[2][1]; const newZ = z + ix * this.cell[0][2] + iy * this.cell[1][2] + iz * this.cell[2][2]; - // Add the new atom to the newAtoms newAtoms.symbols.push(this.symbols[i]); newAtoms.positions.push([newX, newY, newZ]); } } } } - // repeat attributes for each atom + for (const name in this.attributes["atom"]) { const values = this.attributes["atom"][name]; const newValues = []; @@ -301,25 +293,22 @@ class Atoms { } newAtoms.newAttribute(name, newValues, "atom"); } - // console.timeEnd("multiply"); - // Return the new Atoms object + // copy attributes in kind domain, copy is necessary because the attributes of the added atoms should not overwrite the original ones + for (const name in this.attributes["kind"]) { + newAtoms.newAttribute(name, JSON.parse(JSON.stringify(this.attributes["kind"][name])), "kind"); + } return newAtoms; } translate(t) { - for (let i = 0; i < this.positions.length; i++) { - this.positions[i][0] += t[0]; // Shift x-coordinate - this.positions[i][1] += t[1]; // Shift y-coordinate - this.positions[i][2] += t[2]; // Shift z-coordinate - } + this.positions = this.positions.map(([x, y, z]) => [x + t[0], y + t[1], z + t[2]]); } rotate(axis, angle, rotate_cell = false) { const angleRad = (angle * Math.PI) / 180; - const norm = Math.sqrt(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]); - const [u, v, w] = [axis[0] / norm, axis[1] / norm, axis[2] / norm]; // Normalized axis components + const norm = Math.sqrt(axis[0] ** 2 + axis[1] ** 2 + axis[2] ** 2); + const [u, v, w] = [axis[0] / norm, axis[1] / norm, axis[2] / norm]; - // Rodrigues' rotation formula components const cosA = Math.cos(angleRad); const sinA = Math.sin(angleRad); const matrix = [ @@ -354,12 +343,11 @@ class Atoms { } } - center(vacuum = 0.0, axis = (0, 1, 2), center = null) { + center(vacuum = 0.0, axis = [0, 1, 2], center = null) { if (!this.cell) { throw new Error("Cell is not defined."); } - // Calculate current center of mass or geometry let centerOfMass = [0, 0, 0]; for (let i = 0; i < this.positions.length; i++) { centerOfMass[0] += this.positions[i][0]; @@ -368,79 +356,87 @@ class Atoms { } centerOfMass = centerOfMass.map((x) => x / this.positions.length); - // Determine target center point let targetCenter = [0, 0, 0]; if (center) { targetCenter = center; } else { for (let i = 0; i < 3; i++) { if (axis.includes(i)) { - targetCenter[i] = this.cell[i][i] / 2; + targetCenter[i] = (this.cell[0][i] + this.cell[1][i] + this.cell[2][i]) / 2; } } } - // Translate atoms to the target center const translationVector = targetCenter.map((x, i) => x - centerOfMass[i]); - this.translate(...translationVector); + this.translate(translationVector); - // Adjust cell size if vacuum padding is specified if (vacuum !== null) { for (let i = 0; i < 3; i++) { if (axis.includes(i)) { - this.cell[i][i] += 2 * vacuum; // Increase the cell dimension + this.cell[i][i] += 2 * vacuum; } } } } deleteAtoms(indices) { - // Sort the indices in descending order to avoid index shifting - indices.sort((a, b) => b - a); - - for (const index of indices) { - if (index >= 0 && index < this.positions.length) { - this.positions.splice(index, 1); // Remove the atom's position data - this.symbols.splice(index, 1); // Remove the atom's species index + if (!Array.isArray(indices)) { + indices = [indices]; + } + const indexSet = new Set(indices); + this.positions = this.positions.filter((_, i) => !indexSet.has(i)); + this.symbols = this.symbols.filter((_, i) => !indexSet.has(i)); + // Remove attributes in atom domain + for (const domain in this.attributes) { + for (const name in this.attributes["atom"]) { + this.attributes[domain][name] = this.attributes[domain][name].filter((_, i) => !indexSet.has(i)); + } + } + // Remove kind symbols that are not used anymore and their attributes + const usedSymbols = new Set(this.symbols); + for (const symbol in this.kinds) { + if (!usedSymbols.has(symbol)) { + delete this.kinds[symbol]; + for (const name in this.attributes["kind"]) { + delete this.attributes["kind"][name][symbol]; + } } } } - replaceAtoms(indices, newSpeciesSymbol) { - // if newSpeciesSymbol is not in species, add it - if (!this.species[newSpeciesSymbol]) { - this.addSpecies(newSpeciesSymbol); + + replaceAtoms(indices, newKindSymbol, newKindElement = null) { + if (!this.kinds[newKindSymbol]) { + this.addKind(newKindSymbol, newKindElement); } for (const index of indices) { if (index >= 0 && index < this.symbols.length) { - // Replace the species of the atom at the specified index - this.symbols[index] = newSpeciesSymbol; + this.symbols[index] = newKindSymbol; + } else { + throw new Error("Index out of bounds."); } } } + toDict() { const dict = { uuid: this.uuid, - species: {}, + kinds: {}, positions: [], cell: Array.from(this.cell || []), pbc: Array.from(this.pbc), symbols: [], }; - // Populate species dictionary - for (const [symbol, specie] of Object.entries(this.species)) { - dict.species[symbol] = specie.element; + for (const [symbol, kind] of Object.entries(this.kinds)) { + dict.kinds[symbol] = kind.element; } - // Populate positions and symbols - for (let i = 0; i < this.positions.length; i++) { - dict.positions.push(Array.from(this.positions[i])); - dict.symbols.push(this.symbols[i]); - } + dict.positions = this.positions.map((position) => [...position]); + dict.symbols = [...this.symbols]; return dict; } - // Function to calculate fractional coordinates + calculateFractionalCoordinates() { if (this.isUndefinedCell()) { throw new Error("Cell matrix is not defined."); @@ -460,13 +456,12 @@ class Atoms { const newAtomsData = { cell: JSON.parse(JSON.stringify(this.cell)), pbc: JSON.parse(JSON.stringify(this.pbc)), - species: {}, // Shallow copy is usually sufficient for an object of primitives + kinds: {}, symbols: [], positions: [], }; - // Initialize attributes for the new Atoms instance - const newAttributes = { atom: {}, species: {} }; + const newAttributes = { atom: {}, kind: {} }; for (const domain in this.attributes) { for (const name in this.attributes[domain]) { newAttributes[domain][name] = domain === "atom" ? [] : this.attributes[domain][name]; @@ -477,18 +472,16 @@ class Atoms { if (index < 0 || index >= this.positions.length) { throw new Error("Index out of bounds."); } - // Add species and position for each atom newAtomsData.symbols.push(this.symbols[index]); newAtomsData.positions.push(this.positions[index]); - // Add attributes for each atom for (const name in this.attributes["atom"]) { newAttributes["atom"][name].push(this.attributes["atom"][name][index]); } }); - const species = new Set(newAtomsData.symbols); - species.forEach((species) => { - newAtomsData.species[species] = this.species[species].element; + const kindsSet = new Set(newAtomsData.symbols); + kindsSet.forEach((kind) => { + newAtomsData.kinds[kind] = this.kinds[kind].element; }); const newAtoms = new Atoms(newAtomsData); @@ -513,12 +506,10 @@ class Atoms { copy() { const newAtomsData = this.toDict(); const newAtoms = new Atoms(newAtomsData); - // Copy attributes - // copy all attributes - const newAttributes = { atom: {}, species: {} }; + const newAttributes = { atom: {}, kind: {}, "inter-kind": {} }; for (const domain in this.attributes) { for (const name in this.attributes[domain]) { - newAttributes[domain][name] = this.attributes[domain][name].slice(); + newAttributes[domain][name] = JSON.parse(JSON.stringify(this.attributes[domain][name])); } } newAtoms.attributes = newAttributes; @@ -526,4 +517,4 @@ class Atoms { } } -export { Species, Atom, Atoms }; +export { Kind, Atom, Atoms }; diff --git a/src/atoms/plugins/atom.js b/src/atoms/plugins/atom.js index a591e9b..47a23b1 100644 --- a/src/atoms/plugins/atom.js +++ b/src/atoms/plugins/atom.js @@ -34,13 +34,13 @@ export class AtomManager { } init() { - /* Initialize the species settings from the viewer.atoms + /* Initialize the settings from the viewer.atoms */ this.viewer.logger.debug("init atom settings"); this.settings = {}; - Object.entries(this.viewer.originalAtoms.species).forEach(([symbol, species]) => { - this.settings[symbol] = this.getDefaultSetting(symbol, species.element); + Object.entries(this.viewer.originalAtoms.kinds).forEach(([symbol, kind]) => { + this.settings[symbol] = this.getDefaultSetting(symbol, kind.element); }); this.updateAtomColors(); } @@ -48,7 +48,7 @@ export class AtomManager { updateAtomColors() { const colors = []; this.viewer.atoms.symbols.forEach((symbol, globalIndex) => { - // if this.viewer.atoms has color attribute in the species domain, use it + // if this.viewer.atoms has color attribute in the kind domain, use it const color = new THREE.Color(this.settings[symbol].color); colors.push(color); }); @@ -59,18 +59,18 @@ export class AtomManager { } getDefaultSetting(symbol, element) { - /* Get the default bond setting for the species1 and species2 */ + /* Get the default bond setting for the kind1 and kind2 */ let color; let radius; - // if species has color attribute in the species domain, use it - if ("color" in this.viewer.atoms.attributes["species"]) { - color = this.viewer.atoms.attributes["species"]["color"][symbol] || "#3d82ed"; + // if color attribute in the kind domain, use it + if ("color" in this.viewer.atoms.attributes["kind"]) { + color = this.viewer.atoms.attributes["kind"]["color"][symbol] || "#3d82ed"; } else { color = elementColors[this.viewer.colorType][element]; } - // if atoms has radii attribute in the species domain, use it - if ("radii" in this.viewer.atoms.attributes["species"]) { - radius = this.viewer.atoms.attributes["species"]["radii"][symbol] || 1; + // if atoms has radii attribute in the kind domain, use it + if ("radii" in this.viewer.atoms.attributes["kind"]) { + radius = this.viewer.atoms.attributes["kind"]["radii"][symbol] || 1; } else { radius = radiiData[this.viewer.radiusType][element] || 1; } @@ -130,7 +130,7 @@ export class AtomManager { } const atomColors = []; imageAtomsList.symbols.forEach((symbol, globalIndex) => { - // if this.viewer.atoms has color attribute in the species domain, use it + // if this.viewer.atoms has color attribute in the kind domain, use it const color = new THREE.Color(this.settings[symbol].color); atomColors.push(color); }); diff --git a/src/atoms/plugins/bond.js b/src/atoms/plugins/bond.js index cd1917c..8168526 100644 --- a/src/atoms/plugins/bond.js +++ b/src/atoms/plugins/bond.js @@ -7,9 +7,9 @@ import { kdTree } from "../../geometry/kdTree.js"; import { searchBoundary } from "./boundary.js"; class Setting { - constructor({ species1, species2, min = 0.0, max = 3.0, color1 = "#3d82ed", color2 = "#3d82ed", radius = 0.1, order = 1, type = 0 }) { - this.species1 = species1; - this.species2 = species2; + constructor({ kind1, kind2, min = 0.0, max = 3.0, color1 = "#3d82ed", color2 = "#3d82ed", radius = 0.1, order = 1, type = 0 }) { + this.kind1 = kind1; + this.kind2 = kind2; this.min = min; this.max = max; this.color1 = convertColor(color1); @@ -21,8 +21,8 @@ class Setting { toDict() { return { - species1: this.species1, - species2: this.species2, + kind1: this.kind1, + kind2: this.kind2, min: this.min, max: this.max, color1: this.color1, @@ -48,7 +48,7 @@ export class BondManager { init() { /* Initialize the bond settings from the viewer.atoms - The default max is the sum of two radius of the species. + The default max is the sum of two radius of the kinds. The default color is from the elementColors. */ this.viewer.logger.debug("init bond settings"); @@ -56,30 +56,28 @@ export class BondManager { this.stickBonds = []; this.lineBonds = []; this.springBonds = []; - Object.entries(this.viewer.originalAtoms.species).forEach(([symbol1, species1]) => { - Object.entries(this.viewer.originalAtoms.species).forEach(([symbol2, species2]) => { - const elementPair = species1.element + "-" + species2.element; + Object.entries(this.viewer.originalAtoms.kinds).forEach(([symbol1, kind1]) => { + Object.entries(this.viewer.originalAtoms.kinds).forEach(([symbol2, kind2]) => { + const elementPair = kind1.element + "-" + kind2.element; // if the elementPair is not in the default_bond_pairs, skip if (default_bond_pairs[elementPair] === undefined) { return; } - const key = species1.symbol + "-" + species2.symbol; - this.settings[key] = this.getDefaultSetting(species1, species2); + const key = symbol1 + "-" + symbol2; + this.settings[key] = this.getDefaultSetting(symbol1, kind1, symbol2, kind2); }); }); } - getDefaultSetting(species1, species2) { - /* Get the default bond setting for the species1 and species2 */ - let color1 = this.viewer.atomManager.settings[species1.symbol].color; - let color2 = this.viewer.atomManager.settings[species2.symbol].color; - const radius1 = this.viewer.atomManager.settings[species1.symbol].radius; - const radius2 = this.viewer.atomManager.settings[species2.symbol].radius; + getDefaultSetting(symbol1, kind1, symbol2, kind2) { + /* Get the default bond setting for the kind1 and kind2 */ + let color1 = this.viewer.atomManager.settings[symbol1].color; + let color2 = this.viewer.atomManager.settings[symbol2].color; + const radius1 = this.viewer.atomManager.settings[symbol1].radius; + const radius2 = this.viewer.atomManager.settings[symbol2].radius; let min = 0.0; let max = (radius1 + radius2) * 1.1; - const symbol1 = species1.symbol; - const symbol2 = species2.symbol; - const type = default_bond_pairs[species1.element + "-" + species2.element][2]; + const type = default_bond_pairs[kind1.element + "-" + kind2.element][2]; // if type is hydrogen bond, set the min as the max, and the max as the max + 1 if (type === 1) { min = max + 0.4; @@ -88,7 +86,7 @@ export class BondManager { color1 = "#808080"; color2 = "#808080"; } - const setting = new Setting({ species1: symbol1, species2: symbol2, min, max, color1, color2, type }); + const setting = new Setting({ kind1: symbol1, kind2: symbol2, min, max, color1, color2, type }); return setting; } @@ -103,10 +101,10 @@ export class BondManager { } // Modify addSetting to accept a single object parameter - addSetting({ species1, species2, radius, min = 0.0, max = 3.0, color1 = "#3d82ed", color2 = "#3d82ed", order = 1, type = 0 }) { + addSetting({ kind1, kind2, radius, min = 0.0, max = 3.0, color1 = "#3d82ed", color2 = "#3d82ed", order = 1, type = 0 }) { /* Add a new setting to the bond */ - const setting = new Setting({ species1, species2, radius, min, max, color1, color2, order, type }); - const key = species1 + "-" + species2; + const setting = new Setting({ kind1, kind2, radius, min, max, color1, color2, order, type }); + const key = kind1 + "-" + kind2; this.settings[key] = setting; } @@ -114,9 +112,9 @@ export class BondManager { /* Build a dictionary of cutoffs */ const cutoffDict = {}; Object.values(this.settings).forEach((setting) => { - const species1 = setting.species1; - const species2 = setting.species2; - const key1 = species1 + "-" + species2; + const kind1 = setting.kind1; + const kind2 = setting.kind2; + const key1 = kind1 + "-" + kind2; cutoffDict[key1] = setting.toDict(); }); this.viewer.logger.debug("cutoffDict: ", cutoffDict); @@ -347,7 +345,7 @@ export function drawStick(atoms, bondList, bondIndices, settings, radius = 0.1, var position2 = atoms.positions[index2].map((value, index) => value + calculateCartesianCoordinates(atoms.cell, offset2)[index]); position2 = new THREE.Vector3(...position2); // Setting color for each material - const key = atoms.species[atoms.symbols[index1]].symbol + "-" + atoms.species[atoms.symbols[index2]].symbol; + const key = atoms.symbols[index1] + "-" + atoms.symbols[index2]; // if atomColors is not null, use the atomColors, otherwise use the settings const color1 = atomColors ? atomColors[index1] : settings[key].color1; const midpoint1 = new THREE.Vector3().lerpVectors(position1, position2, 0.25); @@ -400,7 +398,7 @@ export function drawLine(atoms, bondList, bondIndices, settings, materialType = vertices.push(position1.x, position1.y, position1.z); vertices.push(position2.x, position2.y, position2.z); // Setting color for each bond - const key = atoms.species[atoms.symbols[index1]].symbol + "-" + atoms.species[atoms.symbols[index2]].symbol; + const key = atoms.symbols[index1] + "-" + atoms.symbols[index2]; const color1 = settings[key].color1; const color2 = settings[key].color2; @@ -671,7 +669,7 @@ export function findNeighbors(atoms, cutoffs, include_self = false, pbc = true) /* Function to find neighbors within a certain cutoff Args: atoms: Atoms object - cutoffs: Dictionary of cutoffs for each species pair, has min and max + cutoffs: Dictionary of cutoffs for each kind pair, has min and max include_self: Include self in the neighbors list pbc: Periodic boundary conditions */ @@ -726,8 +724,8 @@ export function findNeighbors(atoms, cutoffs, include_self = false, pbc = true) offsets.forEach(([atomIndex1, offset1], idx1) => { // skip the atoms not in the original cell if (offset1[0] != 0 || offset1[1] != 0 || offset1[2] != 0) return; - const species1 = atoms.species[atoms.symbols[atomIndex1]].symbol; - const radius1 = covalentRadii[species1] * 1.1 || 1; + const kind1 = atoms.symbols[atomIndex1]; + const radius1 = covalentRadii[kind1] * 1.1 || 1; const pos1 = positions[idx1]; const point = { x: positions[idx1][0], y: positions[idx1][1], z: positions[idx1][2] }; @@ -741,7 +739,7 @@ export function findNeighbors(atoms, cutoffs, include_self = false, pbc = true) if (idx1 == idx2) return; const atomIndex2 = offsets[idx2][0]; if (!include_self && atomIndex1 == atomIndex2) return; - const key = species1 + "-" + atoms.species[atoms.symbols[atomIndex2]].symbol; + const key = kind1 + "-" + atoms.symbols[atomIndex2]; // if key is not in cutoffs, skip if (!cutoffs[key]) return; const pos2 = positions[idx2]; diff --git a/src/atoms/plugins/boundary.js b/src/atoms/plugins/boundary.js index a128098..3742da9 100644 --- a/src/atoms/plugins/boundary.js +++ b/src/atoms/plugins/boundary.js @@ -30,23 +30,20 @@ export class BoundaryManager { } init() { - /* Initialize the species settings from the viewer.atoms + /* Initialize the settings from the viewer.atoms */ this.viewer.logger.debug("init atom settings"); this.settings = {}; - const speciesSet = new Set(this.viewer.originalAtoms.symbols); - const speciesList = Array.from(speciesSet); - for (let i = 0; i < speciesList.length; i++) { - const species = speciesList[i]; - this.settings[species] = this.getDefaultSetting(species); - } + Object.entries(this.viewer.originalAtoms.kinds).forEach(([symbol, kind]) => { + this.settings[symbol] = this.getDefaultSetting(symbol, kind); + }); } - getDefaultSetting(species) { - /* Get the default bond setting for the species1 and species2 */ - const color = elementColors[this.viewer.colorType][species]; - const radius = radiiData[this.viewer.radiusType][species]; - const setting = new Setting({ element: species, symbol: species, radius, color }); + getDefaultSetting(symbol, kind) { + /* Get the default bond setting for the kind1 and kind2 */ + const color = elementColors[this.viewer.colorType][kind.element]; + const radius = radiiData[this.viewer.radiusType][kind.element]; + const setting = new Setting({ element: kind.element, symbol: symbol, radius, color }); return setting; } @@ -60,10 +57,10 @@ export class BoundaryManager { }); } - addSetting({ species1, species2, radius, min = 0.0, max = 3.0, color1 = "#3d82ed", color2 = "#3d82ed", order = 1 }) { + addSetting({ kind1, kind2, radius, min = 0.0, max = 3.0, color1 = "#3d82ed", color2 = "#3d82ed", order = 1 }) { /* Add a new setting to the bond */ - const setting = new Setting({ species1, species2, radius, min, max, color1, color2, order }); - const key = species1 + "-" + species2; + const setting = new Setting({ kind1, kind2, radius, min, max, color1, color2, order }); + const key = kind1 + "-" + kind2; this.settings[key] = setting; } @@ -80,7 +77,7 @@ export function getImageAtoms(atoms, offsets) { // create a new atoms with the boundary atoms const imageAtoms = new Atoms(); imageAtoms.cell = atoms.cell; - imageAtoms.species = atoms.species; + imageAtoms.kinds = atoms.kinds; const positions = offsets.map((offset) => { // Get original position const originalPosition = atoms.positions[offset[0]]; @@ -113,7 +110,7 @@ export function searchBoundary( return []; } let positions = atoms.positions; - let species = atoms.species; // Assuming species is a property of atoms + let kinds = atoms.kinds; // Assuming kinds is a property of atoms if (typeof boundary === "number") { boundary = [ @@ -136,7 +133,7 @@ export function searchBoundary( let i0 = 0; let offsets = []; - let speciesExtended = []; + let kindsExtended = []; for (let m0 = ib[0][0]; m0 < ib[1][0]; m0++) { for (let m1 = ib[0][1]; m1 < ib[1][1]; m1++) { @@ -149,7 +146,7 @@ export function searchBoundary( npositions[i] = npositions[i].map((val, idx) => val + (idx === 0 ? m0 : idx === 1 ? m1 : m2)); offsets.push([i % n, [m0, m1, m2]]); } - speciesExtended = speciesExtended.concat(species); + kindsExtended = kindsExtended.concat(kinds); i0 = i1; } } diff --git a/src/atoms/plugins/highlight.js b/src/atoms/plugins/highlight.js index 140618e..394447e 100644 --- a/src/atoms/plugins/highlight.js +++ b/src/atoms/plugins/highlight.js @@ -31,7 +31,7 @@ export class HighlightManager { } init() { - /* Initialize the species settings from the viewer.atoms */ + /* Initialize the kind settings from the viewer.atoms */ this.viewer.logger.debug("init highlight settings"); this.settings = { selection: new Setting({ indices: [], scale: 1.1, color: "#ffff00" }), diff --git a/src/atoms/plugins/polyhedra.js b/src/atoms/plugins/polyhedra.js index 958a192..91532ad 100644 --- a/src/atoms/plugins/polyhedra.js +++ b/src/atoms/plugins/polyhedra.js @@ -8,15 +8,15 @@ import { convertColor } from "../utils.js"; const defaultColor = 0xffffff; class Setting { - constructor({ species, color = "#3d82ed", show_edge = false }) { - this.species = species; + constructor({ symbol, color = "#3d82ed", show_edge = false }) { + this.symbol = symbol; this.color = convertColor(color); this.show_edge = show_edge; } toDict() { return { - species: this.species, + symbol: this.symbol, color: this.color, show_edge: this.show_edge, }; @@ -35,24 +35,20 @@ export class PolyhedraManager { init() { /* Initialize the polyhedra settings from the viewer.atoms - The default max is the sum of two radius of the species. + The default max is the sum of two radius of the kinds. The default color is from the elementColors. */ this.viewer.logger.debug("init PolyhedraManager"); this.settings = []; const atoms = this.viewer.atoms; - const symbols = atoms.symbols; - const speciesSet = new Set(symbols); - const speciesList = Array.from(speciesSet); - for (let i = 0; i < speciesList.length; i++) { - if (!elementsWithPolyhedra.includes(speciesList[i])) { - continue; + Object.entries(this.viewer.originalAtoms.kinds).forEach(([symbol, kind]) => { + if (!elementsWithPolyhedra.includes(kind.element)) { + return; } - const species = speciesList[i]; - const color = elementColors[this.viewer.colorType][species]; - const setting = new Setting({ species, color }); + const color = elementColors[this.viewer.colorType][kind.element]; + const setting = new Setting({ symbol, color }); this.settings.push(setting); - } + }); } fromSettings(settings) { @@ -66,9 +62,9 @@ export class PolyhedraManager { } // Modify addSetting to accept a single object parameter - addSetting({ species, color = "#3d82ed", show_edge = false }) { + addSetting({ symbol, color = "#3d82ed", show_edge = false }) { /* Add a new setting to the polyhedra */ - const setting = new Setting({ species, color, show_edge }); + const setting = new Setting({ symbol, color, show_edge }); this.settings.push(setting); } @@ -76,7 +72,7 @@ export class PolyhedraManager { /* Build a dictionary of cutoffs */ const cutoffDict = {}; this.settings.forEach((setting) => { - cutoffDict[setting.species] = setting.toDict(); + cutoffDict[setting.symbol] = setting.toDict(); }); return cutoffDict; } @@ -174,7 +170,7 @@ export function drawPolyhedras(atoms, polyhedras, bondList, colorType = "CPK", m export function filterBondMap(bondMap, symbols, elements, modelPolyhedras) { /* loop through bondMap and filter out only those atoms that have - four or more bonds and whose species (retrieved from atoms.symbols[atomIndex]) + four or more bonds and whose kind (retrieved from atoms.symbols[atomIndex]) are in a specified list of elements (elements) */ const filteredMap = {}; @@ -183,9 +179,9 @@ export function filterBondMap(bondMap, symbols, elements, modelPolyhedras) { Object.keys(bondMap).forEach((key) => { const atomIndex = bondMap[key]["atomIndex"]; const numBond = bondMap[key]["sticks"].length; - const speciesName = symbols[atomIndex]; + const kindName = symbols[atomIndex]; - if (modelPolyhedras[atomIndex] && numBond >= 4 && elements.includes(speciesName)) { + if (modelPolyhedras[atomIndex] && numBond >= 4 && elements.includes(kindName)) { filteredMap[key] = bondMap[key]; } }); diff --git a/src/index.js b/src/index.js index 013a700..4bc7a49 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,6 @@ // src/index.js import { WEAS } from "./weas.js"; -import { Species, Atom, Atoms } from "./atoms/atoms.js"; +import { Kind, Atom, Atoms } from "./atoms/atoms.js"; import { AtomsViewer } from "./atoms/AtomsViewer.js"; import { parseXYZ } from "./io/parserXYZ.js"; import { parseCIF } from "./io/parserCif.js"; @@ -8,4 +8,4 @@ import { parseCube } from "./io/parserCube.js"; import { elementAtomicNumbers } from "./atoms/atoms_data.js"; // Export the modules to be publicly available -export { WEAS, Species, Atom, Atoms, AtomsViewer, parseXYZ, parseCIF, parseCube, elementAtomicNumbers }; +export { WEAS, Kind, Atom, Atoms, AtomsViewer, parseXYZ, parseCIF, parseCube, elementAtomicNumbers }; diff --git a/src/io/parserCif.js b/src/io/parserCif.js index 81b7d05..10b8a87 100644 --- a/src/io/parserCif.js +++ b/src/io/parserCif.js @@ -4,7 +4,7 @@ import { convertToMatrixFromABCAlphaBetaGamma, calculateCartesianCoordinates } f export function parseCIF(cifString) { const data = { cell: [], - species: {}, + kinds: {}, positions: [], symbols: [], }; diff --git a/src/io/parserCube.js b/src/io/parserCube.js index eeaec59..88537d2 100644 --- a/src/io/parserCube.js +++ b/src/io/parserCube.js @@ -24,7 +24,7 @@ export function parseCube(cubeContent) { // Initialize data structure for Atoms const data = { - species: {}, + kinds: {}, positions: [], symbols: [], }; @@ -37,12 +37,12 @@ export function parseCube(cubeContent) { // Find the element symbol by atomic number const elementSymbol = Object.keys(elementAtomicNumbers).find((key) => elementAtomicNumbers[key] === atomicNumber); - // Update species data if it's a new element - if (!data.species[elementSymbol]) { - data.species[elementSymbol] = elementSymbol; + // Update kinds data if it's a new element + if (!data.kinds[elementSymbol]) { + data.kinds[elementSymbol] = elementSymbol; } - // Add species and position data for the current atom + // Add kinds and position data for the current atom data.symbols.push(elementSymbol); data.positions.push(position); } diff --git a/src/io/parserPOSCAR.js b/src/io/parserPOSCAR.js index 904db08..3153a61 100644 --- a/src/io/parserPOSCAR.js +++ b/src/io/parserPOSCAR.js @@ -39,7 +39,7 @@ function parsePOSCAR(poscarString) { // Create a data object for Atoms const data = { cell: latticeVectors.flat(), - species: {}, + kinds: {}, positions: positions, symbols: atomTypes.flatMap((type, idx) => Array(atomCounts[idx]).fill(type)), }; diff --git a/src/io/parserXYZ.js b/src/io/parserXYZ.js index 693849c..d63face 100644 --- a/src/io/parserXYZ.js +++ b/src/io/parserXYZ.js @@ -21,7 +21,7 @@ function parseXYZ(xyzString) { // Initialize data structure for the current frame const data = { - species: {}, + kinds: {}, positions: [], symbols: [], }; @@ -35,12 +35,12 @@ function parseXYZ(xyzString) { const [element, x, y, z] = parts; - // Update species data if it's a new species - if (!data.species[element]) { - data.species[element] = element; + // Update kinds data if it's a new kinds + if (!data.kinds[element]) { + data.kinds[element] = element; } - // Add species and position data for the current atom + // Add kinds and position data for the current atom data.symbols.push(element); data.positions.push([parseFloat(x), parseFloat(y), parseFloat(z)]); } diff --git a/src/operation/atoms.js b/src/operation/atoms.js index aaa0e7e..fb6d745 100644 --- a/src/operation/atoms.js +++ b/src/operation/atoms.js @@ -24,8 +24,8 @@ class ReplaceOperation extends BaseOperation { } adjust(newSymbol) { - // if newSymbol not in elementAtomicNumbers, and newSymbol not in this.weas.avr.atoms.species, ship the adjustment - if (!(newSymbol in elementAtomicNumbers || newSymbol in this.weas.avr.atoms.species)) { + // if newSymbol not in elementAtomicNumbers, and newSymbol not in this.weas.avr.atoms.kinds, ship the adjustment + if (!(newSymbol in elementAtomicNumbers || newSymbol in this.weas.avr.atoms.kinds)) { return; } this.symbol = newSymbol; @@ -68,7 +68,7 @@ class AddAtomOperation extends BaseOperation { adjust(newSymbol, newPosition) { // if newSymbol not in elementAtomicNumbers, ship the adjustment - if (!(newSymbol in elementAtomicNumbers || newSymbol in this.weas.avr.atoms.species)) { + if (!(newSymbol in elementAtomicNumbers || newSymbol in this.weas.avr.atoms.kinds)) { return; } this.weas.avr.atoms = this.initialAtoms.copy(); diff --git a/tests/atoms.test.mjs b/tests/atoms.test.mjs index 510d63f..72b3e8f 100644 --- a/tests/atoms.test.mjs +++ b/tests/atoms.test.mjs @@ -39,7 +39,7 @@ describe("Atoms class", () => { }); it("initializes an empty Atoms instance", () => { - expect(atoms.species).toEqual({}); + expect(atoms.kinds).toEqual({}); expect(atoms.symbols).toEqual([]); expect(atoms.positions).toEqual([]); expect(atoms.cell).toEqual([ @@ -51,9 +51,9 @@ describe("Atoms class", () => { }); it("adds a species correctly", () => { - atoms.addSpecies("H"); - expect(atoms.species).toHaveProperty("H"); - expect(atoms.species["H"]).toEqual(new Species("H")); + atoms.addKind("H"); + expect(atoms.kinds).toHaveProperty("H"); + expect(atoms.kinds["H"]).toEqual(new Species("H")); }); // Add more tests for other methods like setCell, addAtom, removeAtom, etc. diff --git a/tests/e2e/gui.spec.js b/tests/e2e/gui.spec.js index 5df2ff3..dc5e4bb 100644 --- a/tests/e2e/gui.spec.js +++ b/tests/e2e/gui.spec.js @@ -51,7 +51,7 @@ test("VectorField", async ({ page }) => { test("ColorBy", async ({ page }) => { await page.goto("http://127.0.0.1:8080/tests/e2e/testColorBy.html"); await expect(page).toHaveScreenshot(); - // color by species + // color by kind await page.evaluate(() => { window.editor.avr.colorBy = "Element"; window.editor.avr.atomManager.settings["C"].color = "##eb4034"; @@ -60,7 +60,7 @@ test("ColorBy", async ({ page }) => { window.editor.avr.atomManager.settings["S"].color = "#FFFF00"; window.editor.avr.drawModels(); }); - await expect(page).toHaveScreenshot("Color-species.png"); + await expect(page).toHaveScreenshot("Color-kind.png"); }); test("Highlight Atoms", async ({ page }) => { diff --git a/tests/e2e/gui.spec.js-snapshots/Color-species-chromium-linux.png b/tests/e2e/gui.spec.js-snapshots/Color-kind-chromium-linux.png similarity index 100% rename from tests/e2e/gui.spec.js-snapshots/Color-species-chromium-linux.png rename to tests/e2e/gui.spec.js-snapshots/Color-kind-chromium-linux.png diff --git a/tests/e2e/testSpecies.html b/tests/e2e/testSpecies.html index 08c7343..4be62a0 100644 --- a/tests/e2e/testSpecies.html +++ b/tests/e2e/testSpecies.html @@ -24,7 +24,7 @@ const filename = "c2h6so.xyz"; fetchFile(filename).then((fileContent) => { const atoms = weas.parseXYZ(fileContent); - atoms[0].addSpecies("C1", "C"); + atoms[0].addKind("C1", "C"); atoms[0].symbols[3] = "C1"; let editor = new weas.WEAS({ domElement }); editor.avr.atoms = atoms; diff --git a/tests/parserXYZ.test.mjs b/tests/parserXYZ.test.mjs index 32f53cd..61f5861 100644 --- a/tests/parserXYZ.test.mjs +++ b/tests/parserXYZ.test.mjs @@ -13,10 +13,10 @@ H 0.000000 0.757160 0.482080 console.log("atoms: ", atoms); expect(atoms).toBeDefined(); - expect(atoms.species).toHaveProperty("H"); - expect(atoms.species).toHaveProperty("O"); - expect(atoms.species["H"]).toEqual(new Species("H")); - expect(atoms.species["O"]).toEqual(new Species("O")); + expect(atoms.kinds).toHaveProperty("H"); + expect(atoms.kinds).toHaveProperty("O"); + expect(atoms.kinds["H"]).toEqual(new Species("H")); + expect(atoms.kinds["O"]).toEqual(new Species("O")); expect(atoms.positions.length).toBe(3); expect(atoms.symbols).toEqual(["O", "H", "H"]); console.log(atoms.positions[0]);