1 /******************************************************************************
2 
3   This source file is part of the Avogadro project.
4 
5   Copyright 2012 Kitware, Inc.
6 
7   This source code is released under the New BSD License, (the "License").
8 
9   Unless required by applicable law or agreed to in writing, software
10   distributed under the License is distributed on an "AS IS" BASIS,
11   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   See the License for the specific language governing permissions and
13   limitations under the License.
14 
15 ******************************************************************************/
16 
17 #include "elements.h"
18 
19 #include "avogadrocore.h"
20 #include "elementdata.h"
21 #include "utilities.h"
22 
23 #include <algorithm>
24 #include <cctype>
25 #include <vector>
26 
27 using Avogadro::Core::isCustomElement;
28 
29 namespace Avogadro {
30 namespace Core {
31 
32 // Handle custom element identifiers:
33 namespace {
34 const static std::string CustomElementSymbolPrefix = "X";
35 const static std::string CustomElementNamePrefix = "CustomElement_";
36 
37 static std::vector<std::string> CustomElementSymbols;
38 static std::vector<std::string> CustomElementNames;
39 
40 // Match carbon's radii
41 static double CustomElementCovalentRadius = element_covalent[6];
42 static double CustomElementVDWRadius = element_VDW[6];
43 
encodeCustomElement(unsigned char atomicNumber)44 inline std::string encodeCustomElement(unsigned char atomicNumber)
45 {
46   std::string result;
47   if (isCustomElement(atomicNumber)) {
48     result.resize(2);
49     unsigned char index = atomicNumber - Avogadro::CustomElementMin;
50     result[0] = 'a' + static_cast<char>(index / 26);
51     result[1] = 'a' + static_cast<char>(index % 26);
52   }
53   return result;
54 }
55 
decodeCustomElement(const std::string & str)56 inline unsigned char decodeCustomElement(const std::string& str)
57 {
58   if (str.size() == 2) {
59     if (str[0] >= 'a' && str[0] <= 'z' && str[1] >= 'a' && str[1] <= 'z') {
60       return CustomElementMin + static_cast<unsigned char>(str[0] - 'a') * 26 +
61              static_cast<unsigned char>(str[1] - 'a');
62     }
63   }
64   return Avogadro::InvalidElement;
65 }
66 
interpretCustomElementName(const std::string & name)67 inline unsigned char interpretCustomElementName(const std::string& name)
68 {
69   if (startsWith(name, CustomElementNamePrefix)) {
70     const std::string number(name.substr(CustomElementNamePrefix.size()));
71     return decodeCustomElement(number);
72   }
73   return InvalidElement;
74 }
75 
createCustomElementName(unsigned char atomicNumber)76 inline std::string createCustomElementName(unsigned char atomicNumber)
77 {
78   return CustomElementNamePrefix + encodeCustomElement(atomicNumber);
79 }
80 
customElementName(unsigned char atomicNumber)81 inline const char* customElementName(unsigned char atomicNumber)
82 {
83   return CustomElementNames[atomicNumber - CustomElementMin].c_str();
84 }
85 
interpretCustomElementSymbol(const std::string & symbol)86 inline unsigned char interpretCustomElementSymbol(const std::string& symbol)
87 {
88   if (symbol.size() == 3)
89     return decodeCustomElement(symbol.substr(1));
90   return InvalidElement;
91 }
92 
createCustomElementSymbol(unsigned char atomicNumber)93 inline std::string createCustomElementSymbol(unsigned char atomicNumber)
94 {
95   return CustomElementSymbolPrefix + encodeCustomElement(atomicNumber);
96 }
97 
customElementSymbol(unsigned char atomicNumber)98 inline const char* customElementSymbol(unsigned char atomicNumber)
99 {
100   return CustomElementSymbols[atomicNumber - CustomElementMin].c_str();
101 }
102 
customElementColor(unsigned char atomicNumber)103 inline unsigned char* customElementColor(unsigned char atomicNumber)
104 {
105   return Core::element_color[atomicNumber % element_count];
106 }
107 
108 // Initialize the static lookup tables.
109 class InitializeCustomElementTables
110 {
111 public:
InitializeCustomElementTables()112   InitializeCustomElementTables()
113   {
114     CustomElementSymbols.resize(CustomElementCount);
115     CustomElementNames.resize(CustomElementCount);
116     std::string suffix;
117     for (unsigned char i = CustomElementMin; i <= CustomElementMax; ++i) {
118       suffix = encodeCustomElement(i);
119       CustomElementSymbols[i - CustomElementMin] =
120         CustomElementSymbolPrefix + suffix;
121       CustomElementNames[i - CustomElementMin] =
122         CustomElementNamePrefix + suffix;
123     }
124   }
125 } CustomElementTableInitializer;
126 
127 } // end anon namespace
128 
Elements()129 Elements::Elements()
130 {
131 }
132 
~Elements()133 Elements::~Elements()
134 {
135 }
136 
elementCount()137 unsigned char Elements::elementCount()
138 {
139   return element_count;
140 }
141 
atomicNumberFromName(const std::string & name)142 unsigned char Elements::atomicNumberFromName(const std::string& name)
143 {
144   for (unsigned char i = 0; i < element_count; ++i)
145     if (name == element_names[i])
146       return i;
147 
148   return interpretCustomElementName(name);
149 }
150 
atomicNumberFromSymbol(const std::string & symbol)151 unsigned char Elements::atomicNumberFromSymbol(const std::string& symbol)
152 {
153   if (symbol.length() == 1) {
154     switch (symbol[0]) {
155       case 'H':
156         return 1;
157       case 'B':
158         return 5;
159       case 'C':
160         return 6;
161       case 'N':
162         return 7;
163       case 'O':
164         return 8;
165       case 'F':
166         return 9;
167       case 'P':
168         return 15;
169       case 'S':
170         return 16;
171       case 'K':
172         return 19;
173       case 'V':
174         return 23;
175       case 'Y':
176         return 39;
177       case 'I':
178         return 53;
179       case 'W':
180         return 74;
181       case 'U':
182         return 92;
183       default:
184         return InvalidElement;
185     }
186   } else {
187     for (unsigned char i = 0; i < element_count; ++i)
188       if (symbol == element_symbols[i])
189         return i;
190     return interpretCustomElementSymbol(symbol);
191   }
192 }
193 
guessAtomicNumber(const std::string & inputStr)194 unsigned char Elements::guessAtomicNumber(const std::string& inputStr)
195 {
196   std::string str(trimmed(inputStr));
197   if (str.empty())
198     return InvalidElement;
199 
200   // atomic number?
201   bool ok;
202   int atomicNumberInt = lexicalCast<int>(str, ok);
203   if (ok)
204     return static_cast<unsigned char>(atomicNumberInt);
205 
206   // Format string as text
207   std::transform(str.begin(), str.end(), str.begin(), tolower);
208   str[0] = static_cast<char>(toupper(static_cast<int>(str[0])));
209 
210   int length = str.size();
211   unsigned char atomicNumber;
212   while (length > 0) {
213     if (length > 3)
214       atomicNumber = atomicNumberFromName(str.substr(0, length));
215     else
216       atomicNumber = atomicNumberFromSymbol(str.substr(0, length));
217     if (atomicNumber != InvalidElement)
218       break;
219     length--;
220   }
221 
222   return atomicNumber;
223 }
224 
name(unsigned char atomicNumber)225 const char* Elements::name(unsigned char atomicNumber)
226 {
227   if (atomicNumber < element_count)
228     return element_names[atomicNumber];
229   else if (isCustomElement(atomicNumber))
230     return customElementName(atomicNumber);
231   else
232     return element_names[0];
233 }
234 
symbol(unsigned char atomicNumber)235 const char* Elements::symbol(unsigned char atomicNumber)
236 {
237   if (atomicNumber < element_count)
238     return element_symbols[atomicNumber];
239   else if (isCustomElement(atomicNumber))
240     return customElementSymbol(atomicNumber);
241   else
242     return element_symbols[0];
243 }
244 
mass(unsigned char atomicNumber)245 double Elements::mass(unsigned char atomicNumber)
246 {
247   if (atomicNumber < element_count)
248     return element_masses[atomicNumber];
249   else
250     return element_masses[0];
251 }
252 
radiusVDW(unsigned char atomicNumber)253 double Elements::radiusVDW(unsigned char atomicNumber)
254 {
255   if (atomicNumber < element_count)
256     return element_VDW[atomicNumber];
257   else if (isCustomElement(atomicNumber))
258     return CustomElementVDWRadius;
259   else
260     return element_VDW[0];
261 }
262 
radiusCovalent(unsigned char atomicNumber)263 double Elements::radiusCovalent(unsigned char atomicNumber)
264 {
265   if (atomicNumber < element_count)
266     return element_covalent[atomicNumber];
267   else if (isCustomElement(atomicNumber))
268     return CustomElementCovalentRadius;
269   else
270     return element_covalent[0];
271 }
272 
color(unsigned char atomicNumber)273 const unsigned char* Elements::color(unsigned char atomicNumber)
274 {
275   if (atomicNumber < element_count)
276     return element_color[atomicNumber];
277   else if (isCustomElement(atomicNumber))
278     return customElementColor(atomicNumber);
279   else
280     return element_color[0];
281 }
282 
283 } // end Core namespace
284 } // end Avogadro namespace
285