Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

# coding: utf-8 

# Copyright (c) Pymatgen Development Team. 

# Distributed under the terms of the MIT License. 

 

""" 

This module is used to estimate the cost of various compounds. Costs are taken 

from the a CostDB instance, for example a CSV file via CostDBCSV. 

For compounds with no cost listed, a Phase Diagram style convex hull 

optimization is performed to determine a set of compositions that can be mixed 

to give the desired compound with lowest total cost. 

""" 

 

from __future__ import division, unicode_literals 

import abc 

from collections import defaultdict 

import csv 

import os 

import itertools 

from monty.design_patterns import singleton 

from monty.string import unicode2str 

import six 

 

import scipy.constants as const 

 

from pymatgen import Composition, Element 

from pymatgen.matproj.snl import is_valid_bibtex 

from pymatgen.phasediagram.entries import PDEntry 

from pymatgen.phasediagram.analyzer import PDAnalyzer 

from pymatgen.phasediagram.maker import PhaseDiagram 

from io import open 

 

 

__author__ = 'Anubhav Jain' 

__copyright__ = 'Copyright 2013, The Materials Project' 

__version__ = '0.1' 

__maintainer__ = 'Anubhav Jain' 

__email__ = 'ajain@lbl.gov' 

__date__ = 'Aug 27, 2013' 

 

 

module_dir = os.path.dirname(os.path.abspath(__file__)) 

 

 

class CostEntry(PDEntry): 

""" 

Extends PDEntry to include a BibTeX reference and include language about 

cost 

""" 

 

def __init__(self, composition, cost, name, reference): 

""" 

Args: 

composition: 

Composition as a pymatgen.core.structure.Composition 

cost: 

Cost (per mol, NOT per kg) of the full Composition 

name: 

Optional parameter to name the entry. Defaults to the reduced 

chemical formula as in PDEntry. 

reference: 

Reference data as BiBTeX string 

""" 

super(CostEntry, self).__init__(composition, cost, name) 

if reference and not is_valid_bibtex(reference): 

raise ValueError( 

"Invalid format for cost reference! Should be BibTeX string.") 

self.reference = reference 

 

def __repr__(self): 

return "CostEntry : {} with cost = {:.4f}".format(self.composition, 

self.energy) 

 

 

class CostDB(six.with_metaclass(abc.ABCMeta)): 

""" 

Abstract class for representing a Cost database. 

Can be extended, e.g. for file-based or REST-based databases 

""" 

 

@abc.abstractmethod 

def get_entries(self, chemsys): 

""" 

For a given chemical system, return an array of CostEntries 

 

Args: 

chemsys: 

array of Elements defining the chemical system. 

 

Returns: 

array of CostEntries 

""" 

return 

 

 

class CostDBCSV(CostDB): 

""" 

Read a CSV file to get costs 

Format is formula,cost_per_kg,name,BibTeX 

""" 

 

def __init__(self, filename): 

# read in data from file 

self._chemsys_entries = defaultdict(list) 

filename = os.path.join(os.path.dirname(__file__), filename) 

reader = csv.reader(open(filename, "rt"), quotechar=unicode2str("|")) 

for row in reader: 

comp = Composition(row[0]) 

cost_per_mol = float(row[1]) * comp.weight.to("kg") * const.N_A 

pde = CostEntry(comp.formula, cost_per_mol, row[2], row[3]) 

chemsys = "-".join(sorted([el.symbol 

for el in pde.composition.elements])) 

self._chemsys_entries[chemsys].append(pde) 

 

def get_entries(self, chemsys): 

chemsys = "-".join(sorted([el.symbol for el in chemsys])) 

return self._chemsys_entries[chemsys] 

 

 

@singleton 

class CostDBElements(CostDBCSV): 

""" 

Singleton object that provides the cost data for elements 

""" 

def __init__(self): 

CostDBCSV.__init__( 

self, os.path.join(module_dir, "costdb_elements.csv")) 

 

 

class CostAnalyzer(object): 

""" 

Given a CostDB, figures out the minimum cost solutions via convex hull 

""" 

 

def __init__(self, costdb): 

self.costdb = costdb 

 

def get_lowest_decomposition(self, composition): 

""" 

Get the decomposition leading to lowest cost 

 

Args: 

composition: 

Composition as a pymatgen.core.structure.Composition 

Returns: 

Decomposition as a dict of {Entry: amount} 

""" 

 

entries_list = [] 

elements = [e.symbol for e in composition.elements] 

for i in range(len(elements)): 

for combi in itertools.combinations(elements, i + 1): 

chemsys = [Element(e) for e in combi] 

x = self.costdb.get_entries(chemsys) 

entries_list.extend(x) 

try: 

pd = PhaseDiagram(entries_list) 

return PDAnalyzer(pd).get_decomposition(composition) 

except IndexError: 

raise ValueError("Error during PD building; most likely, " 

"cost data does not exist!") 

 

def get_cost_per_mol(self, comp): 

""" 

Get best estimate of minimum cost/mol based on known data 

 

Args: 

comp: 

Composition as a pymatgen.core.structure.Composition 

Returns: 

float of cost/mol 

""" 

 

comp = comp if isinstance(comp, Composition) else Composition(comp) 

decomp = self.get_lowest_decomposition(comp) 

return sum(k.energy_per_atom * v * comp.num_atoms for k, v in 

decomp.items()) 

 

def get_cost_per_kg(self, comp): 

""" 

Get best estimate of minimum cost/kg based on known data 

 

Args: 

comp: 

Composition as a pymatgen.core.structure.Composition 

Returns: 

float of cost/kg 

""" 

comp = comp if isinstance(comp, Composition) else Composition(comp) 

return self.get_cost_per_mol(comp) / ( 

comp.weight.to("kg") * const.N_A)