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