Coverage for /builds/ase/ase/ase/cli/dimensionality.py: 100.00%

37 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2025-08-02 00:12 +0000

1# fmt: off 

2 

3# Note: 

4# Try to avoid module level import statements here to reduce 

5# import time during CLI execution 

6 

7 

8class CLICommand: 

9 """Analyze the dimensionality of the bonded clusters in a structure, using 

10 the scoring parameter described in: 

11 

12 "Definition of a scoring parameter to identify low-dimensional materials 

13 components", P.M. Larsen, M. Pandey, M. Strange, and K. W. Jacobsen 

14 Phys. Rev. Materials 3 034003, 2019, 

15 https://doi.org/10.1103/PhysRevMaterials.3.034003 

16 https://arxiv.org/abs/1808.02114 

17 

18 A score in the range [0-1] is assigned to each possible dimensionality 

19 classification. The scores sum to 1. A bonded cluster can be a molecular 

20 (0D), chain (1D), layer (2D), or bulk (3D) cluster. Mixed dimensionalities, 

21 such as 0D+3D are possible. Input files may use any format supported by 

22 ASE. 

23 

24 Example usage: 

25 

26 * ase dimensionality --display-all structure.cif 

27 * ase dimensionality structure1.cif structure2.cif 

28 

29 For each structure the following data is printed: 

30 

31 * type - the dimensionalities present 

32 * score - the score of the classification 

33 * a - the start of the k-interval (see paper) 

34 * b - the end of the k-interval (see paper) 

35 * component counts - the number of clusters with each dimensionality type 

36 

37 If the `--display-all` option is used, all dimensionality classifications 

38 are displayed. 

39 """ 

40 

41 @staticmethod 

42 def add_arguments(parser): 

43 add = parser.add_argument 

44 add('filenames', nargs='+', help='input file(s) to analyze') 

45 add('--display-all', dest='full', action='store_true', 

46 help='display all dimensionality classifications') 

47 add('--no-merge', dest='no_merge', action='store_true', 

48 help='do not merge k-intervals with same dimensionality') 

49 

50 @staticmethod 

51 def run(args, parser): 

52 import os 

53 import warnings 

54 

55 from ase.geometry.dimensionality import analyze_dimensionality 

56 from ase.io import iread 

57 

58 files = [os.path.split(path)[1] for path in args.filenames] 

59 lmax = max(len(f) for f in files) + 2 

60 

61 print('file'.ljust(lmax) + 

62 'type score a b component counts') 

63 print('=' * lmax + '===============================================') 

64 

65 merge = not args.no_merge 

66 

67 # reading CIF files can produce a ton of distracting warnings 

68 with warnings.catch_warnings(): 

69 warnings.filterwarnings('ignore') 

70 for path, f in zip(args.filenames, files): 

71 for atoms in iread(path): 

72 result = analyze_dimensionality(atoms, merge=merge) 

73 if not args.full: 

74 result = result[:1] 

75 

76 for i, entry in enumerate(result): 

77 dimtype = entry.dimtype.rjust(4) 

78 score = f'{entry.score:.3f}'.ljust(5) 

79 a = f'{entry.a:.3f}'.ljust(5) 

80 b = f'{entry.b:.3f}'.ljust(5) 

81 if i == 0: 

82 name = f.ljust(lmax) 

83 else: 

84 name = ' ' * lmax 

85 

86 line = ('{}{}' + ' {}' * 4).format(name, dimtype, 

87 score, a, b, 

88 entry.h) 

89 print(line) 

90 

91 if args.full: 

92 print()