Coverage for /builds/alexhroom/ase/ase/phasediagram.py: 68.21%

368 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2024-08-05 14:37 +0000

1import fractions 

2import functools 

3import re 

4from collections import OrderedDict 

5from typing import Dict, List, Tuple 

6 

7import numpy as np 

8from scipy.spatial import ConvexHull 

9 

10import ase.units as units 

11from ase.formula import Formula 

12 

13_solvated: List[Tuple[str, Dict[str, int], float, bool, float]] = [] 

14 

15 

16def parse_formula(formula): 

17 aq = formula.endswith('(aq)') 

18 if aq: 

19 formula = formula[:-4] 

20 charge = formula.count('+') - formula.count('-') 

21 if charge: 

22 formula = formula.rstrip('+-') 

23 count = Formula(formula).count() 

24 return count, charge, aq 

25 

26 

27def float2str(x): 

28 f = fractions.Fraction(x).limit_denominator(100) 

29 n = f.numerator 

30 d = f.denominator 

31 if abs(n / d - f) > 1e-6: 

32 return f'{f:.3f}' 

33 if d == 0: 

34 return '0' 

35 if f.denominator == 1: 

36 return str(n) 

37 return f'{f.numerator}/{f.denominator}' 

38 

39 

40def solvated(symbols): 

41 """Extract solvation energies from database. 

42 

43 symbols: str 

44 Extract only those molecules that contain the chemical elements 

45 given by the symbols string (plus water and H+). 

46 

47 Data from: 

48 

49 Johnson JW, Oelkers EH, Helgeson HC (1992) 

50 Comput Geosci 18(7):899. 

51 doi:10.1016/0098-3004(92)90029-Q 

52 

53 and: 

54 

55 Pourbaix M (1966) 

56 Atlas of electrochemical equilibria in aqueous solutions. 

57 No. v. 1 in Atlas of Electrochemical Equilibria in Aqueous Solutions. 

58 Pergamon Press, New York. 

59 

60 Returns list of (name, energy) tuples. 

61 """ 

62 

63 if isinstance(symbols, str): 

64 symbols = Formula(symbols).count().keys() 

65 if len(_solvated) == 0: 

66 for line in _aqueous.splitlines(): 

67 energy, formula = line.split(',') 

68 name = formula + '(aq)' 

69 count, charge, aq = parse_formula(name) 

70 energy = float(energy) * 0.001 * units.kcal / units.mol 

71 _solvated.append((name, count, charge, aq, energy)) 

72 references = [] 

73 for name, count, charge, aq, energy in _solvated: 

74 for symbol in count: 

75 if symbol not in 'HO' and symbol not in symbols: 

76 break 

77 else: 

78 references.append((name, energy)) 

79 return references 

80 

81 

82def bisect(A, X, Y, f): 

83 a = [] 

84 for i in [0, -1]: 

85 for j in [0, -1]: 

86 if A[i, j] == -1: 

87 A[i, j] = f(X[i], Y[j]) 

88 a.append(A[i, j]) 

89 

90 if np.ptp(a) == 0: 

91 A[:] = a[0] 

92 return 

93 if a[0] == a[1]: 

94 A[0] = a[0] 

95 if a[1] == a[3]: 

96 A[:, -1] = a[1] 

97 if a[3] == a[2]: 

98 A[-1] = a[3] 

99 if a[2] == a[0]: 

100 A[:, 0] = a[2] 

101 if not (A == -1).any(): 

102 return 

103 i = len(X) // 2 

104 j = len(Y) // 2 

105 bisect(A[:i + 1, :j + 1], X[:i + 1], Y[:j + 1], f) 

106 bisect(A[:i + 1, j:], X[:i + 1], Y[j:], f) 

107 bisect(A[i:, :j + 1], X[i:], Y[:j + 1], f) 

108 bisect(A[i:, j:], X[i:], Y[j:], f) 

109 

110 

111def print_results(results): 

112 total_energy = 0.0 

113 print('reference coefficient energy') 

114 print('------------------------------------') 

115 for name, coef, energy in results: 

116 total_energy += coef * energy 

117 if abs(coef) < 1e-7: 

118 continue 

119 print(f'{name:14}{float2str(coef):>10}{energy:12.3f}') 

120 print('------------------------------------') 

121 print(f'Total energy: {total_energy:22.3f}') 

122 print('------------------------------------') 

123 

124 

125class Pourbaix: 

126 def __init__(self, references, formula=None, T=300.0, **kwargs): 

127 """Pourbaix object. 

128 

129 references: list of (name, energy) tuples 

130 Examples of names: ZnO2, H+(aq), H2O(aq), Zn++(aq), ... 

131 formula: str 

132 Stoichiometry. Example: ``'ZnO'``. Can also be given as 

133 keyword arguments: ``Pourbaix(refs, Zn=1, O=1)``. 

134 T: float 

135 Temperature in Kelvin. 

136 """ 

137 

138 if formula: 

139 assert not kwargs 

140 kwargs = parse_formula(formula)[0] 

141 

142 if 'O' not in kwargs: 

143 kwargs['O'] = 0 

144 if 'H' not in kwargs: 

145 kwargs['H'] = 0 

146 

147 self.kT = units.kB * T 

148 self.references = [] 

149 for name, energy in references: 

150 if name == 'O': 

151 continue 

152 count, charge, aq = parse_formula(name) 

153 if all(symbol in kwargs for symbol in count): 

154 self.references.append((count, charge, aq, energy, name)) 

155 

156 self.references.append(({}, -1, False, 0.0, 'e-')) # an electron 

157 

158 self.count = kwargs 

159 

160 self.N = {'e-': 0} 

161 for symbol in kwargs: 

162 if symbol not in self.N: 

163 self.N[symbol] = len(self.N) 

164 

165 def decompose(self, U, pH, verbose=True, concentration=1e-6): 

166 """Decompose material. 

167 

168 U: float 

169 Potential in V. 

170 pH: float 

171 pH value. 

172 verbose: bool 

173 Default is True. 

174 concentration: float 

175 Concentration of solvated references. 

176 

177 Returns optimal coefficients and energy: 

178 

179 >>> from ase.phasediagram import Pourbaix, solvated 

180 >>> refs = solvated('CoO') + [ 

181 ... ('Co', 0.0), 

182 ... ('CoO', -2.509), 

183 ... ('Co3O4', -9.402)] 

184 >>> pb = Pourbaix(refs, Co=3, O=4) 

185 >>> coefs, energy = pb.decompose(U=1.5, pH=0, 

186 ... concentration=1e-6, 

187 ... verbose=True) 

188 0 HCoO2-(aq) -3.974 

189 1 CoO2--(aq) -3.098 

190 2 H2O(aq) -2.458 

191 3 CoOH+(aq) -2.787 

192 4 CoO(aq) -2.265 

193 5 CoOH++(aq) -1.355 

194 6 Co++(aq) -0.921 

195 7 H+(aq) 0.000 

196 8 Co+++(aq) 1.030 

197 9 Co 0.000 

198 10 CoO -2.509 

199 11 Co3O4 -9.402 

200 12 e- -1.500 

201 reference coefficient energy 

202 ------------------------------------ 

203 H2O(aq) 4 -2.458 

204 Co++(aq) 3 -0.921 

205 H+(aq) -8 0.000 

206 e- -2 -1.500 

207 ------------------------------------ 

208 Total energy: -9.596 

209 ------------------------------------ 

210 """ 

211 

212 alpha = np.log(10) * self.kT 

213 entropy = -np.log(concentration) * self.kT 

214 

215 # We want to minimize np.dot(energies, x) under the constraints: 

216 # 

217 # np.dot(x, eq2) == eq1 

218 # 

219 # with bounds[i,0] <= x[i] <= bounds[i, 1]. 

220 # 

221 # First two equations are charge and number of hydrogens, and 

222 # the rest are the remaining species. 

223 

224 eq1 = [0] + list(self.count.values()) 

225 eq2 = [] 

226 energies = [] 

227 bounds = [] 

228 names = [] 

229 for count, charge, aq, energy, name in self.references: 

230 eq = np.zeros(len(self.N)) 

231 eq[0] = charge 

232 for symbol, n in count.items(): 

233 eq[self.N[symbol]] = n 

234 eq2.append(eq) 

235 if name in ['H2O(aq)', 'H+(aq)', 'e-']: 

236 bounds.append((-np.inf, np.inf)) 

237 if name == 'e-': 

238 energy = -U 

239 elif name == 'H+(aq)': 

240 energy = -pH * alpha 

241 else: 

242 bounds.append((0, np.inf)) 

243 if aq: 

244 energy -= entropy 

245 if verbose: 

246 print('{:<5}{:10}{:10.3f}'.format(len(energies), 

247 name, energy)) 

248 energies.append(energy) 

249 names.append(name) 

250 

251 from scipy.optimize import linprog 

252 

253 result = linprog(c=energies, 

254 A_eq=np.transpose(eq2), 

255 b_eq=eq1, 

256 bounds=bounds) 

257 

258 if verbose: 

259 print_results(zip(names, result.x, energies)) 

260 

261 return result.x, result.fun 

262 

263 def diagram(self, U, pH, plot=True, show=False, ax=None): 

264 """Calculate Pourbaix diagram. 

265 

266 U: list of float 

267 Potentials in V. 

268 pH: list of float 

269 pH values. 

270 plot: bool 

271 Create plot. 

272 show: bool 

273 Open graphical window and show plot. 

274 ax: matplotlib axes object 

275 When creating plot, plot onto the given axes object. 

276 If none given, plot onto the current one. 

277 """ 

278 a = np.empty((len(U), len(pH)), int) 

279 a[:] = -1 

280 colors = {} 

281 f = functools.partial(self.colorfunction, colors=colors) 

282 bisect(a, U, pH, f) 

283 compositions = [None] * len(colors) 

284 names = [ref[-1] for ref in self.references] 

285 for indices, color in colors.items(): 

286 compositions[color] = ' + '.join(names[i] for i in indices 

287 if names[i] not in 

288 ['H2O(aq)', 'H+(aq)', 'e-']) 

289 text = [] 

290 for i, name in enumerate(compositions): 

291 b = (a == i) 

292 x = np.dot(b.sum(1), U) / b.sum() 

293 y = np.dot(b.sum(0), pH) / b.sum() 

294 name = re.sub(r'(\S)([+-]+)', r'\1$^{\2}$', name) 

295 name = re.sub(r'(\d+)', r'$_{\1}$', name) 

296 text.append((x, y, name)) 

297 

298 if plot: 

299 import matplotlib.cm as cm 

300 import matplotlib.pyplot as plt 

301 if ax is None: 

302 ax = plt.gca() 

303 

304 # rasterized pcolormesh has a bug which leaves a tiny 

305 # white border. Unrasterized pcolormesh produces 

306 # unreasonably large files. Avoid this by using the more 

307 # general imshow. 

308 ax.imshow(a, cmap=cm.Accent, 

309 extent=[min(pH), max(pH), min(U), max(U)], 

310 origin='lower', 

311 aspect='auto') 

312 

313 for x, y, name in text: 

314 ax.text(y, x, name, horizontalalignment='center') 

315 ax.set_xlabel('pH') 

316 ax.set_ylabel('potential [V]') 

317 ax.set_xlim(min(pH), max(pH)) 

318 ax.set_ylim(min(U), max(U)) 

319 if show: 

320 plt.show() 

321 

322 return a, compositions, text 

323 

324 def colorfunction(self, U, pH, colors): 

325 coefs, energy = self.decompose(U, pH, verbose=False) 

326 indices = tuple(sorted(np.where(abs(coefs) > 1e-3)[0])) 

327 color = colors.get(indices) 

328 if color is None: 

329 color = len(colors) 

330 colors[indices] = color 

331 return color 

332 

333 

334class PhaseDiagram: 

335 def __init__(self, references, filter='', verbose=True): 

336 """Phase-diagram. 

337 

338 references: list of (name, energy) tuples 

339 List of references. The energy must be the total energy and not 

340 energy per atom. The names can also be dicts like 

341 ``{'Zn': 1, 'O': 2}`` which would be equivalent to ``'ZnO2'``. 

342 filter: str or list of str 

343 Use only those references that match the given filter. 

344 Example: ``filter='ZnO'`` will select those that 

345 contain zinc or oxygen. 

346 verbose: bool 

347 Write information. 

348 """ 

349 

350 if not references: 

351 raise ValueError("You must provide a non-empty list of references" 

352 " for the phase diagram! " 

353 "You have provided '{}'".format(references)) 

354 filter = parse_formula(filter)[0] 

355 

356 self.verbose = verbose 

357 

358 self.species = OrderedDict() 

359 self.references = [] 

360 for name, energy in references: 

361 if isinstance(name, str): 

362 count = parse_formula(name)[0] 

363 else: 

364 count = name 

365 

366 if filter and any(symbol not in filter for symbol in count): 

367 continue 

368 

369 if not isinstance(name, str): 

370 name = Formula.from_dict(count).format('metal') 

371 

372 natoms = 0 

373 for symbol, n in count.items(): 

374 natoms += n 

375 if symbol not in self.species: 

376 self.species[symbol] = len(self.species) 

377 self.references.append((count, energy, name, natoms)) 

378 

379 ns = len(self.species) 

380 self.symbols = [None] * ns 

381 for symbol, id in self.species.items(): 

382 self.symbols[id] = symbol 

383 

384 if verbose: 

385 print('Species:', ', '.join(self.symbols)) 

386 print('References:', len(self.references)) 

387 for i, (count, energy, name, natoms) in enumerate(self.references): 

388 print(f'{i:<5}{name:10}{energy:10.3f}') 

389 

390 self.points = np.zeros((len(self.references), ns + 1)) 

391 for s, (count, energy, name, natoms) in enumerate(self.references): 

392 for symbol, n in count.items(): 

393 self.points[s, self.species[symbol]] = n / natoms 

394 self.points[s, -1] = energy / natoms 

395 

396 if len(self.points) == ns: 

397 # Simple case that qhull would choke on: 

398 self.simplices = np.arange(ns).reshape((1, ns)) 

399 self.hull = np.ones(ns, bool) 

400 elif ns == 1: 

401 # qhull also doesn't like ns=1: 

402 i = self.points[:, 1].argmin() 

403 self.simplices = np.array([[i]]) 

404 self.hull = np.zeros(len(self.points), bool) 

405 self.hull[i] = True 

406 else: 

407 hull = ConvexHull(self.points[:, 1:]) 

408 

409 # Find relevant simplices: 

410 ok = hull.equations[:, -2] < 0 

411 self.simplices = hull.simplices[ok] 

412 

413 # Create a mask for those points that are on the convex hull: 

414 self.hull = np.zeros(len(self.points), bool) 

415 for simplex in self.simplices: 

416 self.hull[simplex] = True 

417 

418 if verbose: 

419 print('Simplices:', len(self.simplices)) 

420 

421 def decompose(self, formula=None, **kwargs): 

422 """Find the combination of the references with the lowest energy. 

423 

424 formula: str 

425 Stoichiometry. Example: ``'ZnO'``. Can also be given as 

426 keyword arguments: ``decompose(Zn=1, O=1)``. 

427 

428 Example:: 

429 

430 pd = PhaseDiagram(...) 

431 pd.decompose(Zn=1, O=3) 

432 

433 Returns energy, indices of references and coefficients.""" 

434 

435 if formula: 

436 assert not kwargs 

437 kwargs = parse_formula(formula)[0] 

438 

439 point = np.zeros(len(self.species)) 

440 N = 0 

441 for symbol, n in kwargs.items(): 

442 point[self.species[symbol]] = n 

443 N += n 

444 

445 # Find coordinates within each simplex: 

446 X = self.points[self.simplices, 1:-1] - point[1:] / N 

447 

448 # Find the simplex with positive coordinates that sum to 

449 # less than one: 

450 eps = 1e-14 

451 candidates = [] 

452 for i, Y in enumerate(X): 

453 try: 

454 x = np.linalg.solve((Y[1:] - Y[:1]).T, -Y[0]) 

455 except np.linalg.linalg.LinAlgError: 

456 continue 

457 if (x > -eps).all() and x.sum() < 1 + eps: 

458 indices = self.simplices[i] 

459 points = self.points[indices] 

460 

461 scaledcoefs = [1 - x.sum()] 

462 scaledcoefs.extend(x) 

463 

464 energy = N * np.dot(scaledcoefs, points[:, -1]) 

465 candidates.append((energy, indices, points, scaledcoefs)) 

466 

467 # Pick the one with lowest energy: 

468 energy, indices, points, scaledcoefs = min( 

469 candidates, key=lambda x: x[0]) 

470 

471 coefs = [] 

472 results = [] 

473 for coef, s in zip(scaledcoefs, indices): 

474 count, e, name, natoms = self.references[s] 

475 coef *= N / natoms 

476 coefs.append(coef) 

477 results.append((name, coef, e)) 

478 

479 if self.verbose: 

480 print_results(results) 

481 

482 return energy, indices, np.array(coefs) 

483 

484 def plot(self, ax=None, dims=None, show=False, **plotkwargs): 

485 """Make 2-d or 3-d plot of datapoints and convex hull. 

486 

487 Default is 2-d for 2- and 3-component diagrams and 3-d for a 

488 4-component diagram. 

489 """ 

490 import matplotlib.pyplot as plt 

491 

492 N = len(self.species) 

493 

494 if dims is None: 

495 if N <= 3: 

496 dims = 2 

497 else: 

498 dims = 3 

499 

500 if ax is None: 

501 projection = None 

502 if dims == 3: 

503 projection = '3d' 

504 from mpl_toolkits.mplot3d import Axes3D 

505 Axes3D # silence pyflakes 

506 fig = plt.figure() 

507 ax = fig.add_subplot(projection=projection) 

508 else: 

509 if dims == 3 and not hasattr(ax, 'set_zlim'): 

510 raise ValueError('Cannot make 3d plot unless axes projection ' 

511 'is 3d') 

512 

513 if dims == 2: 

514 if N == 2: 

515 self.plot2d2(ax, **plotkwargs) 

516 elif N == 3: 

517 self.plot2d3(ax) 

518 else: 

519 raise ValueError('Can only make 2-d plots for 2 and 3 ' 

520 'component systems!') 

521 else: 

522 if N == 3: 

523 self.plot3d3(ax) 

524 elif N == 4: 

525 self.plot3d4(ax) 

526 else: 

527 raise ValueError('Can only make 3-d plots for 3 and 4 ' 

528 'component systems!') 

529 if show: 

530 plt.show() 

531 return ax 

532 

533 def plot2d2(self, ax=None, 

534 only_label_simplices=False, only_plot_simplices=False): 

535 x, e = self.points[:, 1:].T 

536 names = [re.sub(r'(\d+)', r'$_{\1}$', ref[2]) 

537 for ref in self.references] 

538 hull = self.hull 

539 simplices = self.simplices 

540 xlabel = self.symbols[1] 

541 ylabel = 'energy [eV/atom]' 

542 

543 if ax: 

544 for i, j in simplices: 

545 ax.plot(x[[i, j]], e[[i, j]], '-b') 

546 ax.plot(x[hull], e[hull], 'sg') 

547 if not only_plot_simplices: 

548 ax.plot(x[~hull], e[~hull], 'or') 

549 

550 if only_plot_simplices or only_label_simplices: 

551 x = x[self.hull] 

552 e = e[self.hull] 

553 names = [name for name, h in zip(names, self.hull) if h] 

554 for a, b, name in zip(x, e, names): 

555 ax.text(a, b, name, ha='center', va='top') 

556 

557 ax.set_xlabel(xlabel) 

558 ax.set_ylabel(ylabel) 

559 

560 return (x, e, names, hull, simplices, xlabel, ylabel) 

561 

562 def plot2d3(self, ax=None): 

563 x, y = self.points[:, 1:-1].T.copy() 

564 x += y / 2 

565 y *= 3**0.5 / 2 

566 names = [re.sub(r'(\d+)', r'$_{\1}$', ref[2]) 

567 for ref in self.references] 

568 hull = self.hull 

569 simplices = self.simplices 

570 

571 if ax: 

572 for i, j, k in simplices: 

573 ax.plot(x[[i, j, k, i]], y[[i, j, k, i]], '-b') 

574 ax.plot(x[hull], y[hull], 'og') 

575 ax.plot(x[~hull], y[~hull], 'sr') 

576 for a, b, name in zip(x, y, names): 

577 ax.text(a, b, name, ha='center', va='top') 

578 

579 return (x, y, names, hull, simplices) 

580 

581 def plot3d3(self, ax): 

582 x, y, e = self.points[:, 1:].T 

583 

584 ax.scatter(x[self.hull], y[self.hull], e[self.hull], 

585 c='g', marker='o') 

586 ax.scatter(x[~self.hull], y[~self.hull], e[~self.hull], 

587 c='r', marker='s') 

588 

589 for a, b, c, ref in zip(x, y, e, self.references): 

590 name = re.sub(r'(\d+)', r'$_{\1}$', ref[2]) 

591 ax.text(a, b, c, name, ha='center', va='bottom') 

592 

593 for i, j, k in self.simplices: 

594 ax.plot(x[[i, j, k, i]], 

595 y[[i, j, k, i]], 

596 zs=e[[i, j, k, i]], c='b') 

597 

598 ax.set_xlim3d(0, 1) 

599 ax.set_ylim3d(0, 1) 

600 ax.view_init(azim=115, elev=30) 

601 ax.set_xlabel(self.symbols[1]) 

602 ax.set_ylabel(self.symbols[2]) 

603 ax.set_zlabel('energy [eV/atom]') 

604 

605 def plot3d4(self, ax): 

606 x, y, z = self.points[:, 1:-1].T 

607 a = x / 2 + y + z / 2 

608 b = 3**0.5 * (x / 2 + y / 6) 

609 c = (2 / 3)**0.5 * z 

610 

611 ax.scatter(a[self.hull], b[self.hull], c[self.hull], 

612 c='g', marker='o') 

613 ax.scatter(a[~self.hull], b[~self.hull], c[~self.hull], 

614 c='r', marker='s') 

615 

616 for x, y, z, ref in zip(a, b, c, self.references): 

617 name = re.sub(r'(\d+)', r'$_{\1}$', ref[2]) 

618 ax.text(x, y, z, name, ha='center', va='bottom') 

619 

620 for i, j, k, w in self.simplices: 

621 ax.plot(a[[i, j, k, i, w, k, j, w]], 

622 b[[i, j, k, i, w, k, j, w]], 

623 zs=c[[i, j, k, i, w, k, j, w]], c='b') 

624 

625 ax.set_xlim3d(0, 1) 

626 ax.set_ylim3d(0, 1) 

627 ax.set_zlim3d(0, 1) 

628 ax.view_init(azim=115, elev=30) 

629 

630 

631_aqueous = """\ 

632-525700,SiF6-- 

633-514100,Rh(SO4)3---- 

634-504800,Ru(SO4)3---- 

635-499900,Pd(SO4)3---- 

636-495200,Ru(SO4)3--- 

637-485700,H4P2O7 

638-483700,Rh(SO4)3--- 

639-483600,H3P2O7- 

640-480400,H2P2O7-- 

641-480380,Pt(SO4)3---- 

642-471400,HP2O7--- 

643-458700,P2O7---- 

644-447500,LaF4- 

645-437600,LaH2PO4++ 

646-377900,LaF3 

647-376299,Ca(HSiO3)+ 

648-370691,BeF4-- 

649-355400,BF4- 

650-353025,Mg(HSiO3)+ 

651-346900,LaSO4+ 

652-334100,Rh(SO4)2-- 

653-325400,Ru(SO4)2-- 

654-319640,Pd(SO4)2-- 

655-317900,Ru(SO4)2- 

656-312970,Cr2O7-- 

657-312930,CaSO4 

658-307890,NaHSiO3 

659-307800,LaF2+ 

660-307000,LaHCO3++ 

661-306100,Rh(SO4)2- 

662-302532,BeF3- 

663-300670,Pt(SO4)2-- 

664-299900,LaCO3+ 

665-289477,MgSO4 

666-288400,LaCl4- 

667-281500,HZrO3- 

668-279200,HHfO3- 

669-276720,Sr(HCO3)+ 

670-275700,Ba(HCO3)+ 

671-273830,Ca(HCO3)+ 

672-273100,H3PO4 

673-270140,H2PO4- 

674-266500,S2O8-- 

675-264860,Sr(CO3) 

676-264860,SrCO3 

677-263830,Ba(CO3) 

678-263830,BaCO3 

679-262850,Ca(CO3) 

680-262850,CaCO3 

681-260310,HPO4-- 

682-257600,LaCl3 

683-250200,Mg(HCO3)+ 

684-249200,H3VO4 

685-248700,S4O6-- 

686-246640,KSO4- 

687-243990,H2VO4- 

688-243500,PO4--- 

689-243400,KHSO4 

690-242801,HSiO3- 

691-241700,HYO2 

692-241476,NaSO4- 

693-239700,HZrO2+ 

694-239300,LaO2H 

695-238760,Mg(CO3) 

696-238760,MgCO3 

697-237800,HHfO2+ 

698-236890,Ag(CO3)2--- 

699-236800,HNbO3 

700-236600,LaF++ 

701-235640,MnSO4 

702-233400,ZrO2 

703-233000,HVO4-- 

704-231600,HScO2 

705-231540,B(OH)3 

706-231400,HfO2 

707-231386,BeF2 

708-231000,S2O6-- 

709-229000,S3O6-- 

710-229000,S5O6-- 

711-228460,HTiO3- 

712-227400,YO2- 

713-227100,NbO3- 

714-226700,LaCl2+ 

715-223400,HWO4- 

716-221700,LaO2- 

717-218500,WO4-- 

718-218100,ScO2- 

719-214900,VO4--- 

720-210000,YOH++ 

721-208900,LaOH++ 

722-207700,HAlO2 

723-206400,HMoO4- 

724-204800,H3PO3 

725-202350,H2PO3- 

726-202290,SrF+ 

727-201807,BaF+ 

728-201120,BaF+ 

729-200400,MoO4-- 

730-200390,CaF+ 

731-199190,SiO2 

732-198693,AlO2- 

733-198100,YO+ 

734-195900,LaO+ 

735-195800,LaCl++ 

736-194000,CaCl2 

737-194000,HPO3-- 

738-191300,LaNO3++ 

739-190400,ZrOH+++ 

740-189000,HfOH+++ 

741-189000,S2O5-- 

742-187600,ZrO++ 

743-186000,HfO++ 

744-183700,HCrO4- 

745-183600,ScO+ 

746-183100,H3AsO4 

747-180630,HSO4- 

748-180010,H2AsO4- 

749-177930,SO4-- 

750-177690,MgF+ 

751-174800,CrO4-- 

752-173300,SrOH+ 

753-172300,BaOH+ 

754-172200,HBeO2- 

755-171300,CaOH+ 

756-170790,HAsO4-- 

757-166000,ReO4- 

758-165800,SrCl+ 

759-165475,Al(OH)++ 

760-165475,AlOH++ 

761-164730,BaCl+ 

762-164000,La+++ 

763-163800,Y+++ 

764-163100,CaCl+ 

765-162240,BO2- 

766-158493,BeF+ 

767-158188,AlO+ 

768-155700,VOOH+ 

769-155164,CdF2 

770-154970,AsO4--- 

771-153500,Rh(SO4) 

772-152900,BeO2-- 

773-152370,HSO5- 

774-151540,RuCl6--- 

775-149255,MgOH+ 

776-147400,H2S2O4 

777-146900,HS2O4- 

778-146081,CdCl4-- 

779-145521,BeCl2 

780-145200,Ru(SO4) 

781-145056,PbF2 

782-143500,S2O4-- 

783-140330,H2AsO3- 

784-140300,VO2+ 

785-140282,HCO3- 

786-140200,Sc+++ 

787-139900,BeOH+ 

788-139700,MgCl+ 

789-139200,Ru(SO4)+ 

790-139000,Pd(SO4) 

791-138160,HF2- 

792-138100,HCrO2 

793-138000,TiO++ 

794-137300,HGaO2 

795-136450,RbF 

796-134760,Sr++ 

797-134030,Ba++ 

798-133270,Zr++++ 

799-133177,PbCl4-- 

800-132600,Hf++++ 

801-132120,Ca++ 

802-129310,ZnCl3- 

803-128700,GaO2- 

804-128600,BeO 

805-128570,NaF 

806-128000,H2S2O3 

807-127500,Rh(SO4)+ 

808-127200,HS2O3- 

809-126191,CO3-- 

810-126130,HSO3- 

811-125300,CrO2- 

812-125100,H3PO2 

813-124900,S2O3-- 

814-123641,MnF+ 

815-122400,H2PO2- 

816-121000,HMnO2- 

817-120700,RuCl5-- 

818-120400,MnO4-- 

819-120300,Pt(SO4) 

820-119800,HInO2 

821-116300,SO3-- 

822-115971,CdCl3- 

823-115609,Al+++ 

824-115316,BeCl+ 

825-112280,AgCl4--- 

826-111670,TiO2++ 

827-111500,VOH++ 

828-111430,Ag(CO3)- 

829-110720,HZnO2- 

830-108505,Mg++ 

831-108100,HSeO4- 

832-108000,LiOH 

833-107600,MnO4- 

834-106988,HgCl4-- 

835-106700,InO2- 

836-106700,VO++ 

837-106100,VO+ 

838-105500,SeO4-- 

839-105100,RbOH 

840-105000,CsOH 

841-104500,KOH 

842-104109,ZnF+ 

843-103900,PdCl4-- 

844-103579,CuCl4-- 

845-102600,MnO2-- 

846-102150,PbCl3- 

847-101850,H2SeO3 

848-101100,HFeO2 

849-100900,CsCl 

850-100500,CrOH++ 

851-99900,NaOH 

852-99800,VOH+ 

853-99250,LiCl 

854-98340,HSeO3- 

855-98300,ZnCl2 

856-97870,RbCl 

857-97400,HSbO2 

858-97300,HSnO2- 

859-97300,MnOH+ 

860-97016,InF++ 

861-96240,HAsO2 

862-95430,KCl 

863-95400,HFeO2- 

864-94610,CsBr 

865-93290,ZnO2-- 

866-93250,RhCl4-- 

867-92910,NaCl 

868-92800,CrO+ 

869-92250,CO2 

870-91210,PtCl4-- 

871-91157,FeF+ 

872-91100,GaOH++ 

873-91010,RbBr 

874-90550,Be++ 

875-90010,KBr 

876-89963,CuCl3-- 

877-89730,RuCl4- 

878-88400,SeO3-- 

879-88000,FeO2- 

880-87373,CdF+ 

881-86600,GaO+ 

882-86500,HCdO2- 

883-86290,MnCl+ 

884-85610,NaBr 

885-84851,CdCl2 

886-83900,RuCl4-- 

887-83650,AsO2- 

888-83600,Ti+++ 

889-83460,CsI 

890-83400,HCoO2- 

891-82710,AgCl3-- 

892-82400,SbO2- 

893-81980,HNiO2- 

894-81732,CoF+ 

895-81500,MnO 

896-81190,ZnOH+ 

897-81000,HPbO2- 

898-79768,NiF+ 

899-79645,FeF++ 

900-79300,HBiO2 

901-78900,RbI 

902-77740,KI 

903-77700,La++ 

904-77500,RhCl4- 

905-75860,PbF+ 

906-75338,CuCl3- 

907-75216,TlF 

908-75100,Ti++ 

909-74600,InOH++ 

910-74504,HgCl3- 

911-73480,FeCl2 

912-72900,NaI 

913-71980,SO2 

914-71662,HF 

915-71600,RuO4-- 

916-71200,PbCl2 

917-69933,Li+ 

918-69810,PdCl3- 

919-69710,Cs+ 

920-69400,InO+ 

921-67811,AuCl3-- 

922-67800,Rb+ 

923-67510,K+ 

924-67420,ZnO 

925-67340,F- 

926-67300,CdO2-- 

927-66850,ZnCl+ 

928-65850,FeOH+ 

929-65550,TlOH 

930-64200,NiO2-- 

931-63530,RhCl3- 

932-63200,CoO2-- 

933-62591,Na+ 

934-61700,BiO2- 

935-61500,CdOH+ 

936-60100,HCuO2- 

937-59226,InCl++ 

938-58600,SnOH+ 

939-58560,RuCl3 

940-58038,CuCl2- 

941-57900,V+++ 

942-57800,FeOH++ 

943-57760,PtCl3- 

944-57600,HTlO2 

945-56690,H2O 

946-56025,CoOH+ 

947-55100,Mn++ 

948-54380,RuCl3- 

949-53950,PbOH+ 

950-53739,CuF+ 

951-53600,SnO 

952-53100,FeO+ 

953-53030,FeCl+ 

954-52850,NiOH+ 

955-52627,CdCl+ 

956-52000,V++ 

957-51560,AgCl2- 

958-50720,FeO 

959-49459,AgF 

960-49300,Cr+++ 

961-47500,CdO 

962-46190,RhCl3 

963-46142,CuCl2 

964-45200,HHgO2- 

965-45157,CoCl+ 

966-44000,CoO 

967-42838,HgCl2 

968-41600,TlO2- 

969-41200,CuO2-- 

970-40920,NiCl+ 

971-39815,TlCl 

972-39400,Cr++ 

973-39350,PbO 

974-39340,NiO 

975-39050,PbCl+ 

976-38000,Ga+++ 

977-37518,FeCl++ 

978-36781,AuCl2- 

979-35332,AuCl4- 

980-35200,Zn++ 

981-35160,PdCl2 

982-33970,RhCl2 

983-32300,BiOH++ 

984-31700,HIO3 

985-31379,Cl- 

986-30600,IO3- 

987-30410,HCl 

988-30204,HgF+ 

989-30200,CuOH+ 

990-29300,BiO+ 

991-28682,CO 

992-26507,NO3- 

993-26440,RuCl2+ 

994-25590,Br3- 

995-25060,RuCl2 

996-24870,Br- 

997-24730,HNO3 

998-23700,HIO 

999-23400,In+++ 

1000-23280,OCN- 

1001-23000,CoOH++ 

1002-22608,CuCl 

1003-22290,PtCl2 

1004-21900,AgOH 

1005-21870,Fe++ 

1006-20800,CuO 

1007-20300,Mn+++ 

1008-20058,Pb(HS)2 

1009-19700,HBrO 

1010-19100,HClO 

1011-19100,ScOH++ 

1012-18990,NH4+ 

1013-18971,Pb(HS)3- 

1014-18560,Cd++ 

1015-18290,Rh(OH)+ 

1016-17450,AgCl 

1017-16250,CuCl+ 

1018-14780,RhCl2+ 

1019-14000,IO4- 

1020-13130,Pd(OH)+ 

1021-13000,Co++ 

1022-12700,HgOH+ 

1023-12410,I- 

1024-12300,I3- 

1025-12190,Ru(OH)2++ 

1026-12100,HNO2 

1027-11500,PdO 

1028-10900,Ni++ 

1029-10470,Ru(OH)+ 

1030-10450,RuO+ 

1031-9200,IO- 

1032-8900,HgO 

1033-8800,ClO- 

1034-8000,BrO- 

1035-7740,Tl+ 

1036-7738,AgNO3 

1037-7700,NO2- 

1038-7220,RhO 

1039-6673,H2S 

1040-6570,Sn++ 

1041-6383,NH3 

1042-5710,Pb++ 

1043-5500,AgO- 

1044-4500,TlOH++ 

1045-4120,Fe+++ 

1046-3380,RhCl+ 

1047-3200,TlO+ 

1048-3184,AuCl 

1049-2155,HgCl+ 

1050-2040,ClO4- 

1051-1900,ClO3- 

1052-1130,PtO 

1053-820,Rh(OH)++ 

10540,Ag(HS)2- 

10550,H+ 

1056230,RuO 

10571400,HClO2 

10581560,Pt(OH)+ 

10592429,Au(HS)2- 

10602500,PdCl+ 

10612860,HS- 

10623140,RhO+ 

10633215,Xe 

10643554,Kr 

10653890,Ar 

10664100,ClO2- 

10674347,N2 

10684450,BrO3- 

10694565,Ne 

10704658,He 

10715210,RuCl+ 

10727100,RuCl++ 

10738600,H2N2O2 

10749375,TlCl++ 

107510500,HSe- 

107611950,Cu+ 

107715675,Cu++ 

107815700,S5-- 

107916500,S4-- 

108017600,S3-- 

108118200,HN2O2- 

108218330,RhCl++ 

108318380,PtCl+ 

108418427,Ag+ 

108519000,S2-- 

108619500,SeCN- 

108719700,N2H5+ 

108821100,N2H6++ 

108922160,SCN- 

109022880,Bi+++ 

109127700,Rh++ 

109228200,BrO4- 

109328600,HCN 

109432000,Co+++ 

109533200,N2O2-- 

109635900,Ru++ 

109736710,Hg2++ 

109839360,Hg++ 

109941200,CN- 

110041440,Ru+++ 

110142200,Pd++ 

110251300,Tl+++ 

110352450,Rh+++ 

110461600,Pt++ 

110564300,Ag++ 

1106103600,Au+++"""