26
26
from pymatgen .util .due import Doi , due
27
27
28
28
if TYPE_CHECKING :
29
- from typing import Any
29
+ from collections .abc import Sequence
30
+ from typing import Any , ClassVar
30
31
31
- from numpy .typing import ArrayLike
32
+ from numpy .typing import ArrayLike , NDArray
32
33
33
34
__author__ = "Matthew Horton"
34
35
__copyright__ = "Copyright 2017, The Materials Project"
@@ -88,7 +89,7 @@ def __init__(
88
89
threshold : float = 0 ,
89
90
threshold_nonmag : float = 0.1 ,
90
91
threshold_ordering : float = 1e-8 ,
91
- ):
92
+ ) -> None :
92
93
"""
93
94
If magnetic moments are not defined, moments will be
94
95
taken either from default_magmoms.yaml (similar to the
@@ -148,7 +149,7 @@ def __init__(
148
149
149
150
structure = structure .copy ()
150
151
151
- # check for disorder
152
+ # Check for disorder structure
152
153
if not structure .is_ordered :
153
154
raise NotImplementedError (
154
155
f"{ type (self ).__name__ } not implemented for disordered structures, make ordered approximation first."
@@ -285,8 +286,28 @@ def __init__(
285
286
self .structure = structure
286
287
self .threshold_ordering = threshold_ordering
287
288
289
+ def __str__ (self ) -> str :
290
+ """Sorts a Structure (by fractional coordinate), and
291
+ prints sites with magnetic information. This is
292
+ useful over Structure.__str__ because sites are in
293
+ a consistent order, which makes visual comparison between
294
+ two identical Structures with different magnetic orderings
295
+ easier.
296
+ """
297
+ frac_coords = self .structure .frac_coords
298
+ sorted_indices = np .lexsort ((frac_coords [:, 2 ], frac_coords [:, 1 ], frac_coords [:, 0 ]))
299
+ struct = Structure .from_sites ([self .structure [idx ] for idx in sorted_indices ])
300
+
301
+ # adapted from Structure.__repr__
302
+ outs = ["Structure Summary" , repr (struct .lattice )]
303
+ outs .append ("Magmoms Sites" )
304
+ for site in struct :
305
+ prefix = f"{ site .properties ['magmom' ]:+.2f} " if site .properties ["magmom" ] != 0 else " "
306
+ outs .append (prefix + repr (site ))
307
+ return "\n " .join (outs )
308
+
288
309
@staticmethod
289
- def _round_magmoms (magmoms : ArrayLike , round_magmoms_mode : float ) -> np . ndarray :
310
+ def _round_magmoms (magmoms : ArrayLike , round_magmoms_mode : float ) -> NDArray :
290
311
"""If round_magmoms_mode is an integer, simply round to that number
291
312
of decimal places, else if set to a float will try and round
292
313
intelligently by grouping magmoms.
@@ -334,7 +355,10 @@ def get_structure_with_spin(self) -> Structure:
334
355
335
356
return structure
336
357
337
- def get_structure_with_only_magnetic_atoms (self , make_primitive : bool = True ) -> Structure :
358
+ def get_structure_with_only_magnetic_atoms (
359
+ self ,
360
+ make_primitive : bool = True ,
361
+ ) -> Structure :
338
362
"""Get a Structure with only magnetic atoms present.
339
363
340
364
Args:
@@ -393,12 +417,12 @@ def get_ferromagnetic_structure(self, make_primitive: bool = True) -> Structure:
393
417
394
418
@property
395
419
def is_magnetic (self ) -> bool :
396
- """Convenience property, returns True if any non-zero magmoms present."""
420
+ """True if any non-zero magmoms present."""
397
421
return any (map (abs , self .structure .site_properties ["magmom" ]))
398
422
399
423
@property
400
- def magmoms (self ) -> np . ndarray :
401
- """Convenience property, returns magmoms as a numpy array."""
424
+ def magmoms (self ) -> NDArray :
425
+ """magmoms as a NumPy array."""
402
426
return np .array (self .structure .site_properties ["magmom" ])
403
427
404
428
@property
@@ -448,11 +472,15 @@ def number_of_magnetic_sites(self) -> int:
448
472
"""Number of magnetic sites present in structure."""
449
473
return int (np .sum ([abs (m ) > 0 for m in self .magmoms ]))
450
474
451
- def number_of_unique_magnetic_sites (self , symprec : float = 1e-3 , angle_tolerance : float = 5 ) -> int :
475
+ def number_of_unique_magnetic_sites (
476
+ self ,
477
+ symprec : float = 1e-3 ,
478
+ angle_tolerance : float = 5 ,
479
+ ) -> int :
452
480
"""
453
481
Args:
454
- symprec: same as in SpacegroupAnalyzer (Default value = 1e-3)
455
- angle_tolerance: same as in SpacegroupAnalyzer (Default value = 5).
482
+ symprec (float) : same as in SpacegroupAnalyzer (Default value = 1e-3)
483
+ angle_tolerance (float) : same as in SpacegroupAnalyzer (Default value = 5).
456
484
457
485
Returns:
458
486
int: Number of symmetrically-distinct magnetic sites present in structure.
@@ -509,7 +537,11 @@ def ordering(self) -> Ordering:
509
537
return Ordering .AFM
510
538
return Ordering .NM
511
539
512
- def get_exchange_group_info (self , symprec : float = 1e-2 , angle_tolerance : float = 5 ) -> tuple [str , int ]:
540
+ def get_exchange_group_info (
541
+ self ,
542
+ symprec : float = 1e-2 ,
543
+ angle_tolerance : float = 5 ,
544
+ ) -> tuple [str , int ]:
513
545
"""Get the information on the symmetry of the Hamiltonian
514
546
describing the exchange energy of the system, taking into
515
547
account relative direction of magnetic moments but not their
@@ -521,11 +553,11 @@ def get_exchange_group_info(self, symprec: float = 1e-2, angle_tolerance: float
521
553
orderings within pymatgen.
522
554
523
555
Args:
524
- symprec: same as SpacegroupAnalyzer (Default value = 1e-2)
525
- angle_tolerance: same as SpacegroupAnalyzer (Default value = 5)
556
+ symprec (float) : same as SpacegroupAnalyzer (Default value = 1e-2)
557
+ angle_tolerance (float) : same as SpacegroupAnalyzer (Default value = 5)
526
558
527
559
Returns:
528
- spacegroup_symbol, international_number
560
+ tuple[str, int]: spacegroup_symbol, international_number
529
561
"""
530
562
structure = self .get_structure_with_spin ()
531
563
@@ -562,34 +594,14 @@ def matches_ordering(self, other: Structure) -> bool:
562
594
563
595
return cmag_analyzer .matches (b_positive ) or cmag_analyzer .matches (analyzer )
564
596
565
- def __str__ (self ):
566
- """Sorts a Structure (by fractional coordinate), and
567
- prints sites with magnetic information. This is
568
- useful over Structure.__str__ because sites are in
569
- a consistent order, which makes visual comparison between
570
- two identical Structures with different magnetic orderings
571
- easier.
572
- """
573
- frac_coords = self .structure .frac_coords
574
- sorted_indices = np .lexsort ((frac_coords [:, 2 ], frac_coords [:, 1 ], frac_coords [:, 0 ]))
575
- struct = Structure .from_sites ([self .structure [idx ] for idx in sorted_indices ])
576
-
577
- # adapted from Structure.__repr__
578
- outs = ["Structure Summary" , repr (struct .lattice )]
579
- outs .append ("Magmoms Sites" )
580
- for site in struct :
581
- prefix = f"{ site .properties ['magmom' ]:+.2f} " if site .properties ["magmom" ] != 0 else " "
582
- outs .append (prefix + repr (site ))
583
- return "\n " .join (outs )
584
-
585
597
586
598
class MagneticStructureEnumerator :
587
599
"""Combines MagneticStructureAnalyzer and MagOrderingTransformation to
588
600
automatically generate a set of transformations for a given structure
589
601
and produce a list of plausible magnetic orderings.
590
602
"""
591
603
592
- available_strategies = (
604
+ available_strategies : ClassVar [ tuple [ str , ...]] = (
593
605
"ferromagnetic" ,
594
606
"antiferromagnetic" ,
595
607
"ferrimagnetic_by_motif" ,
@@ -602,14 +614,14 @@ def __init__(
602
614
self ,
603
615
structure : Structure ,
604
616
default_magmoms : dict [str , float ] | None = None ,
605
- strategies : list [str ] | tuple [ str , ... ] = (
617
+ strategies : Sequence [str ] = (
606
618
"ferromagnetic" ,
607
619
"antiferromagnetic" ,
608
620
),
609
621
automatic : bool = True ,
610
622
truncate_by_symmetry : bool = True ,
611
623
transformation_kwargs : dict | None = None ,
612
- ):
624
+ ) -> None :
613
625
"""Generate different collinear magnetic orderings for a given input structure.
614
626
615
627
If the input structure has magnetic moments defined, it
@@ -618,18 +630,18 @@ def __init__(
618
630
(this can be changed using default_magmoms kwarg).
619
631
620
632
Args:
621
- structure: input structure
622
- default_magmoms: (optional, defaults provided) dict of
623
- magnetic elements to their initial magnetic moments in μB, generally
624
- these are chosen to be high-spin since they can relax to a low-spin
625
- configuration during a DFT electronic configuration
626
- strategies: different ordering strategies to use, choose from:
633
+ structure (Structure) : input structure
634
+ default_magmoms (dict[str, float], optional): magnetic elements to their
635
+ initial magnetic moments in μB, generally these are chosen to be
636
+ high-spin since they can relax to a low-spin configuration during
637
+ a DFT electronic configuration
638
+ strategies (Sequence[str]): ordering strategies to use, choose from:
627
639
ferromagnetic, antiferromagnetic, antiferromagnetic_by_motif,
628
640
ferrimagnetic_by_motif and ferrimagnetic_by_species (here, "motif",
629
641
means to use a different ordering parameter for symmetry inequivalent
630
642
sites)
631
- automatic: if True, will automatically choose sensible strategies
632
- truncate_by_symmetry: if True, will remove very unsymmetrical
643
+ automatic (bool) : if True, will automatically choose sensible strategies
644
+ truncate_by_symmetry (bool) : if True, will remove very unsymmetrical
633
645
orderings that are likely physically implausible
634
646
transformation_kwargs: keyword arguments to pass to
635
647
MagOrderingTransformation, to change automatic cell size limits, etc.
@@ -654,10 +666,7 @@ def __init__(
654
666
self .max_unique_sites = 8
655
667
656
668
# kwargs to pass to transformation (ultimately to enumlib)
657
- default_transformation_kwargs = {"check_ordered_symmetry" : False , "timeout" : 5 }
658
- transformation_kwargs = transformation_kwargs or {}
659
- transformation_kwargs .update (default_transformation_kwargs )
660
- self .transformation_kwargs = transformation_kwargs
669
+ self .transformation_kwargs = {"check_ordered_symmetry" : False , "timeout" : 5 } | (transformation_kwargs or {})
661
670
662
671
# our magnetically ordered structures will be
663
672
# stored here once generated and also store which
@@ -729,7 +738,10 @@ def _sanitize_input_structure(struct: Structure) -> Structure:
729
738
730
739
return struct
731
740
732
- def _generate_transformations (self , structure : Structure ) -> dict [str , MagOrderingTransformation ]:
741
+ def _generate_transformations (
742
+ self ,
743
+ structure : Structure ,
744
+ ) -> dict [str , MagOrderingTransformation ]:
733
745
"""The central problem with trying to enumerate magnetic orderings is
734
746
that we have to enumerate orderings that might plausibly be magnetic
735
747
ground states, while not enumerating orderings that are physically
@@ -938,15 +950,13 @@ def _generate_ordered_structures(
938
950
and self.ordered_structures_origins instance variables.
939
951
940
952
Args:
941
- sanitized_input_structure: A sanitized input structure
942
- (_sanitize_input_structure)
953
+ sanitized_input_structure (Structure): A sanitized input structure
943
954
transformations: A dict of transformations (values) and name of
944
- enumeration strategy (key), the enumeration strategy name is just
945
- for record keeping
946
-
955
+ enumeration strategy (key), the enumeration strategy name is just
956
+ for record keeping
947
957
948
958
Returns:
949
- list[Structures ]
959
+ tuple[ list[Structure], list[str] ]
950
960
"""
951
961
ordered_structures = self .ordered_structures
952
962
ordered_structures_origins = self .ordered_structure_origins
@@ -1071,7 +1081,7 @@ def magnetic_deformation(structure_A: Structure, structure_B: Structure) -> Magn
1071
1081
ferromagnetic structures.
1072
1082
1073
1083
Adapted from Bocarsly et al. 2017,
1074
- doi : 10.1021/acs.chemmater.6b04729
1084
+ DOI : 10.1021/acs.chemmater.6b04729
1075
1085
1076
1086
Args:
1077
1087
structure_A: Structure
0 commit comments