Coverage for /builds/ase/ase/ase/calculators/siesta/import_ion_xml.py: 92.63%

95 statements  

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

1# fmt: off 

2 

3import re 

4from xml.dom import minidom 

5 

6import numpy as np 

7 

8 

9def get_ion(fname): 

10 """ 

11 Read the ion.xml file of a specie 

12 Input parameters: 

13 ----------------- 

14 fname (str): name of the ion file 

15 Output Parameters: 

16 ------------------ 

17 ion (dict): The ion dictionary contains all the data 

18 from the ion file. Each field of the xml file give 

19 one key. 

20 The different keys are: 

21 'lmax_basis': int 

22 'self_energy': float 

23 'z': int 

24 'symbol': str 

25 'label': str 

26 'mass': flaot 

27 'lmax_projs': int 

28 'basis_specs': str 

29 'norbs_nl': int 

30 'valence': float 

31 'nprojs_nl: int 

32 

33 The following keys give the pao field, 

34 'npts': list of int 

35 'delta':list of float 

36 'cutoff': list of float 

37 'data':list of np.arrayof shape (npts[i], 2) 

38 'orbital': list of dictionary 

39 'projector': list of dictionary 

40 

41 """ 

42 doc = minidom.parse(fname) 

43 

44 # the elements from the header 

45 elements_headers = [['symbol', str], ['label', str], ['z', int], 

46 ['valence', float], ['mass', float], 

47 ['self_energy', float], ['lmax_basis', int], 

48 ['norbs_nl', int], ['lmax_projs', int], 

49 ['nprojs_nl', int]] 

50 

51 ion = {} 

52 for i, elname in enumerate(elements_headers): 

53 name = doc.getElementsByTagName(elname[0]) 

54 ion[elname[0]] = get_data_elements(name[0], elname[1]) 

55 

56 # extract the basis_specs 

57 name = doc.getElementsByTagName("basis_specs") 

58 ion["basis_specs"] = getNodeText(name[0]) 

59 

60 extract_pao_elements(ion, doc) 

61 return ion 

62 

63 

64def getNodeText(node): 

65 nodelist = node.childNodes 

66 result = [] 

67 for node in nodelist: 

68 if node.nodeType == node.TEXT_NODE: 

69 result.append(node.data) 

70 return ''.join(result) 

71 

72 

73def get_data_elements(name, dtype): 

74 """ 

75 return the right type of the element value 

76 """ 

77 if dtype is int: 

78 data = str2int(getNodeText(name)) 

79 if len(data) > 1: 

80 return np.array(data) 

81 elif len(data) == 1: 

82 return data[0] 

83 else: 

84 raise ValueError("len(data)<1 ??") 

85 elif dtype is float: 

86 data = str2float(getNodeText(name)) 

87 if len(data) > 1: 

88 return np.array(data) 

89 elif len(data) == 1: 

90 return data[0] 

91 else: 

92 raise ValueError("len(data)<1 ??") 

93 elif dtype is str: 

94 return getNodeText(name) 

95 else: 

96 raise ValueError('not implemented') 

97 

98 

99def extract_pao_elements(ion, doc): 

100 """ 

101 extract the different pao element of the xml file 

102 Input Parameters: 

103 ----------------- 

104 ion (dict) 

105 doc (minidom.parse) 

106 Output Parameters: 

107 ------------------ 

108 ion (dict): the following keys are added to the ion dict: 

109 npts 

110 delta 

111 cutoff 

112 data 

113 orbital 

114 projector 

115 """ 

116 

117 name_npts = doc.getElementsByTagName("npts") 

118 name_delta = doc.getElementsByTagName("delta") 

119 name_cutoff = doc.getElementsByTagName("cutoff") 

120 name_data = doc.getElementsByTagName("data") 

121 

122 name_orbital = doc.getElementsByTagName("orbital") 

123 name_projector = doc.getElementsByTagName("projector") 

124 

125 ion["orbital"] = [] 

126 ion["projector"] = [] 

127 for i in range(len(name_orbital)): 

128 ion["orbital"].append(extract_orbital(name_orbital[i])) 

129 for i in range(len(name_projector)): 

130 ion["projector"].append(extract_projector(name_projector[i])) 

131 

132 if len(name_data) != len(name_npts): 

133 raise ValueError("len(name_data) != len(name_npts): {0} != {1}". 

134 format(len(name_data), len(name_npts))) 

135 if len(name_data) != len(name_cutoff): 

136 raise ValueError("len(name_data) != len(name_cutoff): {0} != {1}". 

137 format(len(name_data), len(name_cutoff))) 

138 if len(name_data) != len(name_delta): 

139 raise ValueError("len(name_data) != len(name_delta): {0} != {1}". 

140 format(len(name_data), len(name_delta))) 

141 

142 ion["npts"] = np.zeros((len(name_npts)), dtype=int) 

143 ion["delta"] = np.zeros((len(name_delta)), dtype=float) 

144 ion["cutoff"] = np.zeros((len(name_cutoff)), dtype=float) 

145 ion["data"] = [] 

146 

147 for i in range(len(name_data)): 

148 ion["npts"][i] = get_data_elements(name_npts[i], int) 

149 ion["cutoff"][i] = get_data_elements(name_cutoff[i], float) 

150 ion["delta"][i] = get_data_elements(name_delta[i], float) 

151 ion["data"].append(get_data_elements(name_data[i], float). 

152 reshape(ion["npts"][i], 2)) 

153 

154 

155def extract_orbital(orb_xml): 

156 """ 

157 extract the orbital 

158 """ 

159 orb = {} 

160 orb['l'] = str2int(orb_xml.attributes['l'].value)[0] 

161 orb['n'] = str2int(orb_xml.attributes['n'].value)[0] 

162 orb['z'] = str2int(orb_xml.attributes['z'].value)[0] 

163 orb['ispol'] = str2int(orb_xml.attributes['ispol'].value)[0] 

164 orb['population'] = str2float(orb_xml.attributes['population'].value)[0] 

165 

166 return orb 

167 

168 

169def extract_projector(pro_xml): 

170 """ 

171 extract the projector 

172 """ 

173 pro = {} 

174 pro['l'] = str2int(pro_xml.attributes['l'].value)[0] 

175 pro['n'] = str2int(pro_xml.attributes['n'].value)[0] 

176 pro['ref_energy'] = str2float(pro_xml.attributes['ref_energy'].value)[0] 

177 

178 return pro 

179 

180 

181def str2float(string): 

182 numeric_const_pattern = r""" 

183 [-+]? # optional sign 

184 (?: 

185 (?: \d* \. \d+ ) # .1 .12 .123 etc 9.1 etc 98.1 etc 

186 | 

187 (?: \d+ \.? ) # 1. 12. 123. etc 1 12 123 etc 

188 ) 

189 # followed by optional exponent part if desired 

190 (?: [Ee] [+-]? \d+ ) ? 

191 """ 

192 rx = re.compile(numeric_const_pattern, re.VERBOSE) 

193 

194 nb = rx.findall(string) 

195 for i in enumerate(nb): 

196 nb[i[0]] = float(i[1]) 

197 

198 return np.array(nb) 

199 

200 

201def str2int(string): 

202 numeric_const_pattern = r""" 

203 [-+]? # optional sign 

204 (?: 

205 (?: \d* \. \d+ ) # .1 .12 .123 etc 9.1 etc 98.1 etc 

206 | 

207 (?: \d+ \.? ) # 1. 12. 123. etc 1 12 123 etc 

208 ) 

209 # followed by optional exponent part if desired 

210 (?: [Ee] [+-]? \d+ ) ? 

211 """ 

212 rx = re.compile(numeric_const_pattern, re.VERBOSE) 

213 

214 nb = rx.findall(string) 

215 for i in enumerate(nb): 

216 nb[i[0]] = int(i[1]) 

217 

218 return np.array(nb)