@@ -579,6 +579,142 @@ def add_atom_names(self, atom_names):
579
579
self .data ['atom_names' ].extend (atom_names )
580
580
self .data ['atom_numbs' ].extend ([0 for _ in atom_names ])
581
581
582
+ def replicate (self , ncopy ):
583
+ """
584
+ Replicate the each frame in the system in 3 dimensions.
585
+ Each frame in the system will become a supercell.
586
+
587
+ Parameters
588
+ ----------
589
+ ncopy :
590
+ list: [4,2,3]
591
+ or tuple: (4,2,3,)
592
+ make `ncopy[0]` copys in x dimensions,
593
+ make `ncopy[1]` copys in y dimensions,
594
+ make `ncopy[2]` copys in z dimensions.
595
+
596
+ Returns
597
+ -------
598
+ tmp : System
599
+ The system after replication.
600
+ """
601
+ if len (ncopy ) != 3 :
602
+ raise RuntimeError ('ncopy must be a list or tuple with 3 int' )
603
+ for ii in ncopy :
604
+ if type (ii ) is not int :
605
+ raise RuntimeError ('ncopy must be a list or tuple must with 3 int' )
606
+
607
+ tmp = System ()
608
+ nframes = self .get_nframes ()
609
+ data = self .data
610
+ tmp .data ['atom_names' ] = list (np .copy (data ['atom_names' ]))
611
+ tmp .data ['atom_numbs' ] = list (np .array (np .copy (data ['atom_numbs' ])) * np .prod (ncopy ))
612
+ tmp .data ['atom_types' ] = np .sort (np .tile (np .copy (data ['atom_types' ]),np .prod (ncopy )))
613
+ tmp .data ['cells' ] = np .copy (data ['cells' ])
614
+ for ii in range (3 ):
615
+ tmp .data ['cells' ][:,ii ,:] *= ncopy [ii ]
616
+ tmp .data ['coords' ] = np .tile (np .copy (data ['coords' ]),tuple (ncopy )+ (1 ,1 ,1 ))
617
+
618
+ for xx in range (ncopy [0 ]):
619
+ for yy in range (ncopy [1 ]):
620
+ for zz in range (ncopy [2 ]):
621
+ tmp .data ['coords' ][xx ,yy ,zz ,:,:,:] += xx * np .reshape (data ['cells' ][:,0 ,:], [- 1 ,1 ,3 ])\
622
+ + yy * np .reshape (data ['cells' ][:,1 ,:], [- 1 ,1 ,3 ])\
623
+ + zz * np .reshape (data ['cells' ][:,2 ,:], [- 1 ,1 ,3 ])
624
+ tmp .data ['coords' ] = np .reshape (np .transpose (tmp .data ['coords' ], [3 ,4 ,0 ,1 ,2 ,5 ]), (nframes , - 1 , 3 ))
625
+ return tmp
626
+
627
+ def perturb (self ,
628
+ pert_num ,
629
+ cell_pert_fraction ,
630
+ atom_pert_distance ,
631
+ atom_pert_style = 'normal' ):
632
+ """
633
+ Perturb each frame in the system randomly.
634
+ The cell will be deformed randomly, and atoms will be displaced by a random distance in random direction.
635
+
636
+ Parameters
637
+ ----------
638
+ pert_num : int
639
+ Each frame in the system will make `pert_num` copies,
640
+ and all the copies will be perturbed.
641
+ That means the system to be returned will contain `pert_num` * frame_num of the input system.
642
+ cell_pert_fraction : float
643
+ A fraction determines how much (relatively) will cell deform.
644
+ The cell of each frame is deformed by a symmetric matrix perturbed from identity.
645
+ The perturbation to the diagonal part is subject to a uniform distribution in [-cell_pert_fraction, cell_pert_fraction),
646
+ and the perturbation to the off-diagonal part is subject to a uniform distribution in [-0.5*cell_pert_fraction, 0.5*cell_pert_fraction).
647
+ atom_pert_distance: float
648
+ unit: Angstrom. A distance determines how far atoms will move.
649
+ Atoms will move about `atom_pert_distance` in random direction.
650
+ The distribution of the distance atoms move is determined by atom_pert_style
651
+ atom_pert_style : str
652
+ Determines the distribution of the distance atoms move is subject to.
653
+ Avaliable options are
654
+ - `'normal'`: the `distance` will be object to `chi-square distribution with 3 degrees of freedom` after normalization.
655
+ The mean value of the distance is `atom_pert_fraction*side_length`
656
+ - `'uniform'`: will generate uniformly random points in a 3D-balls with radius as `atom_pert_distance`.
657
+ These points are treated as vector used by atoms to move.
658
+ Obviously, the max length of the distance atoms move is `atom_pert_distance`.
659
+ - `'const'`: The distance atoms move will be a constant `atom_pert_distance`.
660
+
661
+ Returns
662
+ -------
663
+ perturbed_system : System
664
+ The perturbed_system. It contains `pert_num` * frame_num of the input system frames.
665
+ """
666
+ perturbed_system = System ()
667
+ nframes = self .get_nframes ()
668
+ for ii in range (nframes ):
669
+ for jj in range (pert_num ):
670
+ tmp_system = self [ii ].copy ()
671
+ cell_perturb_matrix = get_cell_perturb_matrix (cell_pert_fraction )
672
+ tmp_system .data ['cells' ][0 ] = np .matmul (tmp_system .data ['cells' ][0 ],cell_perturb_matrix )
673
+ tmp_system .data ['coords' ][0 ] = np .matmul (tmp_system .data ['coords' ][0 ],cell_perturb_matrix )
674
+ for kk in range (len (tmp_system .data ['coords' ][0 ])):
675
+ atom_perturb_vector = get_atom_perturb_vector (atom_pert_distance , atom_pert_style )
676
+ tmp_system .data ['coords' ][0 ][kk ] += atom_perturb_vector
677
+ tmp_system .rot_lower_triangular ()
678
+ perturbed_system .append (tmp_system )
679
+ return perturbed_system
680
+
681
+ def get_cell_perturb_matrix (cell_pert_fraction ):
682
+ if cell_pert_fraction < 0 :
683
+ raise RuntimeError ('cell_pert_fraction can not be negative' )
684
+ e0 = np .random .rand (6 )
685
+ e = e0 * 2 * cell_pert_fraction - cell_pert_fraction
686
+ cell_pert_matrix = np .array (
687
+ [[1 + e [0 ], 0.5 * e [5 ], 0.5 * e [4 ]],
688
+ [0.5 * e [5 ], 1 + e [1 ], 0.5 * e [3 ]],
689
+ [0.5 * e [4 ], 0.5 * e [3 ], 1 + e [2 ]]]
690
+ )
691
+ return cell_pert_matrix
692
+
693
+ def get_atom_perturb_vector (atom_pert_distance , atom_pert_style = 'normal' ):
694
+ random_vector = None
695
+ if atom_pert_distance < 0 :
696
+ raise RuntimeError ('atom_pert_distance can not be negative' )
697
+
698
+ if atom_pert_style == 'normal' :
699
+ e = np .random .randn (3 )
700
+ random_vector = (atom_pert_distance / np .sqrt (3 ))* e
701
+ elif atom_pert_style == 'uniform' :
702
+ e = np .random .randn (3 )
703
+ while np .linalg .norm (e ) < 0.1 :
704
+ e = np .random .randn (3 )
705
+ random_unit_vector = e / np .linalg .norm (e )
706
+ v0 = np .random .rand (1 )
707
+ v = np .power (v0 ,1 / 3 )
708
+ random_vector = atom_pert_distance * v * random_unit_vector
709
+ elif atom_pert_style == 'const' :
710
+ e = np .random .randn (3 )
711
+ while np .linalg .norm (e ) < 0.1 :
712
+ e = np .random .randn (3 )
713
+ random_unit_vector = e / np .linalg .norm (e )
714
+ random_vector = atom_pert_distance * random_unit_vector
715
+ else :
716
+ raise RuntimeError ('unsupported options atom_pert_style={}' .format (atom_pert_style ))
717
+ return random_vector
582
718
583
719
class LabeledSystem (System ):
584
720
'''
0 commit comments