Coverage for /builds/kinetik161/ase/ase/calculators/kim/calculators.py: 23.26%

86 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-12-10 11:04 +0000

1import os 

2import re 

3 

4from ase.calculators.lammps import convert 

5from ase.calculators.lammpslib import LAMMPSlib 

6from ase.calculators.lammpsrun import LAMMPS 

7from ase.data import atomic_masses, atomic_numbers 

8 

9from .exceptions import KIMCalculatorError 

10from .kimmodel import KIMModelCalculator 

11 

12 

13def KIMCalculator(model_name, options, debug): 

14 """ 

15 Used only for Portable Models 

16 """ 

17 

18 options_not_allowed = ["modelname", "debug"] 

19 

20 _check_conflict_options(options, options_not_allowed, 

21 simulator="kimmodel") 

22 

23 return KIMModelCalculator(model_name, debug=debug, **options) 

24 

25 

26def LAMMPSRunCalculator( 

27 model_name, model_type, supported_species, options, debug, **kwargs 

28): 

29 """Used for Portable Models or LAMMPS Simulator Models if 

30 specifically requested""" 

31 

32 def get_params(model_name, supported_units, supported_species, atom_style): 

33 """ 

34 Extract parameters for LAMMPS calculator from model definition lines. 

35 Returns a dictionary with entries for "pair_style" and "pair_coeff". 

36 Expects there to be only one "pair_style" line. There can be multiple 

37 "pair_coeff" lines (result is returned as a list). 

38 """ 

39 parameters = {} 

40 

41 # In case the SM supplied its own atom_style in its model-init 

42 # -- only needed because lammpsrun writes data files and needs 

43 # to know the proper format 

44 if atom_style: 

45 parameters["atom_style"] = atom_style 

46 

47 # Set units to prevent them from defaulting to metal 

48 parameters["units"] = supported_units 

49 

50 parameters["model_init"] = [ 

51 f"kim_init {model_name} {supported_units}{os.linesep}" 

52 ] 

53 

54 parameters["kim_interactions"] = "kim_interactions {}{}".format( 

55 (" ").join(supported_species), os.linesep 

56 ) 

57 

58 # For every species in "supported_species", add an entry to the 

59 # "masses" key in dictionary "parameters". 

60 parameters["masses"] = [] 

61 for i, species in enumerate(supported_species): 

62 if species not in atomic_numbers: 

63 raise KIMCalculatorError( 

64 "Could not determine mass of unknown species " 

65 "{} listed as supported by model".format(species) 

66 ) 

67 massstr = str( 

68 convert( 

69 atomic_masses[atomic_numbers[species]], 

70 "mass", 

71 "ASE", 

72 supported_units, 

73 ) 

74 ) 

75 parameters["masses"].append(str(i + 1) + " " + massstr) 

76 

77 return parameters 

78 

79 options_not_allowed = ["parameters", "files", "specorder", 

80 "keep_tmp_files"] 

81 

82 _check_conflict_options(options, options_not_allowed, 

83 simulator="lammpsrun") 

84 

85 # If no atom_style kwarg is passed, lammpsrun will default to 

86 # atom_style atomic, which is what we want for KIM Portable Models 

87 atom_style = kwargs.get("atom_style", None) 

88 

89 # Simulator Models will supply their own units from their 

90 # metadata. For Portable Models, we use "metal" units. 

91 supported_units = kwargs.get("supported_units", "metal") 

92 

93 # Set up kim_init and kim_interactions lines 

94 parameters = get_params( 

95 model_name, 

96 supported_units, 

97 supported_species, 

98 atom_style) 

99 

100 return LAMMPS( 

101 **parameters, specorder=supported_species, keep_tmp_files=debug, 

102 **options 

103 ) 

104 

105 

106def LAMMPSLibCalculator(model_name, supported_species, 

107 supported_units, options): 

108 """ 

109 Only used for LAMMPS Simulator Models 

110 """ 

111 options_not_allowed = [ 

112 "lammps_header", 

113 "lmpcmds", 

114 "atom_types", 

115 "log_file", 

116 "keep_alive", 

117 ] 

118 

119 _check_conflict_options(options, options_not_allowed, 

120 simulator="lammpslib") 

121 # Set up LAMMPS header commands lookup table 

122 

123 # This units command actually has no effect, but is necessary because 

124 # LAMMPSlib looks in the header lines for units in order to set them 

125 # internally 

126 model_init = ["units " + supported_units + os.linesep] 

127 

128 model_init.append( 

129 f"kim_init {model_name} {supported_units}{os.linesep}" 

130 ) 

131 model_init.append("atom_modify map array sort 0 0" + os.linesep) 

132 

133 # Assign atom types to species 

134 atom_types = {} 

135 for i_s, s in enumerate(supported_species): 

136 atom_types[s] = i_s + 1 

137 

138 kim_interactions = [ 

139 "kim_interactions {}".format( 

140 (" ").join(supported_species))] 

141 

142 # Return LAMMPSlib calculator 

143 return LAMMPSlib( 

144 lammps_header=model_init, 

145 lammps_name=None, 

146 lmpcmds=kim_interactions, 

147 post_changebox_cmds=kim_interactions, 

148 atom_types=atom_types, 

149 log_file="lammps.log", 

150 keep_alive=True, 

151 **options 

152 ) 

153 

154 

155def ASAPCalculator(model_name, model_type, options, **kwargs): 

156 """ 

157 Can be used with either Portable Models or Simulator Models 

158 """ 

159 import asap3 

160 

161 options_not_allowed = {"pm": ["name", "verbose"], "sm": ["Params"]} 

162 

163 _check_conflict_options( 

164 options, 

165 options_not_allowed[model_type], 

166 simulator="asap") 

167 

168 if model_type == "pm": 

169 

170 return asap3.OpenKIMcalculator( 

171 name=model_name, verbose=kwargs["verbose"], **options 

172 ) 

173 

174 elif model_type == "sm": 

175 model_defn = kwargs["model_defn"] 

176 supported_units = kwargs["supported_units"] 

177 

178 # Verify units (ASAP models are expected to work with "ase" units) 

179 if supported_units != "ase": 

180 raise KIMCalculatorError( 

181 'KIM Simulator Model units are "{}", but expected to ' 

182 'be "ase" for ASAP.'.format(supported_units) 

183 ) 

184 

185 # Check model_defn to make sure there's only one element in it 

186 # that is a non-empty string 

187 if len(model_defn) == 0: 

188 raise KIMCalculatorError( 

189 "model-defn is an empty list in metadata file of " 

190 "Simulator Model {}".format(model_name) 

191 ) 

192 elif len(model_defn) > 1: 

193 raise KIMCalculatorError( 

194 "model-defn should contain only one entry for an ASAP " 

195 "model (found {} lines)".format(len(model_defn)) 

196 ) 

197 

198 if "" in model_defn: 

199 raise KIMCalculatorError( 

200 "model-defn contains an empty string in metadata " 

201 "file of Simulator " 

202 "Model {}".format(model_name) 

203 ) 

204 

205 model_defn = model_defn[0].strip() 

206 

207 # Instantiate calculator from ASAP. Currently, this must be one of: 

208 # (1) EMT 

209 # (2) EMT(EMTRasmussenParameters) 

210 # (3) EMT(EMTMetalGlassParameters) 

211 model_defn_is_valid = False 

212 if model_defn.startswith("EMT"): 

213 # Pull out potential parameters 

214 mobj = re.search(r"\(([A-Za-z0-9_\(\)]+)\)", model_defn) 

215 if mobj is None: 

216 asap_calc = asap3.EMT() 

217 else: 

218 pp = mobj.group(1) 

219 

220 # Currently we only supported two specific EMT models 

221 # that are built into ASAP 

222 if pp.startswith("EMTRasmussenParameters"): 

223 asap_calc = asap3.EMT( 

224 parameters=asap3.EMTRasmussenParameters()) 

225 model_defn_is_valid = True 

226 elif pp.startswith("EMTMetalGlassParameters"): 

227 asap_calc = asap3.EMT( 

228 parameters=asap3.EMTMetalGlassParameters()) 

229 model_defn_is_valid = True 

230 

231 if not model_defn_is_valid: 

232 raise KIMCalculatorError( 

233 'Unknown model "{}" requested for simulator asap.'.format( 

234 model_defn) 

235 ) 

236 

237 # Disable undocumented feature for the EMT self.calculators to 

238 # take the energy of an isolated atoms as zero. (Otherwise it 

239 # is taken to be that of perfect FCC.) 

240 asap_calc.set_subtractE0(False) 

241 

242 return asap_calc 

243 

244 

245def _check_conflict_options(options, options_not_allowed, simulator): 

246 """Check whether options intended to be passed to a given calculator 

247 are allowed. Some options are not allowed because they must be 

248 set internally in this package.""" 

249 s1 = set(options) 

250 s2 = set(options_not_allowed) 

251 common = s1.intersection(s2) 

252 

253 if common: 

254 options_in_not_allowed = ", ".join([f'"{s}"' for s in common]) 

255 

256 msg = ( 

257 'Simulator "{}" does not support argument(s): ' 

258 '{} provided in "options", ' 

259 "because it is (they are) determined internally within the KIM " 

260 "calculator".format(simulator, options_in_not_allowed) 

261 ) 

262 

263 raise KIMCalculatorError(msg)