1# coding: utf-8
2# Copyright (c) Pymatgen Development Team.
3# Distributed under the terms of the MIT License.
4
5"""
6This module is used to estimate the Herfindahl-Hirschman Index, or HHI, of
7chemical compounds. The HHI is a measure of how geographically confined or
8dispersed the elements comprising a compound are. A low HHI is desirable
9because it means the component elements are geographically dispersed.
10
11Data/strategy from "Data-Driven Review of Thermoelectric Materials:
12Performance and Resource Considerations" by Gaultois et al., published
13in Chemistry of Materials (2013).
14"""
15
16import os
17
18from monty.design_patterns import singleton
19
20from pymatgen.core.composition import Composition
21from pymatgen.core.periodic_table import Element
22
23__author__ = "Anubhav Jain"
24__copyright__ = "Copyright 2014, The Materials Project"
25__version__ = "0.1"
26__maintainer__ = "Anubhav Jain"
27__email__ = "ajain@lbl.gov"
28__date__ = "Oct 27, 2014"
29
30
31csv_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "hhi_data.csv")
32
33
34@singleton
35class HHIModel:
36    """
37    HHI calculator.
38    """
39
40    def __init__(self):
41        """
42        Init for HHIModel.
43        """
44        self.symbol_hhip_hhir = {}  # symbol->(HHI_production, HHI reserve)
45
46        with open(csv_path) as f:
47            for line in f:
48                if line[0] != "#":
49                    symbol, hhi_production, hhi_reserve = line.split(",")
50                    self.symbol_hhip_hhir[symbol] = (
51                        float(hhi_production),
52                        float(hhi_reserve),
53                    )
54
55    def _get_hhi_el(self, el_or_symbol):
56        """
57        Returns the tuple of HHI_production, HHI reserve for a single element only
58        """
59        if isinstance(el_or_symbol, Element):
60            el_or_symbol = el_or_symbol.symbol
61
62        return (
63            self.symbol_hhip_hhir[el_or_symbol][0],
64            self.symbol_hhip_hhir[el_or_symbol][1],
65        )
66
67    def get_hhi(self, comp_or_form):
68        """
69        Gets the reserve and production HHI for a compound.
70
71        Args:
72            comp_or_form (Composition or String): A Composition or String formula
73
74        Returns:
75            A tuple representing the (HHI_production, HHI_reserve)
76        """
77
78        try:
79            if not isinstance(comp_or_form, Composition):
80                comp_or_form = Composition(comp_or_form)
81
82            hhi_p = 0
83            hhi_r = 0
84
85            for e in comp_or_form.elements:
86                percent = comp_or_form.get_wt_fraction(e)
87                dp, dr = self._get_hhi_el(e)
88                hhi_p += dp * percent
89                hhi_r += dr * percent
90            return hhi_p, hhi_r
91
92        except Exception:
93            return None, None
94
95    def get_hhi_production(self, comp_or_form):
96        """
97        Gets the production HHI for a compound.
98
99        Args:
100            comp_or_form (Composition or String): A Composition or String formula
101
102        Returns:
103            The HHI production value
104        """
105        return self.get_hhi(comp_or_form)[0]
106
107    def get_hhi_reserve(self, comp_or_form):
108        """
109        Gets the reserve HHI for a compound.
110
111        Args:
112            comp_or_form (Composition or String): A Composition or String formula
113
114        Returns:
115            The HHI reserve value
116        """
117        return self.get_hhi(comp_or_form)[1]
118
119    @staticmethod
120    def get_hhi_designation(hhi):
121        """
122        Gets a designation for low, medium, high HHI, as specified in "U.S.
123        Department of Justice and the Federal Trade Commission, Horizontal
124        merger guidelines; 2010."
125
126        Args:
127            hhi (float): HHI value
128
129        Returns:
130            The designation as String
131        """
132
133        if hhi is None:
134            return None
135
136        if 0 <= hhi < 1500:
137            return "low"
138
139        if 1500 <= hhi <= 2500:
140            return "medium"
141
142        return "high"
143