From 1f6027f062a9eca5d8006741df91545d8ec01ed3 Mon Sep 17 00:00:00 2001 From: Sebastiaan Huber Date: Wed, 6 Sep 2023 07:52:36 +0200 Subject: [PATCH] Dependencies: Add compatibility for `pymatgen>=v2023.9.2` (#6109) As of v2023.9.2, the ``properties`` argument of the `Specie` class is removed and the ``spin`` argument should be used instead. See: https://github.com/materialsproject/pymatgen/commit/118c245d6082fe0b13e19d348fc1db9c0d512019 The ``spin`` argument was introduced in v2023.6.28. See: https://github.com/materialsproject/pymatgen/commit/9f2b3939af45d5129e0778d371d814811924aeb6 Instead of removing support for versions older than v2023.6.28 the code is updated to be able to deal with the new version where `properties` is no longer supported. Cherry-pick: 4e0e7d8e9fd10c4adc3630cf24cebdf749f95351 --- aiida/orm/nodes/data/structure.py | 34 ++++++++++++++++++++------- requirements/requirements-py-3.11.txt | 2 +- tests/test_dataclasses.py | 19 +++++++++++---- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/aiida/orm/nodes/data/structure.py b/aiida/orm/nodes/data/structure.py index 8f8dc95e47..20800a5497 100644 --- a/aiida/orm/nodes/data/structure.py +++ b/aiida/orm/nodes/data/structure.py @@ -834,7 +834,17 @@ def build_kind_name(species_and_occu): species = list(species_and_occu.keys()) occupations = list(species_and_occu.values()) - has_spin = any(specie.as_dict().get('properties', {}).get('spin', 0) != 0 for specie in species) + # As of v2023.9.2, the ``properties`` argument is removed and the ``spin`` argument should be used. + # See: https://github.com/materialsproject/pymatgen/commit/118c245d6082fe0b13e19d348fc1db9c0d512019 + # The ``spin`` argument was introduced in v2023.6.28. + # See: https://github.com/materialsproject/pymatgen/commit/9f2b3939af45d5129e0778d371d814811924aeb6 + has_spin_attribute = hasattr(species[0], '_spin') + + if has_spin_attribute: + has_spin = any(specie.spin != 0 for specie in species) + else: + has_spin = any(specie.as_dict().get('properties', {}).get('spin', 0) != 0 for specie in species) + has_partial_occupancies = (len(occupations) != 1 or occupations[0] != 1.0) if has_partial_occupancies and has_spin: @@ -847,7 +857,10 @@ def build_kind_name(species_and_occu): # If there is spin, we can only have a single specie, otherwise we would have raised above specie = species[0] - spin = specie.as_dict().get('properties', {}).get('spin', 0) + if has_spin_attribute: + spin = specie.spin + else: + spin = specie.as_dict().get('properties', {}).get('spin', 0) if spin < 0: kind_name += '1' @@ -1846,13 +1859,16 @@ def _get_object_pymatgen_structure(self, **kwargs): kind = self.get_kind(site.kind_name) if len(kind.symbols) != 1 or (len(kind.weights) != 1 or sum(kind.weights) < 1.): raise ValueError('Cannot set partial occupancies and spins at the same time') - species.append( - Specie( - kind.symbols[0], - oxidation_state, - properties={'spin': -1 if kind.name.endswith('1') else 1 if kind.name.endswith('2') else 0} - ) - ) + spin = -1 if kind.name.endswith('1') else 1 if kind.name.endswith('2') else 0 + try: + specie = Specie(kind.symbols[0], oxidation_state, properties={'spin': spin}) # pylint: disable=unexpected-keyword-arg + except TypeError: + # As of v2023.9.2, the ``properties`` argument is removed and the ``spin`` argument should be used. + # See: https://github.com/materialsproject/pymatgen/commit/118c245d6082fe0b13e19d348fc1db9c0d512019 + # The ``spin`` argument was introduced in v2023.6.28. + # See: https://github.com/materialsproject/pymatgen/commit/9f2b3939af45d5129e0778d371d814811924aeb6 + specie = Specie(kind.symbols[0], oxidation_state, spin=spin) # pylint: disable=unexpected-keyword-arg + species.append(specie) else: # case when no spin are defined for site in self.sites: diff --git a/requirements/requirements-py-3.11.txt b/requirements/requirements-py-3.11.txt index 92afc7c8b0..398e7d5fdc 100644 --- a/requirements/requirements-py-3.11.txt +++ b/requirements/requirements-py-3.11.txt @@ -133,7 +133,7 @@ pycparser==2.21 pydantic==1.10.9 pydata-sphinx-theme==0.8.1 pygments==2.15.1 -pymatgen==2023.5.31 +pymatgen==2023.9.2 pympler==0.9 pymysql==0.9.3 pynacl==1.5.0 diff --git a/tests/test_dataclasses.py b/tests/test_dataclasses.py index 551bc0716d..954dea434d 100644 --- a/tests/test_dataclasses.py +++ b/tests/test_dataclasses.py @@ -2247,10 +2247,16 @@ def test_partial_occ_and_spin(self): from pymatgen.core.periodic_table import Specie from pymatgen.core.structure import Structure - Fe_spin_up = Specie('Fe', 0, properties={'spin': 1}) - Mn_spin_up = Specie('Mn', 0, properties={'spin': 1}) - Fe_spin_down = Specie('Fe', 0, properties={'spin': -1}) - Mn_spin_down = Specie('Mn', 0, properties={'spin': -1}) + try: + Fe_spin_up = Specie('Fe', 0, spin=1) # pylint: disable=unexpected-keyword-arg + Mn_spin_up = Specie('Mn', 0, spin=1) # pylint: disable=unexpected-keyword-arg + Fe_spin_down = Specie('Fe', 0, spin=-1) # pylint: disable=unexpected-keyword-arg + Mn_spin_down = Specie('Mn', 0, spin=-1) # pylint: disable=unexpected-keyword-arg + except TypeError: + Fe_spin_up = Specie('Fe', 0, properties={'spin': 1}) # pylint: disable=unexpected-keyword-arg + Mn_spin_up = Specie('Mn', 0, properties={'spin': 1}) # pylint: disable=unexpected-keyword-arg + Fe_spin_down = Specie('Fe', 0, properties={'spin': -1}) # pylint: disable=unexpected-keyword-arg + Mn_spin_down = Specie('Mn', 0, properties={'spin': -1}) # pylint: disable=unexpected-keyword-arg FeMn1 = Composition({Fe_spin_up: 0.5, Mn_spin_up: 0.5}) FeMn2 = Composition({Fe_spin_down: 0.5, Mn_spin_down: 0.5}) a = Structure( @@ -2440,7 +2446,10 @@ def test_roundtrip_spins(self): b = a.get_pymatgen(add_spin=True) # check the spins - assert [s.as_dict()['properties']['spin'] for s in b.species] == [-1, -1, -1, -1, 1, 1, 1, 1] + try: + assert [s.as_dict()['spin'] for s in b.species] == [-1, -1, -1, -1, 1, 1, 1, 1] + except KeyError: + assert [s.as_dict()['properties']['spin'] for s in b.species] == [-1, -1, -1, -1, 1, 1, 1, 1] # back to StructureData c = StructureData(pymatgen=b) assert c.get_site_kindnames() == ['Mn1', 'Mn1', 'Mn1', 'Mn1', 'Mn2', 'Mn2', 'Mn2', 'Mn2']