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
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ classifiers = [
]

dependencies = [
"steer-core==0.2.19",
# Compatible-release range, not an exact pin: an exact pin means a steer-core
# security patch cannot be installed without rebuilding this package too.
# A deploy's reproducibility comes from its vendored wheel set, not from here.
"steer-core>=0.2.22,<0.3",
]

[project.optional-dependencies]
Expand Down
178 changes: 178 additions & 0 deletions steer_materials/Atoms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# SPDX-FileCopyrightText: 2024-2026 Stanford University
# SPDX-License-Identifier: AGPL-3.0-or-later

from steer_core.Constants.Units import *
from steer_core.Mixins.TypeChecker import ValidationMixin
from steer_core.Mixins.Serializer import SerializerMixin
from steer_core.Mixins.Dunder import DunderMixin
from steer_core.Mixins.Propagation import PropagationMixin

from steer_core.Decorators.General import recalculate

from steer_core.Constants.Periodic_table import (
atomic_numbers_to_symbols,
atomic_numbers_to_names,
atomic_numbers_to_masses
)

from steer_core.Constants.Units import AU_TO_KG, KG_TO_AU

class Atom(
ValidationMixin,
SerializerMixin,
DunderMixin,
PropagationMixin
):

def __init__(
self,
symbol: str,
atomic_mass: float = None,
charge: float = 0.0
):

self._update_properties = False

self.symbol = symbol
self.atomic_mass = atomic_mass
self.charge = charge

self._calculate_all_properties()
self._update_properties = True

def _calculate_all_properties(self):
"""
Calculate all properties of the atom based on the provided information.
"""
self._calculate_mass_from_symbol()
self._calculate_name_from_symbol()
self._calculate_atomic_number_from_symbol()

def _calculate_mass_from_name(self):
"""
Calculate the atomic mass based on the atom's name.
"""
atomic_number = {v: k for k, v in atomic_numbers_to_names.items()}[self._name]
self._atomic_mass = atomic_numbers_to_masses[atomic_number] * AU_TO_KG

def _calculate_symbol_from_name(self):
"""
Calculate the atomic symbol based on the atom's name.
"""
atomic_number = {v: k for k, v in atomic_numbers_to_names.items()}[self._name]
self._symbol = atomic_numbers_to_symbols[atomic_number]

def _calculate_atomic_number_from_name(self):
"""
Calculate the atomic number based on the atom's name.
"""
atomic_number = {v: k for k, v in atomic_numbers_to_names.items()}[self._name]
self._atomic_number = atomic_number

def _calculate_mass_from_symbol(self):
"""
Calculate the atomic mass based on the atom's symbol.
"""
atomic_number = {v: k for k, v in atomic_numbers_to_symbols.items()}[self._symbol]
self._atomic_mass = atomic_numbers_to_masses[atomic_number] * AU_TO_KG

def _calculate_name_from_symbol(self):
"""
Calculate the atomic name based on the atom's symbol.
"""
atomic_number = {v: k for k, v in atomic_numbers_to_symbols.items()}[self._symbol]
self._name = atomic_numbers_to_names[atomic_number]

def _calculate_atomic_number_from_symbol(self):
"""
Calculate the atomic number based on the atom's symbol.
"""
self._atomic_number = {v: k for k, v in atomic_numbers_to_symbols.items()}[self._symbol]

def _calculate_mass_from_atomic_number(self):
"""
Calculate the atomic mass based on the atom's atomic number.
"""
self._atomic_mass = atomic_numbers_to_masses[self._atomic_number] * AU_TO_KG

def _calculate_name_from_atomic_number(self):
"""
Calculate the atomic name based on the atom's atomic number.
"""
self._name = atomic_numbers_to_names[self._atomic_number]

def _calculate_symbol_from_atomic_number(self):
"""
Calculate the atomic symbol based on the atom's atomic number.
"""
self._symbol = atomic_numbers_to_symbols[self._atomic_number]

def _calculate_name_from_mass(self):
"""
Calculate the atomic name based on the atom's atomic mass.
"""
self._name = {v: k for k, v in atomic_numbers_to_masses.items()}[self.atomic_mass]

def _calculate_symbol_from_mass(self):
"""
Calculate the atomic symbol based on the atom's atomic mass.
"""
atomic_number = {v: k for k, v in atomic_numbers_to_masses.items()}[self.atomic_mass]
self._symbol = atomic_numbers_to_symbols[atomic_number]

def _calculate_atomic_number_from_mass(self):
"""
Calculate the atomic number based on the atom's atomic mass.
"""
self._atomic_number = {v: k for k, v in atomic_numbers_to_masses.items()}[self.atomic_mass]

@property
def name(self) -> str:
return self._name

@property
def symbol(self) -> str:
return self._symbol

@property
def atomic_number(self) -> int:
return self._atomic_number

@property
def atomic_mass(self) -> float:
return round(self._atomic_mass * KG_TO_AU, 3)

@name.setter
@recalculate("mass_from_name")
@recalculate("symbol_from_name")
@recalculate("atomic_number_from_name")
def name(self, value: str):
self.validate_string(value, "name")
self._name = value

@symbol.setter
@recalculate("atomic_number_from_symbol")
@recalculate("mass_from_symbol")
@recalculate("name_from_symbol")
def symbol(self, value: str):
self.validate_string(value, "symbol")
self._symbol = value

@atomic_number.setter
@recalculate("symbol_from_atomic_number")
@recalculate("mass_from_atomic_number")
@recalculate("name_from_atomic_number")
def atomic_number(self, value: int):
self.validate_positive_int(value, "atomic_number")
self._atomic_number = value

@atomic_mass.setter
@recalculate("name_from_mass")
@recalculate("symbol_from_mass")
@recalculate("atomic_number_from_mass")
def atomic_mass(self, value: float):
self.validate_positive_float(value, "atomic_mass")
self._atomic_mass = value * AU_TO_KG



Empty file added steer_materials/Crystal.py
Empty file.
2 changes: 1 addition & 1 deletion steer_materials/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# SPDX-FileCopyrightText: 2024-2026 Stanford University
# SPDX-License-Identifier: AGPL-3.0-or-later

__version__ = "0.1.45"
__version__ = "0.1.48"

40 changes: 40 additions & 0 deletions test/test_atoms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import unittest
from steer_core.Constants.Units import AU_TO_KG

from steer_materials.Atoms import Atom


class TestAtom(unittest.TestCase):

def setUp(self):
self.atom = Atom(symbol="Pu", atomic_mass=244.0)

def test_atom_properties(self):
self.assertEqual(self.atom.name, "Plutonium")
self.assertEqual(self.atom.symbol, "Pu")
self.assertEqual(self.atom.atomic_number, 94)
self.assertAlmostEqual(self.atom._atomic_mass, 244.0 * AU_TO_KG)

def test_atomic_mass_conversion(self):
# Test that the atomic mass is correctly converted to kg
expected_mass_kg = 244.0 * AU_TO_KG
self.assertAlmostEqual(self.atom._atomic_mass, expected_mass_kg)

def test_name_setter(self):
self.atom.name = "Uranium"
self.assertEqual(self.atom.symbol, "U")
self.assertEqual(self.atom.atomic_number, 92)
self.assertAlmostEqual(self.atom._atomic_mass, 238.02891 * AU_TO_KG)

def test_mass_setter(self):
self.atom.atomic_mass = 238.02891
self.assertAlmostEqual(self.atom._atomic_mass, 238.02891 * AU_TO_KG)
self.assertEqual(self.atom.symbol, "U")
self.assertEqual(self.atom.atomic_number, 92)

def test_symbol_setter(self):
self.atom.symbol = "U"
self.assertEqual(self.atom.name, "Uranium")
self.assertEqual(self.atom.atomic_number, 92)
self.assertAlmostEqual(self.atom._atomic_mass, 238.02891 * AU_TO_KG)

Loading