1 /******************************************************************************
2 
3   This source file is part of the Avogadro project.
4 
5   Copyright 2011-2014 Kitware, Inc. and Geoffrey Hutchison
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 "molecule.h"
18 
19 #include "basisset.h"
20 #include "color3f.h"
21 #include "cube.h"
22 #include "elements.h"
23 #include "mesh.h"
24 #include "residue.h"
25 #include "unitcell.h"
26 
27 #include <algorithm>
28 #include <cassert>
29 
30 namespace Avogadro {
31 namespace Core {
32 
Molecule()33 Molecule::Molecule()
34   : m_graphDirty(false), m_basisSet(nullptr), m_unitCell(nullptr)
35 {}
36 
Molecule(const Molecule & other)37 Molecule::Molecule(const Molecule& other)
38   : m_graph(other.m_graph), m_graphDirty(true), m_data(other.m_data),
39     m_customElementMap(other.m_customElementMap),
40     m_atomicNumbers(other.atomicNumbers()), m_positions2d(other.m_positions2d),
41     m_positions3d(other.m_positions3d), m_coordinates3d(other.m_coordinates3d),
42     m_timesteps(other.m_timesteps), m_hybridizations(other.m_hybridizations),
43     m_formalCharges(other.m_formalCharges), m_colors(other.m_colors),
44     m_vibrationFrequencies(other.m_vibrationFrequencies),
45     m_vibrationIntensities(other.m_vibrationIntensities),
46     m_vibrationLx(other.m_vibrationLx), m_bondPairs(other.m_bondPairs),
47     m_bondOrders(other.m_bondOrders), m_selectedAtoms(other.m_selectedAtoms),
48     m_meshes(std::vector<Mesh*>()), m_cubes(std::vector<Cube*>()),
49     m_basisSet(other.m_basisSet ? other.m_basisSet->clone() : nullptr),
50     m_unitCell(other.m_unitCell ? new UnitCell(*other.m_unitCell) : nullptr),
51     m_residues(other.m_residues)
52 {
53   // Copy over any meshes
54   for (Index i = 0; i < other.meshCount(); ++i) {
55     Mesh* m = addMesh();
56     *m = *other.mesh(i);
57   }
58 
59   // Copy over any cubes
60   for (Index i = 0; i < other.cubeCount(); ++i) {
61     Cube* c = addCube();
62     *c = *other.cube(i);
63   }
64 }
65 
Molecule(Molecule && other)66 Molecule::Molecule(Molecule&& other) noexcept
67   : m_graph(std::move(other.m_graph)),
68     m_graphDirty(std::move(other.m_graphDirty)),
69     m_data(std::move(other.m_data)),
70     m_customElementMap(std::move(other.m_customElementMap)),
71     m_atomicNumbers(std::move(other.atomicNumbers())),
72     m_positions2d(std::move(other.m_positions2d)),
73     m_positions3d(std::move(other.m_positions3d)),
74     m_coordinates3d(std::move(other.m_coordinates3d)),
75     m_timesteps(std::move(other.m_timesteps)),
76     m_hybridizations(std::move(other.m_hybridizations)),
77     m_formalCharges(std::move(other.m_formalCharges)),
78     m_colors(std::move(other.m_colors)),
79     m_vibrationFrequencies(std::move(other.m_vibrationFrequencies)),
80     m_vibrationIntensities(std::move(other.m_vibrationIntensities)),
81     m_vibrationLx(std::move(other.m_vibrationLx)),
82     m_bondPairs(std::move(other.m_bondPairs)),
83     m_bondOrders(std::move(other.m_bondOrders)),
84     m_selectedAtoms(std::move(other.m_selectedAtoms)),
85     m_meshes(std::move(other.m_meshes)), m_cubes(std::move(other.m_cubes)),
86     m_residues(std::move(other.m_residues))
87 {
88   m_basisSet = other.m_basisSet;
89   other.m_basisSet = nullptr;
90 
91   m_unitCell = other.m_unitCell;
92   other.m_unitCell = nullptr;
93 }
94 
operator =(const Molecule & other)95 Molecule& Molecule::operator=(const Molecule& other)
96 {
97   if (this != &other) {
98     m_graph = other.m_graph;
99     m_graphDirty = true;
100     m_data = other.m_data;
101     m_customElementMap = other.m_customElementMap;
102     m_atomicNumbers = other.m_atomicNumbers;
103     m_positions2d = other.m_positions2d;
104     m_positions3d = other.m_positions3d;
105     m_coordinates3d = other.m_coordinates3d;
106     m_timesteps = other.m_timesteps;
107     m_hybridizations = other.m_hybridizations;
108     m_formalCharges = other.m_formalCharges;
109     m_colors = other.m_colors,
110     m_vibrationFrequencies = other.m_vibrationFrequencies;
111     m_vibrationIntensities = other.m_vibrationIntensities;
112     m_vibrationLx = other.m_vibrationLx;
113     m_bondPairs = other.m_bondPairs;
114     m_bondOrders = other.m_bondOrders;
115     m_selectedAtoms = other.m_selectedAtoms;
116     m_residues = other.m_residues;
117 
118     clearMeshes();
119 
120     // Copy over any meshes
121     for (Index i = 0; i < other.meshCount(); ++i) {
122       Mesh* m = addMesh();
123       *m = *other.mesh(i);
124     }
125 
126     clearCubes();
127 
128     // Copy over any cubes
129     for (Index i = 0; i < other.cubeCount(); ++i) {
130       Cube* c = addCube();
131       *c = *other.cube(i);
132     }
133 
134     delete m_basisSet;
135     m_basisSet = other.m_basisSet ? other.m_basisSet->clone() : nullptr;
136     delete m_unitCell;
137     m_unitCell = other.m_unitCell ? new UnitCell(*other.m_unitCell) : nullptr;
138   }
139 
140   return *this;
141 }
142 
operator =(Molecule && other)143 Molecule& Molecule::operator=(Molecule&& other) noexcept
144 {
145   if (this != &other) {
146     m_graph = std::move(other.m_graph);
147     m_graphDirty = std::move(other.m_graphDirty);
148     m_data = std::move(other.m_data);
149     m_customElementMap = std::move(other.m_customElementMap);
150     m_atomicNumbers = std::move(other.m_atomicNumbers);
151     m_positions2d = std::move(other.m_positions2d);
152     m_positions3d = std::move(other.m_positions3d);
153     m_coordinates3d = std::move(other.m_coordinates3d);
154     m_timesteps = std::move(other.m_timesteps);
155     m_hybridizations = std::move(other.m_hybridizations);
156     m_formalCharges = std::move(other.m_formalCharges);
157     m_colors = std::move(other.m_colors);
158     m_vibrationFrequencies = std::move(other.m_vibrationFrequencies);
159     m_vibrationIntensities = std::move(other.m_vibrationIntensities);
160     m_vibrationLx = std::move(other.m_vibrationLx);
161     m_bondPairs = std::move(other.m_bondPairs);
162     m_bondOrders = std::move(other.m_bondOrders);
163     m_selectedAtoms = std::move(other.m_selectedAtoms);
164     m_residues = std::move(other.m_residues);
165 
166     clearMeshes();
167     m_meshes = std::move(other.m_meshes);
168 
169     clearCubes();
170     m_cubes = std::move(other.m_cubes);
171 
172     delete m_basisSet;
173     m_basisSet = other.m_basisSet;
174     other.m_basisSet = nullptr;
175 
176     delete m_unitCell;
177     m_unitCell = other.m_unitCell;
178     other.m_unitCell = nullptr;
179   }
180 
181   return *this;
182 }
183 
~Molecule()184 Molecule::~Molecule()
185 {
186   delete m_basisSet;
187   delete m_unitCell;
188   clearMeshes();
189   clearCubes();
190 }
191 
setData(const std::string & name,const Variant & value)192 void Molecule::setData(const std::string& name, const Variant& value)
193 {
194   m_data.setValue(name, value);
195 }
196 
data(const std::string & name) const197 Variant Molecule::data(const std::string& name) const
198 {
199   return m_data.value(name);
200 }
201 
hasData(const std::string & name) const202 bool Molecule::hasData(const std::string& name) const
203 {
204   return m_data.hasValue(name);
205 }
206 
setDataMap(const VariantMap & map)207 void Molecule::setDataMap(const VariantMap& map)
208 {
209   m_data = map;
210 }
211 
dataMap() const212 const VariantMap& Molecule::dataMap() const
213 {
214   return m_data;
215 }
216 
dataMap()217 VariantMap& Molecule::dataMap()
218 {
219   return m_data;
220 }
221 
atomicNumbers()222 Array<unsigned char>& Molecule::atomicNumbers()
223 {
224   return m_atomicNumbers;
225 }
226 
atomicNumbers() const227 const Array<unsigned char>& Molecule::atomicNumbers() const
228 {
229   return m_atomicNumbers;
230 }
231 
hybridizations()232 Array<AtomHybridization>& Molecule::hybridizations()
233 {
234   return m_hybridizations;
235 }
236 
hybridizations() const237 const Array<AtomHybridization>& Molecule::hybridizations() const
238 {
239   return m_hybridizations;
240 }
241 
formalCharges()242 Array<signed char>& Molecule::formalCharges()
243 {
244   return m_formalCharges;
245 }
246 
formalCharges() const247 const Array<signed char>& Molecule::formalCharges() const
248 {
249   return m_formalCharges;
250 }
251 
colors()252 Array<Vector3ub>& Molecule::colors()
253 {
254   return m_colors;
255 }
256 
colors() const257 const Array<Vector3ub>& Molecule::colors() const
258 {
259   return m_colors;
260 }
261 
atomPositions2d()262 Array<Vector2>& Molecule::atomPositions2d()
263 {
264   return m_positions2d;
265 }
266 
atomPositions2d() const267 const Array<Vector2>& Molecule::atomPositions2d() const
268 {
269   return m_positions2d;
270 }
271 
atomPositions3d()272 Array<Vector3>& Molecule::atomPositions3d()
273 {
274   return m_positions3d;
275 }
276 
atomPositions3d() const277 const Array<Vector3>& Molecule::atomPositions3d() const
278 {
279   return m_positions3d;
280 }
281 
bondPairs()282 Array<std::pair<Index, Index>>& Molecule::bondPairs()
283 {
284   return m_bondPairs;
285 }
286 
bondPairs() const287 const Array<std::pair<Index, Index>>& Molecule::bondPairs() const
288 {
289   return m_bondPairs;
290 }
291 
bondOrders()292 Array<unsigned char>& Molecule::bondOrders()
293 {
294   return m_bondOrders;
295 }
296 
bondOrders() const297 const Array<unsigned char>& Molecule::bondOrders() const
298 {
299   return m_bondOrders;
300 }
301 
graph()302 Graph& Molecule::graph()
303 {
304   updateGraph();
305   return m_graph;
306 }
307 
graph() const308 const Graph& Molecule::graph() const
309 {
310   updateGraph();
311   return m_graph;
312 }
313 
customElementMap() const314 const Molecule::CustomElementMap& Molecule::customElementMap() const
315 {
316   return m_customElementMap;
317 }
318 
setCustomElementMap(const Molecule::CustomElementMap & map)319 void Molecule::setCustomElementMap(const Molecule::CustomElementMap& map)
320 {
321   m_customElementMap = map;
322 }
323 
hasCustomElements() const324 bool Molecule::hasCustomElements() const
325 {
326   for (Array<unsigned char>::const_iterator it = m_atomicNumbers.begin(),
327                                             itEnd = m_atomicNumbers.end();
328        it != itEnd; ++it) {
329     if (Core::isCustomElement(*it))
330       return true;
331   }
332   return false;
333 }
334 
addAtom(unsigned char number)335 Molecule::AtomType Molecule::addAtom(unsigned char number)
336 {
337   // Mark the graph as dirty.
338   m_graphDirty = true;
339 
340   // Add the atomic number.
341   m_atomicNumbers.push_back(number);
342 
343   return AtomType(this, static_cast<Index>(m_atomicNumbers.size() - 1));
344 }
345 
removeAtom(Index index)346 bool Molecule::removeAtom(Index index)
347 {
348   if (index >= atomCount())
349     return false;
350 
351   // Before removing the atom we must first remove any bonds to it.
352   Array<BondType> atomBonds = bonds(atom(index));
353   while (atomBonds.size()) {
354     removeBond(atomBonds.back());
355     atomBonds = bonds(atom(index));
356   }
357 
358   Index newSize = static_cast<Index>(m_atomicNumbers.size() - 1);
359   if (index != newSize) {
360     // We need to move the last atom to this position, and update its unique ID.
361     m_atomicNumbers[index] = m_atomicNumbers.back();
362     if (m_positions2d.size() == m_atomicNumbers.size())
363       m_positions2d[index] = m_positions2d.back();
364     if (m_positions3d.size() == m_atomicNumbers.size())
365       m_positions3d[index] = m_positions3d.back();
366     if (m_hybridizations.size() == m_atomicNumbers.size())
367       m_hybridizations[index] = m_hybridizations.back();
368     if (m_formalCharges.size() == m_atomicNumbers.size())
369       m_formalCharges[index] = m_formalCharges.back();
370     if (m_colors.size() == m_atomicNumbers.size())
371       m_colors[index] = m_colors.back();
372 
373     // Find any bonds to the moved atom and update their index.
374     atomBonds = bonds(atom(newSize));
375     for (Array<BondType>::const_iterator it = atomBonds.begin(),
376                                          itEnd = atomBonds.end();
377          it != itEnd; ++it) {
378       std::pair<Index, Index> pair = m_bondPairs[it->index()];
379       if (pair.first == newSize)
380         pair.first = index;
381       else if (pair.second == newSize)
382         pair.second = index;
383       m_bondPairs[it->index()] = pair;
384     }
385   }
386   // Resize the arrays for the smaller molecule.
387   if (m_positions2d.size() == m_atomicNumbers.size())
388     m_positions2d.pop_back();
389   if (m_positions3d.size() == m_atomicNumbers.size())
390     m_positions3d.pop_back();
391   if (m_hybridizations.size() == m_atomicNumbers.size())
392     m_hybridizations.pop_back();
393   if (m_formalCharges.size() == m_atomicNumbers.size())
394     m_formalCharges.pop_back();
395   if (m_colors.size() == m_atomicNumbers.size())
396     m_colors.pop_back();
397   m_atomicNumbers.pop_back();
398 
399   return true;
400 }
401 
removeAtom(const AtomType & atom_)402 bool Molecule::removeAtom(const AtomType& atom_)
403 {
404   return removeAtom(atom_.index());
405 }
406 
clearAtoms()407 void Molecule::clearAtoms()
408 {
409   while (atomCount() != 0)
410     removeAtom(0);
411 }
412 
atom(Index index) const413 Molecule::AtomType Molecule::atom(Index index) const
414 {
415   assert(index < atomCount());
416   return AtomType(const_cast<Molecule*>(this), index);
417 }
418 
atomCount() const419 Index Molecule::atomCount() const
420 {
421   return static_cast<Index>(m_atomicNumbers.size());
422 }
423 
atomCount(unsigned char number) const424 Index Molecule::atomCount(unsigned char number) const
425 {
426   Index count(0);
427   for (Array<unsigned char>::const_iterator it = m_atomicNumbers.begin();
428        it != m_atomicNumbers.end(); ++it) {
429     if (*it == number)
430       ++count;
431   }
432   return count;
433 }
434 
435 namespace {
436 // Make an std::pair where the lower index is always first in the pair. This
437 // offers us the guarantee that any given pair of atoms will always result in
438 // a pair that is the same no matter what the order of the atoms given.
makeBondPair(const Index & a,const Index & b)439 std::pair<Index, Index> makeBondPair(const Index& a, const Index& b)
440 {
441   return a < b ? std::make_pair(a, b) : std::make_pair(b, a);
442 }
443 } // namespace
444 
addBond(Index atom1,Index atom2,unsigned char order)445 Molecule::BondType Molecule::addBond(Index atom1, Index atom2,
446                                      unsigned char order)
447 {
448   assert(atom1 < atomCount());
449   assert(atom2 < atomCount());
450 
451   // check if the bond exists - if not, create it
452   std::pair<Index, Index> pair = makeBondPair(atom1, atom2);
453 
454   Array<std::pair<Index, Index>>::iterator iter =
455     std::find(m_bondPairs.begin(), m_bondPairs.end(), pair);
456 
457   if (iter != m_bondPairs.end()) {
458     // found an existing bond between these atoms
459     Index index = static_cast<Index>(std::distance(m_bondPairs.begin(), iter));
460     if (m_bondOrders[index] != order) {
461       // change the order
462       m_bondOrders[index] = order;
463       m_graphDirty = true;
464     }
465     return BondType(const_cast<Molecule*>(this), index);
466   }
467 
468   m_graphDirty = true;
469   m_bondPairs.push_back(makeBondPair(atom1, atom2));
470   m_bondOrders.push_back(order);
471 
472   return BondType(this, bondCount() - 1);
473 }
474 
addBond(const AtomType & a,const AtomType & b,unsigned char order)475 Molecule::BondType Molecule::addBond(const AtomType& a, const AtomType& b,
476                                      unsigned char order)
477 {
478   assert(a.isValid() && a.molecule() == this);
479   assert(b.isValid() && b.molecule() == this);
480 
481   return addBond(a.index(), b.index(), order);
482 }
483 
removeBond(Index index)484 bool Molecule::removeBond(Index index)
485 {
486   if (index >= bondCount())
487     return false;
488 
489   Index newSize = static_cast<Index>(m_bondOrders.size() - 1);
490   if (index != newSize) {
491     m_bondOrders[index] = m_bondOrders.back();
492     m_bondPairs[index] = m_bondPairs.back();
493   }
494   m_bondOrders.pop_back();
495   m_bondPairs.pop_back();
496   return true;
497 }
498 
removeBond(const BondType & bond_)499 bool Molecule::removeBond(const BondType& bond_)
500 {
501   return removeBond(bond_.index());
502 }
503 
removeBond(Index a,Index b)504 bool Molecule::removeBond(Index a, Index b)
505 {
506   return removeBond(bond(a, b).index());
507 }
508 
removeBond(const AtomType & a,const AtomType & b)509 bool Molecule::removeBond(const AtomType& a, const AtomType& b)
510 {
511   return removeBond(bond(a, b).index());
512 }
513 
clearBonds()514 void Molecule::clearBonds()
515 {
516   while (bondCount())
517     removeBond(0);
518 }
519 
bond(Index index) const520 Molecule::BondType Molecule::bond(Index index) const
521 {
522   assert(index < bondCount());
523 
524   return BondType(const_cast<Molecule*>(this), index);
525 }
526 
bond(const AtomType & a,const AtomType & b) const527 Molecule::BondType Molecule::bond(const AtomType& a, const AtomType& b) const
528 {
529   assert(a.isValid() && a.molecule() == this);
530   assert(b.isValid() && b.molecule() == this);
531 
532   return bond(a.index(), b.index());
533 }
534 
bond(Index atomId1,Index atomId2) const535 Molecule::BondType Molecule::bond(Index atomId1, Index atomId2) const
536 {
537   assert(atomId1 < atomCount());
538   assert(atomId2 < atomCount());
539 
540   std::pair<Index, Index> pair = makeBondPair(atomId1, atomId2);
541 
542   Array<std::pair<Index, Index>>::const_iterator iter =
543     std::find(m_bondPairs.begin(), m_bondPairs.end(), pair);
544 
545   if (iter == m_bondPairs.end())
546     return BondType();
547 
548   Index index = static_cast<Index>(std::distance(m_bondPairs.begin(), iter));
549 
550   return BondType(const_cast<Molecule*>(this), index);
551 }
552 
bonds(const AtomType & a)553 Array<Molecule::BondType> Molecule::bonds(const AtomType& a)
554 {
555   if (!a.isValid())
556     return Array<BondType>();
557 
558   return bonds(a.index());
559 }
560 
bonds(Index a)561 Array<Molecule::BondType> Molecule::bonds(Index a)
562 {
563   Array<BondType> atomBonds;
564   if (a < atomCount()) {
565     for (Index i = 0; i < m_bondPairs.size(); ++i)
566       if (m_bondPairs[i].first == a || m_bondPairs[i].second == a)
567         atomBonds.push_back(BondType(this, i));
568   }
569   return atomBonds;
570 }
571 
bondCount() const572 Index Molecule::bondCount() const
573 {
574   return m_bondPairs.size();
575 }
576 
addMesh()577 Mesh* Molecule::addMesh()
578 {
579   m_meshes.push_back(new Mesh);
580   return m_meshes.back();
581 }
582 
mesh(Index index)583 Mesh* Molecule::mesh(Index index)
584 {
585   if (index < static_cast<Index>(m_meshes.size()))
586     return m_meshes[index];
587   else
588     return nullptr;
589 }
590 
mesh(Index index) const591 const Mesh* Molecule::mesh(Index index) const
592 {
593   if (index < static_cast<Index>(m_meshes.size()))
594     return m_meshes[index];
595   else
596     return nullptr;
597 }
598 
clearMeshes()599 void Molecule::clearMeshes()
600 {
601   while (!m_meshes.empty()) {
602     delete m_meshes.back();
603     m_meshes.pop_back();
604   }
605 }
606 
addCube()607 Cube* Molecule::addCube()
608 {
609   m_cubes.push_back(new Cube);
610   return m_cubes.back();
611 }
612 
cube(Index index)613 Cube* Molecule::cube(Index index)
614 {
615   if (index < static_cast<Index>(m_cubes.size()))
616     return m_cubes[index];
617   else
618     return nullptr;
619 }
620 
cube(Index index) const621 const Cube* Molecule::cube(Index index) const
622 {
623   if (index < static_cast<Index>(m_cubes.size()))
624     return m_cubes[index];
625   else
626     return nullptr;
627 }
628 
clearCubes()629 void Molecule::clearCubes()
630 {
631   while (!m_cubes.empty()) {
632     delete m_cubes.back();
633     m_cubes.pop_back();
634   }
635 }
636 
formula(const std::string & delimiter,int over) const637 std::string Molecule::formula(const std::string& delimiter, int over) const
638 {
639   // Adapted from chemkit:
640   // A map of atomic symbols to their quantity.
641   std::map<unsigned char, size_t> composition;
642   for (Array<unsigned char>::const_iterator it = m_atomicNumbers.begin(),
643                                             itEnd = m_atomicNumbers.end();
644        it != itEnd; ++it) {
645     composition[*it]++;
646   }
647 
648   std::stringstream result;
649   std::map<unsigned char, size_t>::iterator iter;
650 
651   // Carbons first
652   iter = composition.find(6);
653   if (iter != composition.end()) {
654     result << "C";
655     if (iter->second > static_cast<size_t>(over))
656       result << delimiter << iter->second;
657     composition.erase(iter);
658 
659     // If carbon is present, hydrogens are next.
660     iter = composition.find(1);
661     if (iter != composition.end()) {
662       result << delimiter << "H";
663       if (iter->second > static_cast<size_t>(over))
664         result << delimiter << iter->second;
665       composition.erase(iter);
666     }
667   }
668 
669   // The rest:
670   iter = composition.begin();
671   while (iter != composition.end()) {
672     result << delimiter << Elements::symbol(iter->first);
673     if (iter->second > static_cast<size_t>(over))
674       result << delimiter << iter->second;
675     ++iter;
676   }
677 
678   return result.str();
679 }
680 
setUnitCell(UnitCell * uc)681 void Molecule::setUnitCell(UnitCell* uc)
682 {
683   if (uc != m_unitCell) {
684     delete m_unitCell;
685     m_unitCell = uc;
686   }
687 }
688 
mass() const689 double Molecule::mass() const
690 {
691   double m(0.0);
692   for (Index i = 0; i < atomCount(); ++i)
693     m += Elements::mass(atom(i).atomicNumber());
694   return m;
695 }
696 
centerOfGeometry() const697 Vector3 Molecule::centerOfGeometry() const
698 {
699   Vector3 center(0.0, 0.0, 0.0);
700   for (Index i = 0; i < atomCount(); ++i)
701     center += atom(i).position3d();
702   return center / atomCount();
703 }
704 
centerOfMass() const705 Vector3 Molecule::centerOfMass() const
706 {
707   Vector3 center(0.0, 0.0, 0.0);
708   for (Index i = 0; i < atomCount(); ++i) {
709     AtomType curr_atom = atom(i);
710     center += (curr_atom.position3d() * Elements::mass(curr_atom.atomicNumber()));
711   }
712   center /= mass();
713   center /= atomCount();
714   return center;
715 }
716 
radius() const717 double Molecule::radius() const
718 {
719   double radius = 0.0;
720   if (atomCount() > 0) {
721     radius = (centerOfGeometry() - atom(0).position3d()).norm();
722   }
723   return radius;
724 }
725 
bestFitPlane() const726 std::pair<Vector3, Vector3> Molecule::bestFitPlane() const
727 {
728   return bestFitPlane(atomPositions3d());
729 }
730 
bestFitPlane(const Array<Vector3> & pos)731 std::pair<Vector3, Vector3> Molecule::bestFitPlane(const Array<Vector3>& pos)
732 {
733   // copy coordinates to matrix in Eigen format
734   size_t num_atoms = pos.size();
735   assert(num_atoms >= 3);
736   Eigen::Matrix<Vector3::Scalar, Eigen::Dynamic, Eigen::Dynamic> coord(
737     3, num_atoms);
738   for (size_t i = 0; i < num_atoms; ++i) {
739     coord.col(i) = pos[i];
740   }
741 
742   // calculate centroid
743   Vector3 centroid = coord.rowwise().mean();
744 
745   // subtract centroid
746   coord.colwise() -= centroid;
747 
748   // we only need the left-singular matrix
749   auto svd = coord.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV);
750   Vector3 plane_normal = svd.matrixU().rightCols<1>();
751 
752   return std::make_pair(centroid, plane_normal);
753 }
754 
vibrationFrequencies() const755 Array<double> Molecule::vibrationFrequencies() const
756 {
757   return m_vibrationFrequencies;
758 }
759 
setVibrationFrequencies(const Array<double> & freq)760 void Molecule::setVibrationFrequencies(const Array<double>& freq)
761 {
762   m_vibrationFrequencies = freq;
763 }
764 
vibrationIntensities() const765 Array<double> Molecule::vibrationIntensities() const
766 {
767   return m_vibrationIntensities;
768 }
769 
setVibrationIntensities(const Array<double> & intensities)770 void Molecule::setVibrationIntensities(const Array<double>& intensities)
771 {
772   m_vibrationIntensities = intensities;
773 }
774 
vibrationLx(int mode) const775 Array<Vector3> Molecule::vibrationLx(int mode) const
776 {
777   if (mode >= 0 && mode < static_cast<int>(m_vibrationLx.size()))
778     return m_vibrationLx[mode];
779   return Array<Vector3>();
780 }
781 
setVibrationLx(const Array<Array<Vector3>> & lx)782 void Molecule::setVibrationLx(const Array<Array<Vector3>>& lx)
783 {
784   m_vibrationLx = lx;
785 }
786 
787 // bond perception code ported from VTK's vtkSimpleBondPerceiver class
perceiveBondsSimple(const double tolerance,const double min)788 void Molecule::perceiveBondsSimple(const double tolerance, const double min)
789 {
790   // check for coordinates
791   if (m_positions3d.size() != atomCount())
792     return;
793 
794   // cache atomic radii
795   std::vector<double> radii(atomCount());
796   for (size_t i = 0; i < radii.size(); i++) {
797     radii[i] = Elements::radiusCovalent(m_atomicNumbers[i]);
798     if (radii[i] <= 0.0)
799       radii[i] = 2.0;
800   }
801 
802   // check for bonds
803   for (Index i = 0; i < atomCount(); i++) {
804     Vector3 ipos = m_positions3d[i];
805     for (Index j = i + 1; j < atomCount(); j++) {
806       double cutoff = radii[i] + radii[j] + tolerance;
807       Vector3 jpos = m_positions3d[j];
808       Vector3 diff = jpos - ipos;
809 
810       if (std::fabs(diff[0]) > cutoff || std::fabs(diff[1]) > cutoff ||
811           std::fabs(diff[2]) > cutoff ||
812           (m_atomicNumbers[i] == 1 && m_atomicNumbers[j] == 1))
813         continue;
814 
815       // check radius and add bond if needed
816       double cutoffSq = cutoff * cutoff;
817       double diffsq = diff.squaredNorm();
818       if (diffsq < cutoffSq && diffsq > min * min)
819         addBond(atom(i), atom(j), 1);
820     }
821   }
822 }
823 
perceiveBondsFromResidueData()824 void Molecule::perceiveBondsFromResidueData()
825 {
826   for (Index i = 0; i < m_residues.size(); ++i) {
827     m_residues[i].resolveResidueBonds(*this);
828   }
829 }
830 
coordinate3dCount()831 int Molecule::coordinate3dCount()
832 {
833   return static_cast<int>(m_coordinates3d.size());
834 }
835 
setCoordinate3d(int coord)836 bool Molecule::setCoordinate3d(int coord)
837 {
838   if (coord >= 0 && coord < static_cast<int>(m_coordinates3d.size())) {
839     m_positions3d = m_coordinates3d[coord];
840     return true;
841   }
842   return false;
843 }
844 
coordinate3d(int index) const845 Array<Vector3> Molecule::coordinate3d(int index) const
846 {
847   return m_coordinates3d[index];
848 }
849 
setCoordinate3d(const Array<Vector3> & coords,int index)850 bool Molecule::setCoordinate3d(const Array<Vector3>& coords, int index)
851 {
852   if (static_cast<int>(m_coordinates3d.size()) <= index)
853     m_coordinates3d.resize(index + 1);
854   m_coordinates3d[index] = coords;
855   return true;
856 }
857 
timeStep(int index,bool & status)858 double Molecule::timeStep(int index, bool& status)
859 {
860   if (static_cast<int>(m_timesteps.size()) <= index) {
861     status = false;
862     return 0.0;
863   }
864   status = true;
865   return m_timesteps[index];
866 }
867 
setTimeStep(double timestep,int index)868 bool Molecule::setTimeStep(double timestep, int index)
869 {
870   if (static_cast<int>(m_timesteps.size()) <= index)
871     m_timesteps.resize(index + 1);
872   m_timesteps[index] = timestep;
873   return true;
874 }
875 
updateGraph() const876 void Molecule::updateGraph() const
877 {
878   if (!m_graphDirty)
879     return;
880   m_graphDirty = false;
881   m_graph.clear();
882   m_graph.setSize(atomCount());
883   typedef Array<std::pair<Index, Index>>::const_iterator IterType;
884   for (IterType it = m_bondPairs.begin(); it != m_bondPairs.end(); ++it) {
885     m_graph.addEdge(it->first, it->second);
886   }
887 }
888 
forceVectors()889 Array<Vector3>& Molecule::forceVectors()
890 {
891   return m_forceVectors;
892 }
893 
forceVectors() const894 const Array<Vector3>& Molecule::forceVectors() const
895 {
896   return m_forceVectors;
897 }
898 
addResidue(std::string & name,Index & number,char & id)899 Residue& Molecule::addResidue(std::string& name, Index& number, char& id)
900 {
901   Residue newResidue(name, number, id);
902   m_residues.push_back(newResidue);
903   return m_residues[m_residues.size() - 1];
904 }
905 
addResidue(Residue & residue)906 void Molecule::addResidue(Residue& residue)
907 {
908   m_residues.push_back(residue);
909 }
910 
residue(int index)911 Residue& Molecule::residue(int index)
912 {
913   return m_residues[index];
914 }
915 
916 } // namespace Core
917 } // namespace Avogadro
918