Coverage for /builds/ase/ase/ase/io/jsv.py: 83.18%

107 statements  

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

1# fmt: off 

2 

3""" 

4A module for reading and writing crystal structures from JSV 

5See http://www.jcrystal.com/steffenweber/JAVA/JSV/jsv.html 

6 

7By Jesper Friis, Jan. 2012 

8""" 

9 

10 

11import re 

12 

13import numpy as np 

14 

15import ase 

16from ase.geometry import cell_to_cellpar, cellpar_to_cell 

17from ase.spacegroup import Spacegroup, crystal 

18 

19 

20def read_jsv(f): 

21 """Reads a JSV file.""" 

22 natom = nbond = npoly = 0 

23 symbols = [] 

24 labels = [] 

25 cellpar = basis = title = bonds = poly = origin = shell_numbers = None 

26 spacegroup = 1 

27 

28 headline = f.readline().strip() 

29 

30 while True: 

31 line = f.readline() 

32 if not line: 

33 break 

34 line = line.strip() 

35 m = re.match(r'^\[([^]]+)\]\s*(.*)', line) 

36 if m is None or not line: 

37 continue 

38 tag = m.groups()[0].lower() 

39 

40 if len(m.groups()) > 1: 

41 args = m.groups()[1].split() 

42 else: 

43 args = [] 

44 

45 if tag == 'cell': 

46 cellpar = [float(x) for x in args] 

47 elif tag == 'natom': 

48 natom = int(args[0]) 

49 elif tag == 'nbond': 

50 nbond = int(args[0]) 

51 # optional margin of the bondlengths 

52 elif tag == 'npoly': 

53 npoly = int(args[0]) 

54 elif tag == 'space_group': 

55 spacegroup = Spacegroup(*tuple(int(x) for x in args)) 

56 elif tag == 'title': 

57 title = m.groups()[1] 

58 elif tag == 'atoms': 

59 symbols = [] 

60 basis = np.zeros((natom, 3), dtype=float) 

61 shell_numbers = -np.ones((natom, ), dtype=int) # float? 

62 for i in range(natom): 

63 tokens = f.readline().strip().split() 

64 labels.append(tokens[0]) 

65 symbols.append(ase.data.chemical_symbols[int(tokens[1])]) 

66 basis[i] = [float(x) for x in tokens[2:5]] 

67 if len(tokens) > 5: 

68 shell_numbers[i] = float(tokens[5]) # float? 

69 elif tag == 'bonds': 

70 for _ in range(nbond): 

71 f.readline() 

72 bonds = NotImplemented 

73 elif tag == 'poly': 

74 for _ in range(npoly): 

75 f.readline() 

76 poly = NotImplemented 

77 elif tag == 'origin': 

78 origin = NotImplemented 

79 else: 

80 raise ValueError(f'Unknown tag: "{tag}"') 

81 

82 if headline == 'asymmetric_unit_cell': 

83 atoms = crystal(symbols=symbols, 

84 basis=basis, 

85 spacegroup=spacegroup, 

86 cellpar=cellpar, 

87 ) 

88 elif headline == 'full_unit_cell': 

89 atoms = ase.Atoms(symbols=symbols, 

90 scaled_positions=basis, 

91 cell=cellpar_to_cell(cellpar), 

92 ) 

93 atoms.info['spacegroup'] = Spacegroup(spacegroup) 

94 elif headline == 'cartesian_cell': 

95 atoms = ase.Atoms(symbols=symbols, 

96 positions=basis, 

97 cell=cellpar_to_cell(cellpar), 

98 ) 

99 atoms.info['spacegroup'] = Spacegroup(spacegroup) 

100 else: 

101 raise ValueError(f'Invalid JSV file type: "{headline}"') 

102 

103 atoms.info['title'] = title 

104 atoms.info['labels'] = labels 

105 if bonds is not None: 

106 atoms.info['bonds'] = bonds 

107 if poly is not None: 

108 atoms.info['poly'] = poly 

109 if origin is not None: 

110 atoms.info['origin'] = origin 

111 if shell_numbers is not None: 

112 atoms.info['shell_numbers'] = shell_numbers 

113 

114 return atoms 

115 

116 

117def write_jsv(fd, atoms): 

118 """Writes JSV file.""" 

119 fd.write('asymmetric_unit_cell\n') 

120 

121 fd.write('[cell]') 

122 for v in cell_to_cellpar(atoms.cell): 

123 fd.write(' %g' % v) 

124 fd.write('\n') 

125 

126 fd.write('[natom] %d\n' % len(atoms)) 

127 fd.write('[nbond] 0\n') # FIXME 

128 fd.write('[npoly] 0\n') # FIXME 

129 

130 if 'spacegroup' in atoms.info: 

131 sg = Spacegroup(atoms.info['spacegroup']) 

132 fd.write('[space_group] %d %d\n' % (sg.no, sg.setting)) 

133 else: 

134 fd.write('[space_group] 1 1\n') 

135 

136 fd.write('[title] %s\n' % atoms.info.get('title', 'untitled')) 

137 

138 fd.write('\n') 

139 fd.write('[atoms]\n') 

140 if 'labels' in atoms.info: 

141 labels = atoms.info['labels'] 

142 else: 

143 labels = ['%s%d' % (s, i + 1) for i, s in 

144 enumerate(atoms.get_chemical_symbols())] 

145 numbers = atoms.get_atomic_numbers() 

146 scaled = atoms.get_scaled_positions() 

147 for label, n, p in zip(labels, numbers, scaled): 

148 fd.write('%-4s %2d %9.6f %9.6f %9.6f\n' 

149 % (label, n, p[0], p[1], p[2])) 

150 

151 fd.write('Label AtomicNumber x y z (repeat natom times)\n') 

152 

153 fd.write('\n') 

154 fd.write('[bonds]\n') 

155 

156 fd.write('\n') 

157 fd.write('[poly]\n') 

158 

159 fd.write('\n')