Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions pyiron_lammps/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,12 @@ class LammpsStructure:
input_file_name:
"""

def __init__(self, bond_dict: Optional[Dict] = None, units: str = "metal"):
def __init__(
self,
bond_dict: Optional[Dict] = None,
units: str = "metal",
atom_type: str = "atomic",
):
self._string_input: str = ""
self._structure: Optional[Atoms] = None
self._potential: Optional[Any] = None
Expand All @@ -209,6 +214,7 @@ def __init__(self, bond_dict: Optional[Dict] = None, units: str = "metal"):
self._bond_dict: Optional[Dict] = bond_dict
self._force_skewed: bool = False
self._units: str = units
self._atom_type: str = atom_type

@property
def potential(self) -> Any:
Expand Down Expand Up @@ -238,7 +244,10 @@ def structure(self, structure: Atoms):

"""
self._structure = structure
input_str = self.structure_atomic()
if self._atom_type == "charge":
input_str = self.structure_charge()
else: # self.atom_type == 'atomic'
input_str = self.structure_atomic()
self._string_input = input_str + self._get_velocities_input_string()

def _get_velocities_input_string(self) -> str:
Expand Down Expand Up @@ -382,6 +391,45 @@ def structure_atomic(self) -> str:
+ "\n"
)

def structure_charge(self):
"""
Create atom structure including the atom charges.

By convention the LAMMPS atom type numbers are chose alphabetically for the chemical species.

Returns: LAMMPS readable structure.

"""
species_lammps_id_dict = self.get_lammps_id_dict(self.el_eam_lst)
atoms = "Atoms\n\n"
coords = self.rotate_positions(self._structure)
el_charge_lst = self._structure.get_initial_charges()
el_lst = self._structure.get_chemical_symbols()
for id_atom, (el, coord) in enumerate(zip(el_lst, coords)):
dim = self._structure.positions.shape[1]
c = np.zeros(3)
c[:dim] = coord
atoms += (
"{0:d} {1:d} {2:f} {3:.15f} {4:.15f} {5:.15f}".format(
id_atom + 1,
species_lammps_id_dict[el],
el_charge_lst[id_atom],
c[0],
c[1],
c[2],
)
+ "\n"
)
return (
self.lammps_header(
structure=self.structure,
cell_dimensions=self.simulation_cell(),
species_lammps_id_dict=species_lammps_id_dict,
)
+ atoms
+ "\n"
)
Comment on lines +394 to +431
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Missing structure validation check.

Unlike structure_atomic() (lines 367-368), this method doesn't check if self._structure is None before accessing it, which could cause an AttributeError.

Apply this diff to add the validation:

 def structure_charge(self):
     """
     Create atom structure including the atom charges.

     By convention the LAMMPS atom type numbers are chose alphabetically for the chemical species.

     Returns: LAMMPS readable structure.

     """
+    if self._structure is None:
+        raise ValueError("Structure not set")
     species_lammps_id_dict = self.get_lammps_id_dict(self.el_eam_lst)
🤖 Prompt for AI Agents
In pyiron_lammps/structure.py around lines 394 to 431, add the same validation
present in structure_atomic (around lines 367-368): check if self._structure is
None at the start of structure_charge and raise a clear exception (or otherwise
handle the missing structure) before any access to self._structure to prevent
AttributeError; ensure the new check mirrors the project’s existing error
type/message used in structure_atomic.


def rotate_positions(self, structure: Atoms) -> List[Tuple[float, float, float]]:
"""
Rotate all atomic positions in given structure according to new Prism cell
Expand Down
29 changes: 29 additions & 0 deletions tests/test_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,32 @@ def test_skewed_cell(self):
cell[0, 1] = cell[0, 0] * 0.6
up = UnfoldingPrism(cell=cell)
self.assertFalse(np.all(np.isclose(up.A, cell)))

def test_structure_charge(self):
atoms = Atoms("Fe1", positions=np.zeros((1, 3)), cell=np.eye(3))
atoms.set_initial_charges(charges=np.ones(len(atoms)) * 2.0)
lmp_structure = LammpsStructure(atom_type="charge")
lmp_structure._el_eam_lst = ["Fe"]
lmp_structure.structure = atoms
self.assertEqual(
lmp_structure._string_input.split("\n"),
[
"Start File for LAMMPS ",
"1 atoms ",
"1 atom types ",
"",
"0. 1.000000000000000 xlo xhi",
"0. 1.000000000000000 ylo yhi",
"0. 1.000000000000000 zlo zhi",
"",
"Masses",
"",
" 1 55.845000 # (Fe) ",
"",
"Atoms",
"",
"1 1 2.000000 0.000000000000000 0.000000000000000 0.000000000000000",
"",
"",
],
)
Loading