24
24
from pymatgen .util .string import Stringify , formula_double_format
25
25
26
26
if TYPE_CHECKING :
27
- from collections .abc import Generator , ItemsView , Iterator
27
+ from collections .abc import Generator , ItemsView , Iterator , Mapping
28
28
from typing import Any , ClassVar , Literal
29
29
30
30
from typing_extensions import Self
@@ -115,11 +115,11 @@ class Composition(collections.abc.Hashable, collections.abc.Mapping, MSONable, S
115
115
# Tolerance in distinguishing different composition amounts.
116
116
# 1e-8 is fairly tight, but should cut out most floating point arithmetic
117
117
# errors.
118
- amount_tolerance = 1e-8
119
- charge_balanced_tolerance = 1e-8
118
+ amount_tolerance : ClassVar [ float ] = 1e-8
119
+ charge_balanced_tolerance : ClassVar [ float ] = 1e-8
120
120
121
121
# Special formula handling for peroxides and certain elements. This is so
122
- # that formula output does not write LiO instead of Li2O2 for example.
122
+ # that formula output does not write " LiO" instead of " Li2O2" for example.
123
123
special_formulas : ClassVar [dict [str , str ]] = {
124
124
"LiO" : "Li2O2" ,
125
125
"NaO" : "Na2O2" ,
@@ -134,7 +134,8 @@ class Composition(collections.abc.Hashable, collections.abc.Mapping, MSONable, S
134
134
"H" : "H2" ,
135
135
}
136
136
137
- oxi_prob = None # prior probability of oxidation used by oxi_state_guesses
137
+ # Prior probability of oxidation used by oxi_state_guesses
138
+ oxi_prob : ClassVar [dict | None ] = None
138
139
139
140
def __init__ (self , * args , strict : bool = False , ** kwargs ) -> None :
140
141
"""Very flexible Composition construction, similar to the built-in Python
@@ -417,35 +418,38 @@ def get_reduced_composition_and_factor(self) -> tuple[Self, float]:
417
418
"""Calculate a reduced composition and factor.
418
419
419
420
Returns:
420
- A normalized composition and a multiplicative factor, i.e. ,
421
- Li4Fe4P4O16 returns (Composition("LiFePO4"), 4).
421
+ tuple[Composition, float]: Normalized Composition and multiplicative factor,
422
+ i.e. " Li4Fe4P4O16" returns (Composition("LiFePO4"), 4).
422
423
"""
423
- factor = self .get_reduced_formula_and_factor ()[1 ]
424
+ factor : float = self .get_reduced_formula_and_factor ()[1 ]
424
425
return self / factor , factor
425
426
426
427
def get_reduced_formula_and_factor (self , iupac_ordering : bool = False ) -> tuple [str , float ]:
427
428
"""Calculate a reduced formula and factor.
428
429
429
430
Args:
430
431
iupac_ordering (bool, optional): Whether to order the
431
- formula by the iupac "electronegativity" series, defined in
432
+ formula by the IUPAC "electronegativity" series, defined in
432
433
Table VI of "Nomenclature of Inorganic Chemistry (IUPAC
433
434
Recommendations 2005)". This ordering effectively follows
434
- the groups and rows of the periodic table, except the
435
+ the groups and rows of the periodic table, except for
435
436
Lanthanides, Actinides and hydrogen. Note that polyanions
436
437
will still be determined based on the true electronegativity of
437
438
the elements.
438
439
439
440
Returns:
440
- A pretty normalized formula and a multiplicative factor, i.e. ,
441
- Li4Fe4P4O16 returns (LiFePO4, 4).
441
+ tuple[str, float]: Normalized formula and multiplicative factor,
442
+ i.e., " Li4Fe4P4O16" returns (LiFePO4, 4).
442
443
"""
443
- all_int = all (abs (val - round (val )) < type (self ).amount_tolerance for val in self .values ())
444
+ all_int : bool = all (abs (val - round (val )) < type (self ).amount_tolerance for val in self .values ())
444
445
if not all_int :
445
446
return self .formula .replace (" " , "" ), 1
446
- el_amt_dict = {key : round (val ) for key , val in self .get_el_amt_dict ().items ()}
447
+
448
+ el_amt_dict : dict [str , int ] = {key : round (val ) for key , val in self .get_el_amt_dict ().items ()}
449
+ factor : float
447
450
formula , factor = reduce_formula (el_amt_dict , iupac_ordering = iupac_ordering )
448
451
452
+ # Do not "completely reduce" certain formulas
449
453
if formula in type (self ).special_formulas :
450
454
formula = type (self ).special_formulas [formula ]
451
455
factor /= 2
@@ -458,10 +462,10 @@ def get_integer_formula_and_factor(
458
462
"""Calculate an integer formula and factor.
459
463
460
464
Args:
461
- max_denominator (int): all amounts in the el:amt dict are
462
- first converted to a Fraction with this maximum denominator
465
+ max_denominator (int): all amounts in the el_amt dict are
466
+ first converted to a Fraction with this maximum denominator.
463
467
iupac_ordering (bool, optional): Whether to order the
464
- formula by the iupac "electronegativity" series, defined in
468
+ formula by the IUPAC "electronegativity" series, defined in
465
469
Table VI of "Nomenclature of Inorganic Chemistry (IUPAC
466
470
Recommendations 2005)". This ordering effectively follows
467
471
the groups and rows of the periodic table, except the
@@ -470,13 +474,14 @@ def get_integer_formula_and_factor(
470
474
the elements.
471
475
472
476
Returns:
473
- A pretty normalized formula and a multiplicative factor, i.e.,
474
- Li0.5O0.25 returns (Li2O, 0.25). O0.25 returns (O2, 0.125)
477
+ A normalized formula and a multiplicative factor, i.e.,
478
+ " Li0.5O0.25" returns (Li2O, 0.25). " O0.25" returns (O2, 0.125)
475
479
"""
476
- el_amt = self .get_el_amt_dict ()
477
- _gcd = gcd_float (list (el_amt .values ()), 1 / max_denominator )
480
+ el_amt : dict [ str , float ] = self .get_el_amt_dict ()
481
+ _gcd : float = gcd_float (list (el_amt .values ()), 1 / max_denominator )
478
482
479
- dct = {key : round (val / _gcd ) for key , val in el_amt .items ()}
483
+ dct : dict [str , int ] = {key : round (val / _gcd ) for key , val in el_amt .items ()}
484
+ factor : float
480
485
formula , factor = reduce_formula (dct , iupac_ordering = iupac_ordering )
481
486
if formula in type (self ).special_formulas :
482
487
formula = type (self ).special_formulas [formula ]
@@ -485,8 +490,8 @@ def get_integer_formula_and_factor(
485
490
486
491
@property
487
492
def reduced_formula (self ) -> str :
488
- """A pretty normalized formula, i.e., LiFePO4 instead of
489
- Li4Fe4P4O16.
493
+ """A normalized formula, i.e., " LiFePO4" instead of
494
+ " Li4Fe4P4O16" .
490
495
"""
491
496
return self .get_reduced_formula_and_factor ()[0 ]
492
497
@@ -1055,7 +1060,7 @@ def _get_oxi_state_guesses(
1055
1060
raise ValueError (f"Composition { comp } cannot accommodate max_sites setting!" )
1056
1061
1057
1062
# Load prior probabilities of oxidation states, used to rank solutions
1058
- if not type (self ).oxi_prob :
1063
+ if type (self ).oxi_prob is None :
1059
1064
all_data = loadfn (f"{ MODULE_DIR } /../analysis/icsd_bv.yaml" )
1060
1065
type(self ).oxi_prob = {Species .from_str (sp ): data for sp , data in all_data ["occurrence" ].items ()}
1061
1066
oxi_states_override = oxi_states_override or {}
@@ -1319,38 +1324,38 @@ def _parse_chomp_and_rank(match, formula: str, m_dict: dict[str, float], m_point
1319
1324
1320
1325
1321
1326
def reduce_formula (
1322
- sym_amt : dict [str , float ] | dict [ str , int ],
1327
+ sym_amt : Mapping [str , float ],
1323
1328
iupac_ordering : bool = False ,
1324
- ) -> tuple [str , float ]:
1325
- """Helper function to reduce a sym_amt dict to a reduced formula and factor .
1329
+ ) -> tuple [str , int ]:
1330
+ """Helper function to reduce a symbol-amount mapping .
1326
1331
1327
1332
Args:
1328
- sym_amt (dict): {symbol: amount} .
1333
+ sym_amt (dict[str, float] ): Symbol to amount mapping .
1329
1334
iupac_ordering (bool, optional): Whether to order the
1330
- formula by the iupac "electronegativity" series, defined in
1335
+ formula by the IUPAC "electronegativity" series, defined in
1331
1336
Table VI of "Nomenclature of Inorganic Chemistry (IUPAC
1332
1337
Recommendations 2005)". This ordering effectively follows
1333
- the groups and rows of the periodic table, except the
1338
+ the groups and rows of the periodic table, except for
1334
1339
Lanthanides, Actinides and hydrogen. Note that polyanions
1335
1340
will still be determined based on the true electronegativity of
1336
1341
the elements.
1337
1342
1338
1343
Returns:
1339
- tuple[str, float ]: reduced formula and factor.
1344
+ tuple[str, int ]: reduced formula and factor.
1340
1345
"""
1341
- syms = sorted (sym_amt , key = lambda x : [get_el_sp (x ).X , x ])
1346
+ syms : list [ str ] = sorted (sym_amt , key = lambda x : [get_el_sp (x ).X , x ])
1342
1347
1343
1348
syms = list (filter (lambda x : abs (sym_amt [x ]) > Composition .amount_tolerance , syms ))
1344
1349
1345
- factor = 1
1346
- # Enforce integers for doing gcd.
1350
+ # Enforce integer for calculating greatest common divisor
1351
+ factor : int = 1
1347
1352
if all (int (i ) == i for i in sym_amt .values ()):
1348
1353
factor = abs (gcd (* (int (i ) for i in sym_amt .values ())))
1349
1354
1350
- poly_anions = []
1351
- # if the composition contains a poly anion
1355
+ # If the composition contains polyanion
1356
+ poly_anions : list [ str ] = []
1352
1357
if len (syms ) >= 3 and get_el_sp (syms [- 1 ]).X - get_el_sp (syms [- 2 ]).X < 1.65 :
1353
- poly_sym_amt = {syms [i ]: sym_amt [syms [i ]] / factor for i in [ - 2 , - 1 ] }
1358
+ poly_sym_amt : dict [ str , float ] = {syms [i ]: sym_amt [syms [i ]] / factor for i in ( - 2 , - 1 ) }
1354
1359
poly_form , poly_factor = reduce_formula (poly_sym_amt , iupac_ordering = iupac_ordering )
1355
1360
1356
1361
if poly_factor != 1 :
@@ -1369,9 +1374,17 @@ def reduce_formula(
1369
1374
return "" .join ([* reduced_form , * poly_anions ]), factor
1370
1375
1371
1376
1377
+ class CompositionError (Exception ):
1378
+ """Composition exceptions."""
1379
+
1380
+
1372
1381
class ChemicalPotential (dict , MSONable ):
1373
- """Represent set of chemical potentials. Can be: multiplied/divided by a Number
1374
- multiplied by a Composition (returns an energy) added/subtracted with other ChemicalPotentials.
1382
+ """Represent set of chemical potentials.
1383
+
1384
+ Can be:
1385
+ - multiplied/divided by a Number
1386
+ - multiplied by a Composition (returns an energy)
1387
+ - added/subtracted with other ChemicalPotentials
1375
1388
"""
1376
1389
1377
1390
def __init__ (self , * args , ** kwargs ) -> None :
@@ -1424,7 +1437,3 @@ def get_energy(self, composition: Composition, strict: bool = True) -> float:
1424
1437
if strict and (missing := set (composition ) - set (self )):
1425
1438
raise ValueError (f"Potentials not specified for { missing } " )
1426
1439
return sum (self .get (key , 0 ) * val for key , val in composition .items ())
1427
-
1428
-
1429
- class CompositionError (Exception ):
1430
- """Exception class for composition errors."""
0 commit comments