1 /* Copyright (c) 2015  Gerald Knizia
2  *
3  * This file is part of the IboView program (see: http://www.iboview.org)
4  *
5  * IboView is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3.
8  *
9  * IboView is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with bfint (LICENSE). If not, see http://www.gnu.org/licenses/
16  *
17  * Please see IboView documentation in README.txt for:
18  * -- A list of included external software and their licenses. The included
19  *    external software's copyright is not touched by this agreement.
20  * -- Notes on re-distribution and contributions to/further development of
21  *    the IboView software
22  */
23 
24 #include "Iv.h"
25 #include <boost/format.hpp>
26 #include <iostream>
27 #include <QTextStream>
28 #include <QFileInfo>
29 #include <cmath>
30 #include <algorithm>
31 
32 #include <QBrush>
33 #include <QFont>
34 #include <QHeaderView>
35 #include <QSize>
36 #include <QColor>
37 
38 #include "CtIo.h"
39 #include "CtMatrix.h"
40 #include "CxColor.h"
41 #include "CtConstants.h"
42 #include "CxOpenMpProxy.h"
43 
44 #include "IvDocument.h"
45 #include "IvFindOrbitalsForm.h"
46 #include "IvShowTextForm.h"
47 #include "IvScript.h"
48 #include "IvIrc.h"
49 #include "IvOrbitalFile.h"
50 #include "CtBasisLibrary.h"
51 #include "IvTables.h"
52 
53 #include "CtInt1e.h"
54 #include "IrAmrr.h"
55 #include "CtMatrix.h" // for ArgSort1
56 #include "CtRhf.h"
57 using namespace ct;
58 
FDataSet(QString const & Desc_,FAtomSetPtr pAtoms_,FDocument * pParent)59 FDataSet::FDataSet(QString const &Desc_, FAtomSetPtr pAtoms_, FDocument *pParent)
60    : pAtoms(pAtoms_), m_Desc(Desc_), m_pDocument(pParent)
61 {
62    Active = false;
63 }
64 
GetDesc(uint) const65 QString FDataSet::GetDesc(uint) const { return m_Desc; };
66 
67 
InvalidateRenderCache()68 void FDataSet::InvalidateRenderCache()
69 {
70 }
71 
BuildRenderCache(FView3d *)72 void FDataSet::BuildRenderCache(FView3d */*pView3d*/)
73 {
74 }
75 
76 
FGeometry(QString const & Desc_,FAtomSetPtr pAtoms_,FDocument * pDocument_)77 FGeometry::FGeometry(QString const &Desc_, FAtomSetPtr pAtoms_, FDocument *pDocument_)
78    : FDataSet(Desc_, pAtoms_, pDocument_)
79 {
80    FindBondLines(pDocument_);
81    FixMinBasis();
82 }
83 
FixMinBasis()84 void FGeometry::FixMinBasis()
85 {
86    for (uint iAt = 0; iAt < pAtoms->size(); ++ iAt) {
87       ct::FAtom
88          &At = (*pAtoms)[iAt];
89       if (ct::g_BasisSetLibrary.HaveBasis(At.AtomicNumber, "MINAO"))
90          At.BasisDesc[BASIS_Guess] = "MINAO";
91       else if (ct::g_BasisSetLibrary.HaveBasis(At.AtomicNumber, "MINAO-PP"))
92          At.BasisDesc[BASIS_Guess] = "MINAO-PP";
93       else {
94 //          std::cerr << boost::format("WARNING: Failed to find a minimal basis for atom %i (%s);") % iAt % ElementNameFromNumber(At.AtomicNumber) << std::endl;
95          IV_NOTIFY2(NOTIFY_Warning, "Failed to find a minimal basis for atom %1 (%2)", iAt, s2q(At.ElementName()));
96          At.BasisDesc[BASIS_Guess] = "MINAO";
97       }
98    }
99 }
100 
101 
AddBond(FBondLine const & bl)102 void FGeometry::AddBond(FBondLine const &bl)
103 {
104    if (bl.iAt >= (int)pAtoms->size() || bl.jAt >= (int)pAtoms->size()) {
105 //       std::cout << boost::format("!Error: Ignoring bond %3i--%3i. Have only %i atoms!") % (1+bl.iAt) % (1+bl.jAt) % pAtoms->size() << std::endl;
106       IV_NOTIFY3(NOTIFY_Warning, "Ignoring bond %1--%2. Have only %3 atoms!", 1+bl.iAt, 1+bl.jAt, (int)pAtoms->size());
107       return;
108    }
109    DeleteBond(bl.iAt+1, bl.jAt+1, false);
110    m_BondLines.push_back(bl);
111 }
112 
DeleteBond(int iAt,int jAt,bool ComplainIfNotThere)113 void FGeometry::DeleteBond(int iAt, int jAt, bool ComplainIfNotThere)
114 {
115    iAt -= 1; // convert 1-based (external) to 0-based (internal)
116    jAt -= 1;
117    bool
118       FoundSomething = false;
119    for (uint iBond = 0; iBond < m_BondLines.size(); ++ iBond) {
120       FBondLine &bl = m_BondLines[iBond];
121       if ( (bl.iAt == iAt && bl.jAt == jAt) ||
122            (bl.iAt == jAt && bl.jAt == iAt) ) {
123          m_BondLines.erase(m_BondLines.begin() + iBond);
124          -- iBond;
125          FoundSomething = true;
126       }
127    };
128    if (ComplainIfNotThere && !FoundSomething)
129 //       std::cout << boost::format("!Error: no bond %3i--%3i present which could be deleted.") % (iAt) % (jAt) << std::endl;
130       IV_NOTIFY(NOTIFY_Warning, QString("No bond %1--%2 present which could be deleted.").arg(iAt).arg(jAt));
131 }
132 
AddBond(int iAt,int jAt,QString const & sFlags)133 void FGeometry::AddBond(int iAt, int jAt, QString const &sFlags)
134 {
135    uint Flags = 0;
136    QStringList FlagList = sFlags.split("|", QString::SkipEmptyParts);
137    foreach(QString const &Flag, FlagList) {
138       if (Flag == "gray" || Flag == "grey")
139          Flags |= BOND_Grey;
140       else if (Flag == "dotted")
141          Flags |= BOND_Partial;
142       else {
143    //       std::cerr << "!WARNING: bond flag '" << q2s(sFlags) << "' not recognized." << std::endl;
144          IV_NOTIFY1(NOTIFY_Warning, "Bond flag '%1' not recognized.", Flag);
145       }
146    }
147    AddBond(FBondLine(iAt-1, jAt-1, Flags));
148 }
149 
150 
151 // extern float UffBondRadii[103];
152 
FindBondLines(FDocument * pDocument)153 void FGeometry::FindBondLines(FDocument *pDocument)
154 {
155    m_BondLines.clear();
156    for ( uint iAt0 = 0; iAt0 < pAtoms->size(); ++ iAt0 ) {
157       FAtom const
158          &At0 = (*pAtoms)[iAt0];
159       for ( uint iAt1 = iAt0+1; iAt1 < pAtoms->size(); ++ iAt1 ) {
160          FAtom const
161             &At1 = (*pAtoms)[iAt1];
162          FElementOptions
163             *pEo0 = pDocument->pElementOptions(iAt0, &*pAtoms),
164             *pEo1 = pDocument->pElementOptions(iAt1, &*pAtoms);
165          double
166             r0 = pEo0->GetCovalentRadius(),
167             r1 = pEo1->GetCovalentRadius(),
168             rij = Dist(At0.vPos, At1.vPos);
169          if ( rij > 0.5 * (pEo0->GetBondRadiusFactor() + pEo1->GetBondRadiusFactor()) * (r0 + r1) )
170             continue;
171 //          double
172 //             r0 = GetCovalentRadius(At0.AtomicNumber),
173 //             r1 = GetCovalentRadius(At1.AtomicNumber),
174 //             rij = Dist(At0.vPos, At1.vPos);
175 //          if ( rij > 1.3 * (r0 + r1) )
176 //             continue;
177          AddBond(FBondLine(iAt0, iAt1, 0));
178       }
179    }
180 }
181 
AtomOptions(int iAt)182 FAtomOptions &FDocument::AtomOptions(int iAt) {
183 //    if (iAt >= (int)pAtoms->size()) {
184 //       std::cout << boost::format("WARNING: SetAtomFlag: Atom %i does not (yet?) exist. Flags anyway stored for future reference.") % iAt << std::endl;
185 //    }
186    return m_AtomOptions[iAt];
187 }
188 
AtomFlags(int iAt)189 uint &FDocument::AtomFlags(int iAt) {
190    return AtomOptions(iAt).Flags;
191 }
192 
IsAtomHidden(int iAt)193 bool FDocument::IsAtomHidden(int iAt)
194 {
195    return 0 != (AtomFlags(iAt) & ATOM_Hidden);
196 }
197 
IsAtomSelected(int iAt)198 bool FDocument::IsAtomSelected(int iAt)
199 {
200    return 0 != (AtomFlags(iAt) & ATOM_Selected);
201 }
202 
IsAtomExcludedFromAlignment(int iAt)203 bool FDocument::IsAtomExcludedFromAlignment(int iAt)
204 {
205    return 0 != (AtomFlags(iAt) & ATOM_NoAlign);
206 }
207 
208 
ClearAtomFlags(bool EmitUpdate)209 void FDocument::ClearAtomFlags(bool EmitUpdate)
210 {
211    m_AtomOptions.clear();
212    m_SelectionSequenceId = 0;
213    if (EmitUpdate)
214       emit SelectionChanged();
215 }
216 
217 struct FSortSelectedAtomsPred
218 {
FSortSelectedAtomsPredFSortSelectedAtomsPred219    explicit FSortSelectedAtomsPred(FDocument *pDocument_) : m_pDocument(pDocument_) {};
220 
operator ()FSortSelectedAtomsPred221    bool operator() (int iAt, int jAt) const {
222       return m_pDocument->AtomOptions(iAt).iSelectionSequenceId < m_pDocument->AtomOptions(jAt).iSelectionSequenceId;
223    }
224 
225    FDocument mutable
226       *m_pDocument;
227 };
228 
GetSelectedAtoms(bool SortedBySelectionSequence)229 FDocument::FAtomIdList FDocument::GetSelectedAtoms(bool SortedBySelectionSequence)
230 {
231    FFrame
232       *pFrame = GetCurrentFrame();
233    if (!pFrame) {
234       IV_NOTIFY(NOTIFY_Error, "No current frame for selecting atoms");
235       return FAtomIdList();
236    }
237    ct::FAtomSet
238       *pAtoms = pFrame->pGetAtoms();
239    if (!pAtoms) {
240       IV_NOTIFY(NOTIFY_Error, "Frame has no associated geometry data");
241       return FAtomIdList();
242    }
243 
244    FAtomIdList
245       iSelectedAtoms;
246    FAtomOptionMap::iterator
247       itAtomOptions;
248    for (itAtomOptions = m_AtomOptions.begin(); itAtomOptions != m_AtomOptions.end(); ++ itAtomOptions)
249       if (bool(itAtomOptions->second.Flags & ATOM_Selected) && itAtomOptions->first >= 0 && (size_t)itAtomOptions->first < pAtoms->size() )
250          iSelectedAtoms.push_back(itAtomOptions->first);
251    if (SortedBySelectionSequence) {
252       FSortSelectedAtomsPred SortBySeqPred(this);
253       std::sort(iSelectedAtoms.begin(), iSelectedAtoms.end(), SortBySeqPred);
254    }
255    return iSelectedAtoms;
256 }
257 
AtomLabel(int iAt) const258 QString FDocument::AtomLabel(int iAt) const
259 {
260    if (!GetCurrentFrame())
261       return "UNKN";
262    FAtomSet const
263       *pAtoms = GetCurrentFrame()->pGetAtoms();
264    if (!pAtoms || iAt >= (int)pAtoms->size())
265       return "ERR";
266    return QString("%1 %2").arg(s2q((*pAtoms)[iAt].GetElementName())).arg(1+iAt);
267 }
268 
269 // QString FDocument::FmtDistance(double fLengthInAu) const
270 // {
271 //    return QString("%1 pm").arg(fLengthInAu * ct::ToAng * 100., 0, 'f', 2);
272 // }
273 
274 
275 
EmitEndl(QTextStream & out)276 void EmitEndl(QTextStream &out) {
277    int fieldWidth = out.fieldWidth();
278    out.setFieldWidth(0);
279    out << "\n";
280    out.setFieldWidth(fieldWidth);
281 }
282 
GetAtomLabel(int iAt)283 QString FDocument::GetAtomLabel(int iAt)
284 {
285    FAtomSet
286       *pAtoms = GetCurrentFrame()->pGetAtoms();
287    QString
288       s;
289    QTextStream
290       out(&s);
291    if (iAt < 0 || (size_t)iAt >= pAtoms->size()) {
292       IV_NOTIFY1(NOTIFY_Error, "No atom %1 exists.", iAt+1);
293       return "???";
294    }
295    FAtom
296       &At = (*pAtoms)[iAt];
297    out.setFieldWidth(2);
298    out << s2q(At.GetElementName()).toUpper();
299    out.setFieldWidth(3);
300    out << (1+iAt);
301    return s;
302 }
303 
in_range_incl(int iMin,int iMax,int i)304 static bool in_range_incl(int iMin, int iMax, int i)
305 {
306    return iMin <= i && i <= iMax;
307 }
308 
309 
310 // "If this element had an ECP, how many electrons would the ECP likely represent?"
iLikelyEcpCharge(int iElement)311 int iLikelyEcpCharge(int iElement)
312 {
313    // these are for ECP10MDFs used with vtz-pp. def2 sets are always all-electron for those.
314    // And at the time of writing, the PP sets are not actually there for 21-28.
315    if (in_range_incl(21,36,iElement)) return 10;
316 
317    // these are def2-nzvpp/dhf-nvzpp associations (mostly ECPxxMDF, but some MWB are left).
318    if (in_range_incl(1,36,iElement)) return 0;
319    if (in_range_incl(37,54,iElement)) return 28;
320    if (in_range_incl(55,71,iElement)) return 46; // I guess? somehow I don't seem to have sets for lanthanides (except lanthan)
321    if (in_range_incl(72,86,iElement)) return 60;
322 
323    return 0;
324 }
325 
326 
327 
CalcChargesOnSelectedAtoms()328 void FDocument::CalcChargesOnSelectedAtoms()
329 {
330    FFrame
331       *pFrame = GetCurrentFrame();
332    if (!pFrame)
333       return IV_NOTIFY(NOTIFY_Error, "No current frame to search orbitals");
334    FAtomIdList
335       iSelectedAtoms = GetSelectedAtoms();
336    FMemoryLogQt
337       Log;
338    FAtomSet
339       *pAtoms = GetCurrentFrame()->pGetAtoms();
340    if (!pAtoms)
341       return IV_NOTIFY(NOTIFY_Error, "No atoms?");
342 //    IvEmit("\n!Search Orbitals on Atoms: [THRCHG=%1]", ThrPrint);
343    QString
344       sAtomList = "Charges on Atoms:";
345    for (size_t iiAt = 0; iiAt < iSelectedAtoms.size(); ++ iiAt)
346       sAtomList.append(QString(" %1").arg(iSelectedAtoms[iiAt]+1));
347 
348    pFrame->RunChargeAnalysis(Log, &iSelectedAtoms);
349 
350    FShowTextForm
351       ShowTextForm("<pre>" + Log.GetText() + "</pre>", sAtomList, QString(), g_pMainWindow);
352    ShowTextForm.exec();
353 }
354 
355 
356 
FindOrbitalsOnSelectedAtoms()357 void FDocument::FindOrbitalsOnSelectedAtoms()
358 {
359    FFrame
360       *pFrame = GetCurrentFrame();
361    if (!pFrame)
362       return IV_NOTIFY(NOTIFY_Error, "No current frame to search orbitals");
363 
364    double
365       ThrPrint = 0.02;
366    FAtomIdList iSelectedAtoms = GetSelectedAtoms();
367 
368    FFoundOrbitalList
369       FoundOrbitals;
370 
371 //    IvEmit("\n!Search Orbitals on Atoms: [THRCHG=%1]", ThrPrint);
372    QString
373       sAtomList = "Find Orbitals on Atoms:";
374    for (size_t iiAt = 0; iiAt < iSelectedAtoms.size(); ++ iiAt)
375       sAtomList.append(QString(" %1").arg(iSelectedAtoms[iiAt]+1));
376    sAtomList.append(QString(" [THRCHG=%1]").arg(ThrPrint));
377 
378 
379    FDataSetList::iterator
380       itDataSet;
381    for (itDataSet = pFrame->m_Data.begin(); itDataSet != pFrame->m_Data.end(); ++ itDataSet) {
382       FOrbital
383          *pOrb = dynamic_cast<FOrbital*>(itDataSet->get());
384       if (!pOrb)
385          continue;
386       TArray<double>
387          IaoCharges = pOrb->MakeIaoCharges();
388       bool
389          KeepThis = false;
390       double
391          fChgOnSelected = 0.;
392       for (size_t iiAt = 0; iiAt < iSelectedAtoms.size(); ++ iiAt) {
393          size_t iAt = iSelectedAtoms[iiAt];
394          if (iAt >= IaoCharges.size()) // not supposed to happen.
395             break;
396          if (IaoCharges[iAt] > ThrPrint)
397             KeepThis = true;
398          fChgOnSelected += IaoCharges[iAt];
399       }
400       if (KeepThis) {
401          FoundOrbitals.push_back(FFoundOrbital(fChgOnSelected, itDataSet - pFrame->m_Data.begin(), pOrb));
402       }
403    }
404 
405 
406    FFoundOrbitalModel
407       Model(FoundOrbitals, this);
408    FFindOrbitalsForm
409       Dialog(&Model, sAtomList, g_pMainWindow);
410    Dialog.exec();
411 }
412 
413 
414 
415 
GetType() const416 QString FGeometry::GetType() const { return "G"; }; // geometry
417 
418 
419 // FRenderCache::~FRenderCache()
420 // {
421 // };
422 
423 
424 // void FDataSet::InvalidateRenderCache()
425 // {
426 //    this->pRenderCache = 0;
427 // };
428 
429 
FOrbitalVisualConfig()430 FOrbitalVisualConfig::FOrbitalVisualConfig()
431 {
432    bColorSet = false;
433    iIsoType = ISOTYPE_Undefined;
434    fIsoValue = -1.;
435    // ^- both not explicitly defined -- take whatever is currently the default when the orbtial is first traced.
436 }
437 
AssignDefaultColor(FDocument * pDocument)438 void FOrbitalVisualConfig::AssignDefaultColor(FDocument *pDocument)
439 {
440    if (pDocument == 0)
441       return IV_NOTIFY(NOTIFY_Error, "No parent document set. Very strange!");
442    //    uint32_t cFullAlpha = 0xff000000;
443    pDocument->GetNextOrbitalColors(cIsoPlus, cIsoMinus);
444    bColorSet = true;
445 }
446 
447 
Link(FOrbital * pOrbital)448 void FOrbitalVisualConfig::Link(FOrbital *pOrbital)
449 {
450    assert(LinkedOrbitals.find(pOrbital) == LinkedOrbitals.end());
451    LinkedOrbitals.insert(pOrbital);
452 }
453 
Unlink(FOrbital * pOrbital)454 void FOrbitalVisualConfig::Unlink(FOrbital *pOrbital)
455 {
456    assert(LinkedOrbitals.find(pOrbital) != LinkedOrbitals.end());
457    LinkedOrbitals.erase(pOrbital);
458 }
459 
UpdateLinkedRepresentations(uint32_t Flags,FView3d * pView3d)460 void FOrbitalVisualConfig::UpdateLinkedRepresentations(uint32_t Flags, FView3d *pView3d)
461 {
462    FOrbitalChain::iterator
463       it;
464    for (it = LinkedOrbitals.begin(); it != LinkedOrbitals.end(); ++ it) {
465       if (Flags & UPDATE_InvalidateIsoSettings) {
466          // completely delete the data set's mesh and rebuild it from scratch.
467          (*it)->InvalidateRenderCache();
468       }
469       if (Flags & UPDATE_InvalidateColors) {
470          (*it)->InvalidateColors();
471       }
472       if (Flags & UPDATE_Rebuild) {
473          assert(pView3d != 0);
474          (*it)->BuildRenderCache(pView3d);
475       }
476    }
477 }
478 
FOrbitalInfo(double fEnergy_,double fOcc_,int iSym_,FOrbitalSpin OrbSpin_)479 FOrbitalInfo::FOrbitalInfo(double fEnergy_, double fOcc_, int iSym_, FOrbitalSpin OrbSpin_)
480    : fEnergy(fEnergy_), fOcc(fOcc_), iSym(iSym_), iOriginalIndex(-1)
481 {
482    // fix up occupation numbers close to 0.0, 1.0, and 2.0.
483    // These might have been messed up due to input reading.
484    double const fEps = 1e-6;
485    if (std::abs(fOcc - 2.) < fEps)
486       fOcc = 2.;
487    if (std::abs(fOcc - 1.) < fEps)
488       fOcc = 1.;
489    if (std::abs(fOcc - 0.) < fEps)
490       fOcc = 0.;
491    // should we do something about negative occupations? We could see some in transition
492    // densities, or perturbative RDMs.
493 
494    if (OrbSpin_ != ORBSPIN_Unknown)
495       Spin = OrbSpin_;
496    else {
497       // Nothing explicit set, so we guess. Assume high-spin open-shell WF by
498       // default (with closed/alpha/empty orbitals).
499       if (std::abs(fOcc - 2.) < fEps) {
500          Spin = ORBSPIN_SpinFree;
501          fOcc = 2.;
502       } else if (std::abs(fOcc - 0.) < fEps) {
503          Spin = ORBSPIN_SpinFree;
504          fOcc = 0.;
505       } else if (std::abs(fOcc - 1.) < fEps) {
506          Spin = ORBSPIN_Alpha;
507          fOcc = 1.;
508       } else
509          Spin = ORBSPIN_SpinFree;
510    }
511 }
512 
FOrbital(QString const & Desc_,FAtomSetPtr pAtoms_,FDocument * pDocument_,FBasisSetPtr pBasisSet_,double * pCoeffs_,FOrbitalInfo const & info_,FOrbitalVisualConfigPtr pRefVisConfig,double * pDm,double * pQm)513 FOrbital::FOrbital(QString const &Desc_, FAtomSetPtr pAtoms_, FDocument *pDocument_, FBasisSetPtr pBasisSet_, double *pCoeffs_, FOrbitalInfo const &info_, FOrbitalVisualConfigPtr pRefVisConfig, double *pDm, double *pQm)
514    : FDataSet(Desc_, pAtoms_, pDocument_), pVisConfig(pRefVisConfig), info(info_), pBasisSet(pBasisSet_)
515 {
516    // make a new visual representation object if none was supplied from the outside.
517    if (pVisConfig.get() == 0)
518       pVisConfig = FOrbitalVisualConfigPtr(new FOrbitalVisualConfig());
519    pVisConfig->Link(this);
520 
521    // copy over coefficients.
522    pCoeffs.insert(pCoeffs.end(), pCoeffs_, pCoeffs_ + pBasisSet->nFn());
523 
524    // if dipole and quadrupole moments were supplied, store those too. These
525    // are used for setting up the viewing box and orientation of the orbital.
526    HaveMoments = false;
527    assert((pDm != 0) == (pQm != 0));
528    if (pDm) {
529       for (uint i = 0; i < 3; ++ i)
530          vDipMom[i] = pDm[i];
531       for (uint i = 0; i < 3; ++ i)
532          for (uint j = 0; j < 3; ++ j)
533             mQuadMom[i][j] = pQm[3*i + j];
534       HaveMoments = true;
535    }
536 }
537 
~FOrbital()538 FOrbital::~FOrbital()
539 {
540    pVisConfig->Unlink(this);
541 }
542 
543 
GetType() const544 QString FOrbital::GetType() const {
545    if (pGlMesh != 0)
546       return "O*"; // to mark which ones have data?
547    return "O"; // orbital
548 };
549 
550 
GetBaseColor() const551 uint32_t FDataSet::GetBaseColor() const
552 {
553    return 0;
554 }
555 
DependsOnWaveFunction() const556 bool FDataSet::DependsOnWaveFunction() const
557 {
558    return false;
559 }
560 
GetBaseColor() const561 uint32_t FOrbital::GetBaseColor() const
562 {
563    FOrbital const *pOrbital = this;
564    uint32_t dwColor;
565 
566    if (pOrbital->pVisConfig->bColorSet)
567 //    dwColor = (FColor(0.5f*(FColor(pOrbital->pVisConfig->cIsoPlus) + FColor(pOrbital->pVisConfig->cIsoMinus))).uint32());
568       dwColor = ct::irgb(FColor(0.5f*(FColor(pOrbital->pVisConfig->cIsoPlus) + FColor(pOrbital->pVisConfig->cIsoMinus))).uint32());
569    else
570       dwColor = 0xffffff;
571       // ^-- not set before rendering is finished...
572    dwColor |= 0xff000000;
573    return dwColor;
574 }
575 
DependsOnWaveFunction() const576 bool FOrbital::DependsOnWaveFunction() const
577 {
578    return true;
579 }
580 
FlipPhase()581 void FOrbital::FlipPhase()
582 {
583    for (size_t i = 0; i < pCoeffs.size(); ++ i)
584       pCoeffs[i] *= -1;
585    for (size_t i = 0; i < pIaoCoeffs.size(); ++ i)
586       pIaoCoeffs[i] *= -1;
587    InvalidateRenderCache();
588 }
589 
InvalidateColors()590 void FOrbital::InvalidateColors()
591 {
592    FOrbital *pOrb = this;
593    // update the color also in the mesh, if one already exists.
594    if (pOrb->pGlMesh.get()) {
595       for (uint i = 0; i < pOrb->pGlMesh->Vertices.size(); ++ i) {
596          FBaseVertex
597             &vx = pOrb->pGlMesh->Vertices[i];
598          if (vx.iRole == 0) vx.dwColor = pOrb->pVisConfig->cIsoPlus;
599          if (vx.iRole == 1) vx.dwColor = pOrb->pVisConfig->cIsoMinus;
600       }
601       pOrb->pGlMesh->Invalidate();
602    }
603 }
604 
605 
InvalidateRenderCache()606 void FOrbital::InvalidateRenderCache() {
607    pGlMesh = 0;
608 }
609 
610 
pOrbDescFromType(FOrbitalSpin Spin,double fOcc)611 char const *pOrbDescFromType(FOrbitalSpin Spin, double fOcc) {
612    if (fOcc == 2.)
613       return "AB";
614    if (fOcc == 1. && Spin == ORBSPIN_Alpha)
615       return "A_";
616    if (fOcc == 1. && Spin == ORBSPIN_Beta)
617       return "_B";
618    if (fOcc == 0. && Spin == ORBSPIN_SpinFree)
619       return "__";
620    if (fOcc == 0. && Spin == ORBSPIN_Alpha)
621       return "x_";
622    if (fOcc == 0. && Spin == ORBSPIN_Beta)
623       return "_x";
624    return "??";
625 }
626 
627 // QString FOrbitalInfo::MakeDesc(size_t iOrb) const
MakeDesc(QString RefName) const628 QString FOrbitalInfo::MakeDesc(QString RefName) const
629 {
630 //    return QString("%1 [E =%2  O =%3]").arg(RefName.trimmed(),3).arg(fEnergy,8,'f',4).arg(fOcc,7,'f',4);
631    QString
632       s;
633    QTextStream
634       out(&s);
635    out.setFieldAlignment(QTextStream::AlignRight);
636    out << QString("%1 [").arg(RefName, 5);
637    out << QString("E =%1 ").arg(fEnergy,8,'f',4);
638    out << "O =" << pOrbDescFromType(Spin, fOcc);
639    out << "]";
640    return s;
641 //    return QString("%1.1 [E =%2  O =%3]").arg(iOrb+1,3).arg(fEnergy,8,'f',4).arg(fOcc,7,'f',4);
642 //     [E=%8.4f  O= %6.4f]
643 }
644 
MakeDesc(size_t iOrb) const645 QString FOrbitalInfo::MakeDesc(size_t iOrb) const
646 {
647    return MakeDesc(QString("%1.1").arg(iOrb+1));
648 //    return QString("%1.1 [E =%2  O =%3]").arg(iOrb+1,3).arg(fEnergy,8,'f',4).arg(fOcc,7,'f',4);
649 }
650 
UpdateDescFromInfo(size_t iOrb)651 void FOrbital::UpdateDescFromInfo(size_t iOrb)
652 {
653    m_Desc = info.MakeDesc(iOrb);
654 }
655 
GetDesc(uint Flags) const656 QString FOrbital::GetDesc(uint Flags) const
657 {
658    if (Flags == DESC_Full) {
659       return MakeFullDesc(0.02, ORBDESC_CompactSpace, 4);
660    }
661    return FBase::GetDesc(Flags);
662 }
663 
664 
MakeFullDesc(double ThrPrint,uint Flags,int nMaxAtoms) const665 QString FOrbital::MakeFullDesc(double ThrPrint, uint Flags, int nMaxAtoms) const
666 {
667    bool
668       Compact = bool(Flags & ORBDESC_CompactSpace);
669    QString
670       s;
671    if (0 == (Flags & ORBDESC_ChargesOnly))
672       s = this->GetDesc() + (Compact? "   " : "   ");
673    if (Compact)
674       s.replace("  ", " ");
675    TArray<double>
676       Charges = MakeIaoCharges();
677    TArray<size_t>
678       Indices;
679    Indices.resize(Charges.size());
680    ct::ArgSort1(&Indices[0], &Charges[0], 1, Charges.size(), true); // last: reverse. Largest first.
681 
682    {
683       QTextStream
684          out(&s);
685 //       out << "  CENTERS/CHARGES:";
686 //       out << "   ";
687       out.setRealNumberNotation(QTextStream::FixedNotation);
688       double
689          fChgRest = 0.;
690       int
691          nAtomsEmitted = 0;
692       for (size_t ii = 0; ii < Indices.size(); ++ ii) {
693          size_t
694             iAt = Indices[ii];
695          if (iAt >= pAtoms->size())
696             continue; // not supposed to happen.
697          double
698             fAtChg = Charges[iAt];
699          if (fAtChg < ThrPrint || (nMaxAtoms >= 0 && (nAtomsEmitted >= nMaxAtoms))) {
700             fChgRest += fAtChg;
701             continue;
702          }
703          FAtom
704             &At = (*pAtoms)[iAt];
705          out.setRealNumberPrecision(0);
706          if (Compact) {
707             if (ii != 0)
708                out << " | ";
709             out << s2q(At.GetElementName()) << " " << (1+iAt) << ": ";
710             out.setRealNumberPrecision(2);
711             out << Charges[iAt];
712          } else {
713             out << "  ";
714             out.setFieldWidth(2);
715             out << s2q(At.GetElementName()).toUpper();
716             out.setFieldWidth(3);
717             out << (1+iAt);
718             out.setFieldWidth(0);
719             out << " ";
720             out.setFieldWidth(6);
721             out.setRealNumberPrecision(3);
722             out << Charges[iAt];
723          }
724 
725          out.setFieldWidth(0);
726          nAtomsEmitted += 1;
727       }
728       if (fChgRest > ThrPrint) {
729          out.setRealNumberPrecision(3);
730          out << "   (other: " << fChgRest << ")";
731       }
732    }
733    return s;
734 }
735 
736 
737 // -> IvView3d.cpp
738 // void FOrbital::BuildRenderCache(FView3d */*pView3d*/) {
739 // }
740 
741 
742 
FFrame(QString Desc_,FDocument * pDocument_)743 FFrame::FFrame(QString Desc_, FDocument *pDocument_)
744    : m_pDocument(pDocument_)
745 {
746    m_InputFileName = Desc_;
747    m_pFrameLog = new FMemoryLogQt(this);
748 }
749 
IFrame(QString Desc_,FDocument * pDocument_)750 IFrame::IFrame(QString Desc_, FDocument *pDocument_) : FFrame(Desc_, pDocument_) {
751    setObjectName(QString("IFrame(%1)").arg(m_InputFileName));
752 }
753 
754 
755 
HaveOrbitals() const756 bool FFrame::HaveOrbitals() const
757 {
758    FDataSetList::const_iterator itDataSet;
759    _for_each(itDataSet, m_Data) {
760       if (dynamic_cast<FOrbital const*>(&**itDataSet) != 0)
761          return true;
762    }
763    return false;
764 }
765 
ClearOrbitals()766 void FFrame::ClearOrbitals()
767 {
768    // make a new list of data sets without the electronic structure things.
769    FDataSetList
770       New;
771    FDataSetList::const_iterator itDataSet;
772    _for_each(itDataSet, m_Data) {
773       if (!(*itDataSet)->DependsOnWaveFunction()) {
774          New.push_back(*itDataSet);
775       }
776    }
777    m_Data.swap(New);
778    m_pOrbBasis = 0;
779    m_pMinBasis = 0;
780    m_CIaoData.clear();
781    m_CIb = FMatrixView(0,0,0);
782 }
783 
ClearOrbitals(bool EmitSignals)784 void FDocument::ClearOrbitals(bool EmitSignals)
785 {
786    if (EmitSignals)
787       BeginTotalReset();
788       //emit layoutAboutToBeChanged();
789    for (size_t iFrame = 0; iFrame < m_Frames.size(); ++ iFrame)
790       GetFrame(iFrame)->ClearOrbitals();
791    if (EmitSignals)
792       EndTotalReset();
793    //emit layoutChanged();
794 }
795 
796 // std::string BaseName(std::string const &FileName) {
797 //    // get the base file name as description, without the path.
798 //    size_t
799 //       iPathSept = FileName.find_last_of('/');
800 //    std::string
801 //       s;
802 //    if ( iPathSept != std::string::npos )
803 //       s = FileName.substr(1+iPathSept);
804 //    else
805 //       s = FileName;
806 //    return s;
807 // }
808 
GetBaseInputFileName() const809 QString FFrame::GetBaseInputFileName() const
810 {
811    return RemoveExt(m_InputFileName);
812 }
813 
GetFullInputFileName() const814 QString FFrame::GetFullInputFileName() const
815 {
816    return m_InputFileName;
817 }
818 
819 
pGetGeometry()820 FGeometry *FFrame::pGetGeometry()
821 {
822    if (m_Data.empty())
823       return 0;
824    return dynamic_cast<FGeometry*>(m_Data[0].get());
825 }
826 
pGetAtoms()827 ct::FAtomSet *FFrame::pGetAtoms()
828 {
829    FGeometry
830       *pGeometry = pGetGeometry();
831    if (pGeometry == 0)
832       return 0;
833    return pGeometry->pAtoms.get();
834 }
835 
GetEnergy() const836 double FFrame::GetEnergy() const
837 {
838    ct::FAtomSet const
839       *pAtoms = pGetAtoms();
840    if (pAtoms)
841       return pAtoms->GetLastEnergy();
842    return 0.; // no atoms -> no energy.
843 }
844 
GetGradient() const845 double FFrame::GetGradient() const
846 {
847    ct::FAtomSet const
848       *pAtoms = pGetAtoms();
849    if (pAtoms) {
850       double
851          fGrad = 0.;
852       for (size_t iAt = 0; iAt < pAtoms->size(); ++ iAt)
853          fGrad += ct::LengthSq((*pAtoms)[iAt].vGrad);
854       fGrad = std::sqrt(fGrad);
855       return fGrad;
856    }
857    return 0.;
858 }
859 
860 
pGetOrbital(int iMo)861 FOrbital *FFrame::pGetOrbital(int iMo)
862 {
863    if (iMo <= 0 || size_t(iMo) >= m_Data.size())
864       return 0;
865    return dynamic_cast<FOrbital*>(m_Data[iMo].get());
866 }
867 
868 
add_bond(int iAt,int jAt,QString const & Flags)869 void IFrame::add_bond(int iAt, int jAt, QString const &Flags)
870 {
871    FGeometry
872       *pGeometry = pGetGeometry();
873    if (pGeometry)
874       pGeometry->AddBond(iAt, jAt, Flags);
875 }
876 
delete_bond(int iAt,int jAt)877 void IFrame::delete_bond(int iAt, int jAt)
878 {
879    FGeometry
880       *pGeometry = pGetGeometry();
881    if (pGeometry)
882       pGeometry->DeleteBond(iAt, jAt);
883 }
884 
reset_bonds()885 void IFrame::reset_bonds()
886 {
887    FGeometry
888       *pGeometry = pGetGeometry();
889    if (pGeometry)
890       pGeometry->FindBondLines(m_pDocument);
891 }
892 
get_name()893 QString IFrame::get_name()
894 {
895    return m_InputFileName;
896 }
897 
set_name(QString const & Name)898 void IFrame::set_name(QString const &Name)
899 {
900    m_InputFileName = Name;
901 }
902 
903 
904 
905 
906 template<class T>
Scale(T * p,size_t n,T Factor)907 void Scale(T *p, size_t n, T Factor) {
908    for (size_t i = 0; i < n; ++ i)
909       p[i] *= Factor;
910 }
911 
912 
MakeOrbitalMatrix(FMatrixView & COrb,FBasisSetPtr & pBasis2,TArray<FOrbital * > * pRefOrbitals,uint32_t Flags,FMemoryStack & Mem)913 void FFrame::MakeOrbitalMatrix(FMatrixView &COrb, FBasisSetPtr &pBasis2, TArray<FOrbital*> *pRefOrbitals, uint32_t Flags, FMemoryStack &Mem)
914 {
915    if (!HaveOrbitals()) {
916       COrb = FMatrixView(0,0,0);
917       pBasis2 = 0;
918       if (pRefOrbitals)
919          pRefOrbitals->clear();
920       return;
921    }
922 
923    FFrame
924       *pLastFrame = this;
925    // count the number of orbitals and find the common basis size.
926    pBasis2 = 0;
927    TArray<FOrbital*>
928       RefOrbitals1,
929       &RefOrbitals = (pRefOrbitals? *pRefOrbitals : RefOrbitals1);
930    RefOrbitals.clear();
931    RefOrbitals.reserve(pLastFrame->m_Data.size());
932    for (uint i = 0; i < pLastFrame->m_Data.size(); ++ i) {
933       FOrbital
934          *pOrb = dynamic_cast<FOrbital*>(pLastFrame->m_Data[i].get());
935       // filter out orbitals we are not supposed to return.
936       if (!pOrb)
937          continue;
938       if (((Flags & ORBMAT_OccupiedOnly) != 0) && pOrb->info.fOcc <= 0.)
939          continue;
940       if (((Flags & ORBMAT_VirtualOnly) != 0) && pOrb->info.fOcc > 0.)
941          continue;
942       FOrbitalSpin
943          OrbSpin = pOrb->info.Spin;
944       if (((Flags & ORBMAT_AlphaAndClosedOnly) != 0) && (OrbSpin != ORBSPIN_Alpha && OrbSpin != ORBSPIN_SpinFree))
945          continue;
946       if (((Flags & ORBMAT_BetaAndClosedOnly) != 0) && (OrbSpin != ORBSPIN_Beta && OrbSpin != ORBSPIN_SpinFree))
947          continue;
948       if (((Flags & ORBMAT_AlphaOnly) != 0) && (OrbSpin != ORBSPIN_Alpha))
949          continue;
950       if (((Flags & ORBMAT_ClosedOnly) != 0) && (OrbSpin != ORBSPIN_SpinFree))
951          continue;
952       if (((Flags & ORBMAT_BetaOnly) != 0) && (OrbSpin != ORBSPIN_Beta))
953          continue;
954       if (pBasis2.get() == 0) {
955          pBasis2 = pOrb->pBasisSet;
956       } else {
957          if (pBasis2 != pOrb->pBasisSet.get())
958             throw std::runtime_error("FFrame::MakeOrbitalMatrix: Multiple orbitals in *same* frame expressed wrt. different basis sets.");
959       }
960       RefOrbitals.push_back(pOrb);
961    }
962    if (RefOrbitals.size() == 0) {
963       // no orbitals of the given class exist.
964       pBasis2 = 0;
965       COrb = FMatrixView(0,0,0);
966       return;
967    }
968    uint
969       nOrb2 = RefOrbitals.size(),
970       nBf2 = pBasis2->nFn();
971 
972    // make a matrix for the orbitals on Mem and copy the coefficients into it
973    COrb = MakeStackMatrix(nBf2, nOrb2, Mem);
974    for (uint i = 0; i < RefOrbitals.size(); ++ i) {
975       FOrbital
976          *pOrb = RefOrbitals[i];
977       assert(pOrb->pCoeffs.size() == nBf2);
978       for (uint iBf = 0; iBf < nBf2; ++ iBf)
979          COrb(iBf, i) = pOrb->pCoeffs[iBf];
980    }
981 }
982 
GetWfType()983 FWfType FFrame::GetWfType()
984 {
985    size_t
986       nAlpha = 0,
987       nBeta = 0,
988       nClosed = 0,
989       nOther = 0;
990    FDataSetList::const_iterator
991       itDataSet;
992    _for_each(itDataSet, m_Data) {
993       FOrbital const
994          *pOrbital = dynamic_cast<FOrbital const*>(&**itDataSet);
995       if (pOrbital == 0)
996          continue; // dataset is not an orbital.
997       switch (pOrbital->info.Spin) {
998          case ORBSPIN_SpinFree:
999             nClosed += 1;
1000             break;
1001          case ORBSPIN_Alpha:
1002             nAlpha += 1;
1003             break;
1004          case ORBSPIN_Beta:
1005             nBeta += 1;
1006             break;
1007          default:
1008             nOther += 1;
1009             break;
1010       }
1011    }
1012 //    std::cout << boost::format("GetWfType(): nA = %i  nB = %i  nC = %i  nO = %i\n") % nAlpha % nBeta % nClosed % nOther;
1013    if ((nAlpha != 0 && nBeta == 0) || (nClosed != 0 && nBeta == 0))
1014       return WFTYPE_Rhf;
1015    if (nAlpha != 0 && nBeta != 0 && nClosed == 0)
1016       return WFTYPE_Uhf;
1017    if (nBeta == 0 && nAlpha == 0 && (nClosed != 0 || nOther != 0))
1018       // todo: should check if total number of occupied orbitals is smaller than
1019       // the number of valence orbitals... but I don't have a valence orbital counting
1020       // function coded up atm.
1021       return WFTYPE_Mcscf;
1022    return WFTYPE_Other;
1023 }
1024 
1025 
MakeOrbitalMoments(ct::FMemoryStack & Mem)1026 void FFrame::MakeOrbitalMoments(ct::FMemoryStack &Mem)
1027 {
1028    if (!HaveOrbitals())
1029       return;
1030    void
1031       *pFreeMe = Mem.Alloc(0);
1032    // compute center and quadrupole moment for all orbitals in the current frame.
1033    // Unfortunatelly, with CtInt1e making those matrices is *very* slow for
1034    // larger molecules.
1035    ct::FAtomSet
1036       *pAtoms = pGetAtoms();
1037    ct::FMatrixView
1038       COrb;
1039    FBasisSetPtr
1040       pBasisSet;
1041    TArray<FOrbital*>
1042       RefOrbitals;
1043    MakeOrbitalMatrix(COrb, pBasisSet, &RefOrbitals, 0, Mem);
1044 
1045    size_t
1046       nOrb = COrb.nCols,
1047       nAo = pBasisSet->nFn();
1048 
1049    TArray<double>
1050       CartMomMatrices;
1051    {
1052       TVector3<double>
1053          ExpansionPoint(0., 0., 0.);
1054       CartMomMatrices.resize(nAo*nAo*10);
1055       FDoublettIntegralFactoryMultipoleMoment::FMoment
1056          CartMoments[10];
1057       for (size_t i = 0; i < 10; ++ i)
1058          for (size_t ixyz = 0; ixyz <= 2; ++ ixyz)
1059             CartMoments[i][ixyz] = ir::iCartPow[i][ixyz];
1060       FDoublettIntegralFactoryMultipoleMoment
1061          CartMomFactory(&CartMoments[0], &CartMoments[10], ExpansionPoint);
1062       pAtoms->Make1eIntegralMatrixMultiComp(&CartMomMatrices[0], *pBasisSet, *pBasisSet, CartMomFactory, 1.0, Mem);
1063 
1064       FStackMatrix
1065          Values(10, nOrb, &Mem);
1066       Values.Clear();
1067 
1068       {  // this has hard O(N^3) scaling but is still significantly faster than the O(n^2) variants...
1069          FStackMatrix
1070             T1(nAo, nOrb, &Mem);
1071          for (size_t iComp = 0; iComp < 10; ++ iComp) {
1072             FMatrixView
1073                DipN(&CartMomMatrices[nAo*nAo*(iComp)], nAo, nAo);
1074             Mxm(T1, DipN, COrb);
1075             for (size_t iOrb = 0; iOrb < nOrb; ++ iOrb)
1076                Values(iComp, iOrb) = Dot(&T1(0, iOrb), &COrb(0, iOrb), nAo);
1077          }
1078       }
1079 
1080       for (size_t iOrb = 0; iOrb < nOrb; ++ iOrb) {
1081          for (size_t i = 0; i < 3; ++ i)
1082             RefOrbitals[iOrb]->vDipMom[i] = Values(1+i,iOrb);
1083       }
1084       for (size_t i = 0; i < 3; ++ i) {
1085          for (size_t j = 0; j <= i; ++ j)
1086          {
1087             size_t ij;
1088             TVector3<unsigned> CartMom(0, 0, 0);
1089             CartMom[i] += 1;
1090             CartMom[j] += 1;
1091             for (ij = 4; ij <= 10; ++ ij)
1092                if (ir::iCartPow[ij][0] == CartMom[0] && ir::iCartPow[ij][1] == CartMom[1] && ir::iCartPow[ij][2] == CartMom[2])
1093                   break;
1094             for (size_t iOrb = 0; iOrb < nOrb; ++ iOrb) {
1095                TVector3<double>
1096                   Center(RefOrbitals[iOrb]->vDipMom[0], RefOrbitals[iOrb]->vDipMom[1], RefOrbitals[iOrb]->vDipMom[2]);
1097                double
1098 //                   rsq = Dot(Center, Center),
1099                   mass = 1.;
1100 
1101                double v = Values(ij,iOrb) - Center[i] * Center[j] * mass;
1102 //                if (i==j)
1103 //                   v += rsq * mass;
1104                // ^- very strange... the "parallel axis theorem" according to wikipedia
1105                // gives a different transformation for diagonal elements. But this version here
1106                // works and their's doesn't.
1107                RefOrbitals[iOrb]->mQuadMom[i][j] = v;
1108                RefOrbitals[iOrb]->mQuadMom[j][i] = v;
1109             }
1110          }
1111       }
1112 
1113       for (size_t iOrb = 0; iOrb < nOrb; ++ iOrb)
1114          RefOrbitals[iOrb]->HaveMoments = true;
1115    }
1116 
1117    Mem.Free(pFreeMe);
1118 }
1119 
FindRow(FDataSet * pSet)1120 int FFrame::FindRow(FDataSet* pSet)
1121 {
1122    for (int i = 0; i < (int)m_Data.size(); ++ i) {
1123       if (m_Data[i] == pSet)
1124          return i;
1125    }
1126    IV_NOTIFY1(NOTIFY_Error, "Failed to find data set '%1' in the current frame.", pSet->GetDesc());
1127    return -1;
1128 }
1129 
1130 
1131 // predicate: sorts orbitals into original order, before linking them.
1132 struct FSortOrbitalsToOriginalOrder
1133 {
operator ()FSortOrbitalsToOriginalOrder1134    bool operator() (FDataSetPtr pA, FDataSetPtr pB) {
1135       FOrbital
1136          *pOrbA = dynamic_cast<FOrbital*>(pA.get()),
1137          *pOrbB = dynamic_cast<FOrbital*>(pB.get());
1138       if (pOrbA == 0 && pOrbB == 0)
1139          return false;
1140       if (pOrbA == 0)
1141          return true;
1142       if (pOrbB == 0)
1143          return false;
1144       // so... both are orbitals.
1145       return pOrbA->info.iOriginalIndex < pOrbB->info.iOriginalIndex;
1146    }
1147 };
1148 
1149 
1150 // this sorts & flips the orbitals into a order maximizing the overlap with the
1151 // last frame's orbitals.
LinkOrbitalsToPreviousFrame(FFrame * pPrevFrame,ct::FMemoryStack & Mem)1152 void FFrame::LinkOrbitalsToPreviousFrame(FFrame *pPrevFrame, ct::FMemoryStack &Mem)
1153 {
1154    if (!HaveOrbitals())
1155       return;
1156    if (!pPrevFrame) {
1157       // no reference frame. Don't link the orbitals---bring them into original order
1158       // in which we got them (before linking visual configs)
1159       std::stable_sort(m_Data.begin(), m_Data.end(), FSortOrbitalsToOriginalOrder());
1160       return;
1161    }
1162 
1163    void
1164       *pTopOfStack = Mem.Alloc(0);
1165    // for now just do it with direct overlap. However, we first need to re-assemble
1166    // the last frame's orbital matrix.
1167    ct::FMatrixView
1168       CurOrb, LastOrb;
1169    FBasisSetPtr
1170       pBasis1, pBasis2;
1171    TArray<FOrbital*>
1172       RefOrbitals1, RefOrbitals2;
1173    // FIXME: for UHF wave functions this needs to be done for alpha and beta orbitals separately.
1174    //        Technically, we should probably always do it within a given subspace, but we might
1175    //        not be able to distinguish active orbitals with 2.0000 from closed shell-orbitals
1176    //        in every situation, since this information mightnot be provided in the input with
1177    //        sufficient accuracy...
1178    this->MakeOrbitalMatrix(CurOrb, pBasis1, &RefOrbitals1, 0, Mem);
1179    pPrevFrame->MakeOrbitalMatrix(LastOrb, pBasis2, &RefOrbitals2, 0, Mem);
1180 
1181    uint
1182       nOrb1 = CurOrb.nCols,
1183       nOrb2 = LastOrb.nCols, // last.
1184       nBf2 = pBasis2->nFn(),
1185       nBf1 = pBasis1->nFn();
1186    // for now assume that format of orbitals is equal...
1187    assert(nBf2 == LastOrb.nRows);
1188    if (nBf1 != nBf2)
1189       return IV_NOTIFY(NOTIFY_Error, "Current and last frame's orbital formats do not match. Currently we do not support aligning them.");
1190    if (nOrb1 != nOrb2)
1191       return IV_NOTIFY(NOTIFY_Error, "Current and last frame's number of orbitals do not match. Currently we do not support aligning them.");
1192 
1193    {
1194       // form the overlap of the last and the current orbitals with respect to the
1195       // current frame's overlap matrix (note that we should not directly use the
1196       // cross overlap: due to the geometry change, the core orbitals have moved:
1197       // cores of the same atoms thus have near zero overlap in different frames!)
1198       FStackMatrix
1199          SAo(nBf1, nBf1, &Mem),
1200          SMo(nOrb1, nOrb2, &Mem);
1201       pGetAtoms()->MakeOverlapMatrix(SAo, *pBasis1, *pBasis1, Mem);
1202       ChainMxm(SMo, Transpose(CurOrb), SAo, LastOrb, Mem);
1203 
1204       // delete overlap between orbitals of different spin. It's a hack to get it
1205       // workting for UHF and co.
1206       for (size_t iOrb2 = 0; iOrb2 < nOrb2; ++ iOrb2)
1207          for (size_t iOrb1 = 0; iOrb1 < nOrb1; ++ iOrb1)
1208             if (RefOrbitals1[iOrb1]->info.Spin != RefOrbitals2[iOrb2]->info.Spin)
1209                SMo(iOrb1, iOrb2) = 0.;
1210 
1211       std::vector<bool>
1212          bNewOrbAssigned(nOrb2, false);
1213       for (uint iRef = 0; iRef < nOrb2; ++ iRef) {
1214          // find orbital with maximum absolute overlap with respect to iRef.
1215          // Update: can't I just SVD the matrix to get corresponding orbitals?
1216          double fMaxOvl = 0.;
1217          int iMaxOrb = -1;
1218          for (uint i = 0; i < nOrb1; ++ i) {
1219             if (bNewOrbAssigned[i])
1220                continue;
1221             double fOvl = std::abs(SMo(i,iRef));
1222             if (fOvl > fMaxOvl) {
1223                fMaxOvl = fOvl;
1224                iMaxOrb = i;
1225             }
1226          }
1227 
1228          FOrbital
1229             *pLastOrb = RefOrbitals2[iRef],
1230             *pCurOrb = RefOrbitals1[iMaxOrb];
1231 
1232          // flip the orbital if this overlap is negative.
1233          bNewOrbAssigned[iMaxOrb] = true;
1234          if (fMaxOvl < 0.8) {
1235 //             IV_NOTIFY(NOTIFY_Warning, IvFmt("poor overlap (%1) between %2/%3 and %4/%5",
1236 //                QString::number(fMaxOvl,'g',3), pCurOrb->GetDesc(), this->GetBaseInputFileName(),
1237 //                pLastOrb->GetDesc(), pPrevFrame->GetBaseInputFileName()));
1238             m_pFrameLog->Write("WARNING: poor overlap ({:.3f}) between {}/{} and {}/{}",
1239                       fMaxOvl,
1240                       q2s(pCurOrb->GetDesc()), q2s(this->GetBaseInputFileName()),
1241                       q2s(pLastOrb->GetDesc()), q2s(pPrevFrame->GetBaseInputFileName()));
1242          }
1243          if (SMo(iMaxOrb,iRef) < 0)
1244             pCurOrb->FlipPhase();
1245          // link up this orbital's visual config to the last one's
1246          pCurOrb->LinkVisualConfig(pLastOrb->pVisConfig);
1247 
1248 
1249          // put current orb into iRef's row index in the current frame.
1250          int
1251             iRefRow = pPrevFrame->FindRow(pLastOrb),
1252             iCurRow = this->FindRow(pCurOrb);
1253          m_Data[iCurRow].swap(m_Data[iRefRow]);
1254       }
1255    }
1256 
1257    Mem.Free(pTopOfStack);
1258 }
1259 
LinkVisualConfig(FOrbitalVisualConfigPtr p)1260 void FOrbital::LinkVisualConfig(FOrbitalVisualConfigPtr p)
1261 {
1262    if (pVisConfig != p) {
1263       pVisConfig = p;
1264       pVisConfig->Link(this);
1265    }
1266 }
1267 
FWfOptions(QObject * parent)1268 FWfOptions::FWfOptions(QObject *parent)
1269    : QObject(parent)
1270 {
1271    setObjectName("WfOptions");
1272    InitProperties();
1273    m_NumThreads = g_nMaxOmpThreads;
1274 }
1275 
AssignBasisSets(ct::FAtomSet * pAtoms)1276 void FWfOptions::AssignBasisSets(ct::FAtomSet *pAtoms)
1277 {
1278    std::string
1279       sOrbBasis = q2s(GetOrbBasis()),
1280       sFitBasis = q2s(GetFitBasis());
1281 //    IvEmit(s2q("// SET BASIS: " + sOrbBasis + " // " + sFitBasis));
1282    for (size_t iAt = 0; iAt != pAtoms->size(); ++ iAt) {
1283       (*pAtoms)[iAt].BasisDesc[ct::BASIS_Orbital] = sOrbBasis;
1284       (*pAtoms)[iAt].BasisDesc[ct::BASIS_JFit] = sFitBasis;
1285       (*pAtoms)[iAt].BasisDesc[ct::BASIS_JkFit] = sFitBasis;
1286    }
1287 }
1288 
AssignScfOptions(ct::FHfOptions & ScfOpt)1289 void FWfOptions::AssignScfOptions(ct::FHfOptions &ScfOpt)
1290 {
1291    ScfOpt.ThrOrb = GetThrGrad();
1292    ScfOpt.ThrDen = GetThrEnergy();
1293    ScfOpt.MaxIt = (uint)GetMaxIter();
1294    ScfOpt.XcAlgo = ct::XCALGO_AuxiliaryExpand;
1295    ScfOpt.JkAlgo = ct::JKALGO_DfCoulombOnlyCache3ix;
1296    ScfOpt.ComputeFlags = 0;
1297 
1298    // remove the explanation parts of the functional so that
1299    // FXcFunctional has a chance to recognize it.
1300    QString
1301       sFunctional1 = GetFunctional();
1302    int iParens = sFunctional1.indexOf("(");
1303    if (iParens != -1) {
1304       sFunctional1.chop(sFunctional1.size() - iParens);
1305       sFunctional1 = sFunctional1.trimmed();
1306    }
1307 //    IvEmit("Set xc: '%1'",sFunctional1);
1308    ScfOpt.XcFunctionalName = q2s(sFunctional1);
1309    // grid params?
1310 }
1311 
AssignWfDecl(ct::FWfDecl & WfDecl,ct::FAtomSet * pAtoms)1312 void FWfOptions::AssignWfDecl(ct::FWfDecl &WfDecl, ct::FAtomSet *pAtoms)
1313 {
1314    int
1315       iCharge = GetCharge(),
1316       iExtraSpin = GetExtraSpin();
1317    int
1318       nElec = pAtoms->NuclearCharge() - iCharge,
1319       Ms2;
1320    if (nElec % 2 == 0)
1321       Ms2 = 0 + 2 * iExtraSpin;
1322    else
1323       Ms2 = 1 + 2 * iExtraSpin;
1324    WfDecl = ct::FWfDecl(nElec, Ms2);
1325 }
1326 
Clear()1327 void FDocument::Clear()
1328 {
1329    BeginTotalReset();
1330    SetActiveCol(-1);
1331    SetActiveRow(-1);
1332 
1333    //emit layoutAboutToBeChanged();
1334 //    m_ActiveRow = -1;
1335 //    m_ActiveCol = -1;
1336    m_Frames.clear();
1337    m_InputFileName = "";
1338    m_AtomOptions.clear();
1339    m_iNextOrbitalColor = 0;
1340    EndTotalReset();
1341 //    m_iNextOrbitalColorScheme = 0;
1342    // leave the current setting. may be useful if someone wants to apply them
1343    // to multiple files.
1344 //    delete m_pWfOptions;
1345 //    m_pWfOptions = new FWfOptions(this);
1346 }
1347 
BeginTotalReset()1348 void FDocument::BeginTotalReset()
1349 {
1350    beginResetModel();
1351 }
EndTotalReset()1352 void FDocument::EndTotalReset()
1353 {
1354    endResetModel();
1355    emit layoutAboutToBeChanged();
1356    emit layoutChanged();
1357    // ^- FIXME: fix up connections in main and then remove these two.
1358    //emit layoutChanged();
1359    emit ActiveColChanged(m_ActiveCol);
1360    emit ActiveRowChanged(m_ActiveRow);
1361 }
1362 
RebuildWf(FLogQt & Log)1363 void FDocument::RebuildWf(FLogQt &Log)
1364 {
1365    ct::FHfOptions
1366       ScfOpt;
1367    m_pWfOptions->AssignScfOptions(ScfOpt);
1368 
1369    //emit layoutAboutToBeChanged();
1370    BeginTotalReset();
1371 
1372    if (m_pWfOptions->GetRunScf())
1373       ClearOrbitals(false); // false: no signals.
1374 
1375    // need to re-set the maximum number of threads. OMP seems to think
1376    // that it shouldn't spawn more threads, otherwise, if not invoked from main thread.
1377 //    omp_set_num_threads(g_nMaxOmpThreads);
1378    omp_set_num_threads(m_pWfOptions->GetNumThreads());
1379 
1380    bool ErrorOccured = false;
1381    std::string ErrorDesc;
1382 
1383    FFrame
1384       *pLastFrame = 0;
1385    FHfResultPtr
1386       pLastWf; // for starting guess purposes.
1387    FMemoryStack2
1388       Mem(size_t(m_pWfOptions->GetNumThreads() * m_pWfOptions->GetWorkSpaceMb())<<20);
1389    for (size_t iFrame = 0; iFrame < m_Frames.size(); ++ iFrame)
1390    {
1391       FFrame
1392          *pFrame = GetFrame(iFrame);
1393       Log.Write("*** CURRENT FRAME: {} ({} of {})", q2s(RemovePath(pFrame->GetFullInputFileName())), iFrame+1, m_Frames.size());
1394 
1395       // forward main log to frame's local computation log.
1396       connect(&Log, SIGNAL(textEmitted(QString)), &pFrame->Log(), SLOT(appendText(QString)));
1397 
1398       try {
1399          ct::FAtomSet
1400             *pAtoms = pFrame->pGetAtoms();
1401          if (!pAtoms)
1402             continue;
1403          // reset the default basis sets.
1404          m_pWfOptions->AssignBasisSets(pAtoms);
1405 
1406          ct::TMemoryLock<char>
1407             pFreeMe(0, &Mem);
1408          ct::FWfDecl
1409             WfDecl;
1410          m_pWfOptions->AssignWfDecl(WfDecl, pAtoms);
1411 
1412          if (m_pWfOptions->GetRunScf()) {
1413             FHfResultPtr
1414                pWf = new FHfResult;
1415 
1416             ct::FHfMethod
1417                Hf(pWf.get(), pLastWf.get(), Log, WfDecl, *pAtoms, ScfOpt, Mem);
1418 
1419             pLastWf = pWf;
1420             pAtoms->SetLastEnergy(pWf->Energy); // probably should not be here... (and neither the gradients)
1421 
1422             // insert the orbitals as data sets.
1423             // WARNING: for correct energies in anti-bonding orbtials, I
1424             // need to store ALL orbitals here! Including virtuals (otherwise
1425             // there is not enough stuff to assemble the valence-virtual basis Fock matrix!).
1426             size_t
1427                nOrbsToLoad = pWf->Orb.nCols;
1428             if (m_SkipVirtualOrbitals)
1429                nOrbsToLoad = WfDecl.nElecA();
1430             for ( uint i = 0; i < nOrbsToLoad; ++ i ) {
1431 //             for ( uint i = 0; i < WfDecl.nElecA(); ++ i ) {
1432    //          for ( uint i = 0; i < Hf.pMinBasis->nFn(); ++ i ) {
1433                double
1434                   fOcc = 0.;
1435                if (i < WfDecl.nElecA())
1436                   fOcc = 1.;
1437                if (i < WfDecl.nElecB())
1438                   fOcc = 2.;
1439                double
1440                   fEnergy = pWf->Ew[i];
1441                FOrbitalInfo
1442                   info = FOrbitalInfo(fEnergy, fOcc, 0);
1443                info.iOriginalIndex = int(i);
1444                pFrame->m_Data.push_back( new FOrbital(info.MakeDesc(i), pAtoms, this, pWf->pOrbBasis,
1445                      &pWf->Orb(0,i), info, 0, 0, 0) );
1446             }
1447          }
1448 
1449 //          pFrame->RunIaoAnalysis(pFrame->Log(), m_pWfOptions, Mem);
1450          pFrame->RunIaoAnalysis(Log, m_pWfOptions, m_pWfOptions->GetRunIbba(), Mem); // last: decides on whether or not to localzie orbs.
1451          pFrame->MakeOrbitalMoments(Mem); // <- this is not required (will be made on the fly)
1452 
1453          pFrame->LinkOrbitalsToPreviousFrame(pLastFrame, Mem);
1454          pLastFrame = pFrame;
1455       } catch (std::exception &e) {
1456          std::cout << "** ENTERED RebuildWf//ErrorHandler." << std::endl;
1457          ErrorDesc = e.what();
1458 //          IvNotify(NOTIFY_Error, IvFmt("RebuildWf failed: %1", s2q(e.what())));
1459          // ^- can't call this outside the gui thread...
1460          ErrorOccured = true;
1461       }
1462       disconnect(&Log, SIGNAL(textEmitted(QString)), &pFrame->Log(), SLOT(appendText(QString)));
1463 
1464       if (ErrorOccured)
1465          break;
1466 
1467       Log.Write("\n");
1468       if (iFrame != m_Frames.size()-1)
1469          Log.endSection();
1470    }
1471 
1472    if (ErrorOccured) {
1473       ClearOrbitals(false);
1474       Log.EmitError("RebuildWf failed: " + ErrorDesc);
1475    }
1476 //    Log.Write("\nAll Frames Done.");
1477    //emit layoutChanged();
1478    //endResetModel();
1479    EndTotalReset();
1480 
1481    // this is to update what we can select in the UI.
1482    if (m_ActiveCol == -1)
1483       SetActiveCol(0);
1484    SetActiveRow(0);
1485 }
1486 
1487 
AcquireFrameObjectLock(FDataSetList & ObjectLock)1488 void FDocument::AcquireFrameObjectLock(FDataSetList &ObjectLock)
1489 {
1490    ObjectLock.clear();
1491    FFrameList::iterator
1492       itFrame;
1493    size_t
1494       nObjects = 0;
1495    for (itFrame = m_Frames.begin(); itFrame != m_Frames.end(); ++ itFrame)
1496       nObjects += (*itFrame)->m_Data.size();
1497    ObjectLock.reserve(nObjects);
1498    for (itFrame = m_Frames.begin(); itFrame != m_Frames.end(); ++ itFrame) {
1499       FDataSetList::iterator
1500          itData;
1501       for (itData = (*itFrame)->m_Data.begin(); itData != (*itFrame)->m_Data.end(); ++ itData)
1502          ObjectLock.push_back(*itData); // this copies the smart pointer in order to add a reference to it.
1503    }
1504 }
1505 
1506 
MakeOrbitalMoments()1507 void FDocument::MakeOrbitalMoments()
1508 {
1509    assert_rt(!"not implemented.");
1510 }
1511 
MakeOrbitalCharges()1512 void FDocument::MakeOrbitalCharges()
1513 {
1514    assert_rt(!"not implemented.");
1515 }
1516 
MakeOrbitalCachedData()1517 void FDocument::MakeOrbitalCachedData()
1518 {
1519    // should go through all frames and fix them up.
1520    MakeOrbitalMoments();
1521    MakeOrbitalCharges();
1522 }
1523 
BeginInsertFrames()1524 void FDocument::BeginInsertFrames()
1525 {
1526    // takes care of QAbstractTableModel interface requirements.
1527    if (m_CallEndInsertRows != -1)
1528       return IV_NOTIFY(NOTIFY_Error, "BeginInsertFrames already called");
1529    m_CallEndInsertRows = 0;
1530 
1531 }
1532 
InsertFrame(FFramePtr pFrame)1533 void FDocument::InsertFrame(FFramePtr pFrame)
1534 {
1535    // check if there was a last frame; either something already loaded
1536    // or something inserted before during the current insert operation block.
1537    FFramePtr
1538       pLastFrame(0);
1539    if (!m_FramesToInsert.empty())
1540       pLastFrame = m_FramesToInsert.back();
1541    else {
1542       if (!m_Frames.empty())
1543          pLastFrame = m_Frames.back();
1544    }
1545 
1546    // do some post-processing with actual computations...
1547    // Temporary data for visualizations, intermediate data
1548    // and characterizations of the orbitals, etc.
1549    {
1550       FMemoryStack2
1551 //          Mem(200000000); // ~200 mb
1552          Mem(size_t(m_pWfOptions->GetWorkSpaceMb())<<20ul);
1553       pFrame->RunIaoAnalysis(pFrame->Log(), m_pWfOptions, false, Mem); // false: do not make IBOs by default. Just read what's in the input.
1554       // (^- note: this is called for *all* loaded files.)
1555       pFrame->MakeOrbitalMoments(Mem);
1556       pFrame->LinkOrbitalsToPreviousFrame(pLastFrame.get(), Mem);
1557    }
1558 
1559    // remember the new frame, but don't insert it into the document just yet.
1560    // (doing that later simplifies dealing with the model interface)
1561    m_FramesToInsert.push_back(pFrame);
1562 }
1563 
1564 
EndInsertFrames()1565 void FDocument::EndInsertFrames()
1566 {
1567    // tell our connected clients that the data model's structure is about to change.
1568    bool emit1 = false;
1569    if (emit1) emit layoutAboutToBeChanged();
1570 
1571    if (m_CallEndInsertRows == -1)
1572       return IV_NOTIFY(NOTIFY_Error, "BeginInsertFrames not called");
1573    if (!m_FramesToInsert.empty()) {
1574       int
1575          nRowsOld = rowCount(),
1576          nRowsNew = nRowsOld;
1577 
1578       int
1579          iNewCol = (int)m_Frames.size();
1580       if (emit1) {
1581          beginInsertColumns(QModelIndex(), m_Frames.size(), m_Frames.size() + m_FramesToInsert.size() - 1); // last is exclusive.
1582          // count old number of rows and new number of rows.
1583          for (size_t iNewFrame = 0; iNewFrame < m_FramesToInsert.size(); ++iNewFrame)
1584             nRowsNew = std::max(nRowsOld, m_FramesToInsert[iNewFrame]->rowCount());
1585          if (nRowsNew != nRowsOld) {
1586             if (emit1) beginInsertRows(QModelIndex(), nRowsOld, nRowsNew - 1); // end exclusive.
1587             m_CallEndInsertRows = 1;
1588          }
1589       } else
1590          beginResetModel();
1591 
1592       // now actual insert of the new frames into the model
1593       m_Frames.insert(m_Frames.end(), m_FramesToInsert.begin(), m_FramesToInsert.end());
1594       m_FramesToInsert.clear();
1595 
1596       //emit dataChanged(createIndex(0, m_ActiveCol), createIndex(pFrameData->size()-1, m_ActiveCol));
1597       // ^- that cannot be emitted before the layout change is complete, can it?
1598       //    And anyway, is that required? Also, if I do the layoutChanged thing, do I
1599       //    still need to deal with rowsAboutToBeInserted etc?
1600       if (emit1) {
1601          if (m_CallEndInsertRows == 1)
1602             endInsertRows();
1603          endInsertColumns();
1604          emit layoutChanged();
1605       } else {
1606          endResetModel();
1607          emit layoutAboutToBeChanged();
1608          emit layoutChanged();
1609          // ^- FIXME: fix up connections in main and then remove these two.
1610       }
1611       // set the last row of the first frame as current.
1612 //       FDataSetList
1613 //          *pFrameData = &m_Frames.back()->m_Data;
1614 
1615       // switch to the last row of the first new frame as active.
1616 //       IvEmit("!!set active col/row: %1 %2", iNewCol, GetFrame(iNewCol)->rowCount() - 1);
1617       SetActiveCol(iNewCol);
1618 //       SetActiveRow(GetFrame(iNewCol)->rowCount() - 1);
1619 
1620 //1   SetActiveCol(0);
1621 //1   SetActiveRow(pFrameData->size() - 1);
1622    //    if (size_t(m_ActiveCol) >= m_Frames.size())
1623    //       m_ActiveCol = m_Frames.size() - 1;
1624    //    if (size_t(m_ActiveRow) >= GetCurrentFrame()->m_Data.size())
1625    //       m_ActiveRow = m_Frames.size() - 1;
1626    //    emit ActiveRowChanged(m_ActiveRow);
1627    //    emit ActiveColChanged(m_ActiveCol);
1628    //    int nRow1 = this->rowCount(), nCol1 = this->columnCount();
1629 //1      emit dataChanged(createIndex(0, m_ActiveCol), createIndex(pFrameData->size()-1, m_ActiveCol));
1630 //1      emit VisualRepresentationChanged();
1631    }
1632    m_CallEndInsertRows = -1;
1633 }
1634 
1635 
ConvertXmlSpinDecl(orbital_file::FOrbitalSpin s)1636 FOrbitalSpin ConvertXmlSpinDecl(orbital_file::FOrbitalSpin s)
1637 {
1638    switch (s) {
1639       case orbital_file::ORBSPIN_SpinFree: return ORBSPIN_SpinFree;
1640       case orbital_file::ORBSPIN_Alpha: return ORBSPIN_Alpha;
1641       case orbital_file::ORBSPIN_Beta: return ORBSPIN_Beta;
1642       case orbital_file::ORBSPIN_Unknown: return ORBSPIN_Unknown;
1643       default:
1644          assert(0);
1645          return ORBSPIN_Unknown;
1646    }
1647 }
1648 
LoadOrbitalFile(FFrameList & LoadedFrames,QString FileName)1649 bool FDocument::LoadOrbitalFile(FFrameList &LoadedFrames, QString FileName)
1650 {
1651    // at least the basic molpro XMLs made via "{put,xml,...}" currently only
1652    // have one frame per file. But they clearly could support more. So we
1653    // should keep this reasonably flexible.
1654 
1655    unsigned
1656       FileLoadFlags = 0;
1657    if (m_SkipVirtualOrbitals)
1658       FileLoadFlags |= orbital_file::LOADFILE_SkipVirtualOrbs;
1659    orbital_file::FMolproXmlDataPtr
1660       pXmlData;
1661    try {
1662       pXmlData = orbital_file::LoadOrbitalFile(q2s(FileName), orbital_file::FLoadOptions(FileLoadFlags));
1663    } catch (orbital_file::FFileTypeUnrecognizedError &e) {
1664       // return that this was not an orbital file. It is called from LoadFile,
1665       // which may now try some other formats.
1666       return false;
1667    } catch (std::runtime_error const &e) {
1668       IV_NOTIFY(NOTIFY_Error, "LoadOrbitalFile failed: " + s2q(e.what()));
1669       return true;
1670    }
1671    if (pXmlData.get() == 0) {
1672       IV_NOTIFY(NOTIFY_Error, "LoadOrbitalFile failed: " + QString("Unknown error during loading of '%s'").arg(FileName));
1673       return true;
1674    }
1675 
1676    FFramePtr
1677       pFrame(new IFrame(FileName, this));
1678 
1679    FDataSetList
1680       *pFrameData = &pFrame->m_Data;
1681    boost::intrusive_ptr<FGeometry>
1682       pGeometry = new FGeometry(RemovePath(FileName), pXmlData->pAtoms, this);
1683    pFrameData->push_back(pGeometry);
1684    pGeometry->Active = true;
1685 
1686    // insert the orbitals as data sets.
1687    for ( uint i = 0; i < pXmlData->pOrbSet->OrbInfos.size(); ++ i ) {
1688       orbital_file::FOrbitalInfo
1689          *pOrbInfo = &*pXmlData->pOrbSet->OrbInfos[i];
1690       FOrbitalInfo
1691          info = FOrbitalInfo(pOrbInfo->fEnergy, pOrbInfo->fOcc, pOrbInfo->iSym, ConvertXmlSpinDecl(pOrbInfo->Spin));
1692       info.iOriginalIndex = int(i);
1693 //       QString
1694 //          Desc = s2q(pOrbInfo->Desc());
1695       QString
1696          Desc = info.MakeDesc(s2q(pOrbInfo->Desc()));
1697       pFrameData->push_back( new FOrbital(Desc, pXmlData->pAtoms, this, pOrbInfo->pBasisSet,
1698             &pOrbInfo->Orb[0], info, 0, 0, 0) );
1699    }
1700 
1701    // insert the frame into the "new stuff" list.
1702    LoadedFrames.push_back(pFrame);
1703    return true;
1704 }
1705 
LoadXyzFile(FFrameList & LoadedFrames,QString FileName)1706 void FDocument::LoadXyzFile(FFrameList &LoadedFrames, QString FileName)
1707 {
1708    FBasisDescs
1709       DefaultBases;
1710    DefaultBases[BASIS_Orbital] = "def2-TZVP"; // FIXME: should be taken as property from somewhere...
1711    DefaultBases[BASIS_Guess] = "MINAO";
1712 
1713    ct::FAtomSetList
1714       AtomSets;
1715    try {
1716       ct::LoadMultiXyz(AtomSets, q2s(FileName), DefaultBases);
1717    } catch (FXyzLoadException &e) {
1718       IvNotify(NOTIFY_Error, s2q(e.what()));
1719       return;
1720    }
1721 
1722    ct::FAtomSetList::iterator
1723       itAtomSet;
1724    size_t
1725       nAtomSets = AtomSets.size(),
1726       iAtomSet = 0;
1727    _for_each(itAtomSet, AtomSets) {
1728       // use either .xyz file's name itself as frame name, or if the .xyz
1729       // contains multiple frames, then its file name with the frame id appended.
1730       QString
1731          FrameName;
1732       if (nAtomSets > 1)
1733          FrameName = QString("%1_%2").arg(FileName).arg((int)iAtomSet, 4, 10, QChar('0'));
1734       else
1735          FrameName = FileName;
1736       // make a new frame object, consisting of only a geometry as data set.
1737       FFramePtr
1738          pFrame(new IFrame(FrameName, this));
1739       FDataSetList
1740          *pFrameData = &pFrame->m_Data;
1741       boost::intrusive_ptr<FGeometry>
1742          pGeometry = new FGeometry(RemovePath(FrameName), *itAtomSet, this);
1743       pFrameData->push_back(pGeometry);
1744       pGeometry->Active = true;
1745       // insert the frame into the "new stuff" list.
1746       LoadedFrames.push_back(pFrame);
1747       ++ iAtomSet;
1748    }
1749 }
1750 
1751 
LoadFile(FFrameList & LoadedFrames,QString FileName)1752 void FDocument::LoadFile(FFrameList &LoadedFrames, QString FileName)
1753 {
1754    if (FileName != ":/!clipboard!") {
1755       // guess the file type based on file name and extension
1756       // and forward to appropriate load routine.
1757       QFileInfo
1758          FileInfo(FileName);
1759       // first.. is this file actually there and readable? if we handle this
1760       // here we can't mess it up in the sub-routines.
1761       if (!FileInfo.isFile())
1762          return IV_NOTIFY1(NOTIFY_Error, "LoadFile failed: Input '%1' is not a file.", FileName);
1763       if (!FileInfo.isReadable())
1764          return IV_NOTIFY1(NOTIFY_Error, "LoadFile failed: Input '%1' is a file, but not readable (permissions okay?).", FileName);
1765       // now let's see what we got...
1766       // (todo: it might be helpful to have a way of overwriting
1767       //  the default choice based on file extensions. But for now there are
1768       //  more important usability tasks to take care of.)
1769       IvNotify(NOTIFY_StartWork, IvFmt("Loading %1...", FileName));
1770       QString
1771          FileExt = FileInfo.suffix();
1772 //       if (FileExt == "xml" || FileExt == "molden")
1773 //          // note: LoadOrbitalFile can also look at the first line of the file to help with identification,
1774 //          //       maybe we should pass more stuff here and catch its exceptions?
1775 //          return LoadOrbitalFile(LoadedFrames, FileName);
1776       if (LoadOrbitalFile(LoadedFrames, FileName))
1777          return;
1778       if (FileExt == "xyz")
1779          return LoadXyzFile(LoadedFrames, FileName);
1780       return IV_NOTIFY1(NOTIFY_Error, "LoadFile failed: File extension of '%1' not recognized.", FileName);
1781    } else {
1782       // we currently only support loading .xyz files from clipboard data.
1783       return LoadXyzFile(LoadedFrames, FileName);
1784    }
1785 }
1786 
1787 
Load(QStringList FileNames)1788 void FDocument::Load(QStringList FileNames)
1789 {
1790    if (FileNames.empty())
1791       return;
1792 
1793 //    m_InputFileName = FileName;
1794    m_InputFileName = "";
1795    BeginInsertFrames();
1796    {
1797       FFrameList
1798          LoadedFrames;
1799       foreach(QString FileName, FileNames)
1800          LoadFile(LoadedFrames, FileName);
1801       if (LoadedFrames.size() > 1)
1802          IvNotify(NOTIFY_StartWork, IvFmt("Processing %1 frames...", LoadedFrames.size()));
1803       for (size_t iFrame = 0; iFrame < LoadedFrames.size(); ++iFrame)
1804          InsertFrame(LoadedFrames[iFrame]);
1805    }
1806 
1807    EndInsertFrames();
1808    IvNotify(NOTIFY_FinishWork, IvFmt("Finished loading %1 files.", FileNames.size()));
1809 
1810    emit ActiveColChanged(m_ActiveCol); // that's for updating the file name in the title...
1811 }
1812 
1813 
LinkOrbitals()1814 void FDocument::LinkOrbitals()
1815 {
1816    if (!HaveOrbitals())
1817       return;
1818    ClearOrbitalRepresentations();
1819 
1820    FFrame
1821       *pLastFrame = 0;
1822    FMemoryStack2
1823       Mem(size_t(m_pWfOptions->GetNumThreads() * m_pWfOptions->GetWorkSpaceMb())<<20);
1824    for (size_t iFrame = 0; iFrame < m_Frames.size(); ++ iFrame) {
1825       FFrame
1826          *pFrame = GetFrame(iFrame);
1827       FLogQt
1828          &Log = pFrame->Log();
1829       Log.Write("*** ORBITAL LINK TO PREV. FRAME", q2s(RemovePath(pFrame->GetFullInputFileName())), iFrame+1, m_Frames.size());
1830       pFrame->LinkOrbitalsToPreviousFrame(pLastFrame, Mem);
1831       pLastFrame = pFrame;
1832    }
1833 }
1834 
1835 
ClearOrbitalRepresentations()1836 void FDocument::ClearOrbitalRepresentations()
1837 {
1838    for (int iFrame = 0; iFrame < GetNumFrames(); ++ iFrame){
1839       FDataSetList
1840          *pData = GetFrameData(iFrame);
1841       if (pData) {
1842          for (int iRow = 0; size_t(iRow) < pData->size(); ++ iRow) {
1843             (*pData)[iRow]->InvalidateRenderCache();
1844          }
1845       }
1846    }
1847 }
1848 
1849 
ReorderOrRestrictFrameSet(FFrameIndexList const & iNewIndices)1850 void FDocument::ReorderOrRestrictFrameSet(FFrameIndexList const &iNewIndices)
1851 {
1852    // check if the currently active column is still in the new frame set.
1853    // if yes, find its new frame number.
1854    int iPrevActiveCol = GetActiveColIndex();
1855    int iNewActiveCol = -1;
1856    for (size_t i = 0; i < iNewIndices.size(); ++ i)
1857       if (iNewIndices[i] == iPrevActiveCol)
1858          iNewActiveCol = int(i);
1859    emit layoutAboutToBeChanged();
1860 
1861    FFrameList
1862       NewFrames;
1863    NewFrames.reserve(iNewIndices.size());
1864    for (size_t i = 0; i < iNewIndices.size(); ++ i) {
1865       size_t
1866          iFrame = size_t(iNewIndices[i]);
1867       if (iFrame < m_Frames.size()) {
1868          NewFrames.push_back(m_Frames[iFrame]);
1869       } else {
1870          IvNotify(NOTIFY_Warning, IvFmt("Attempted to include frame %1 in new frame order, but there is no such frame (have: %2). Ignored.", int(iFrame), int(m_Frames.size())));
1871       }
1872    }
1873    m_Frames.swap(NewFrames);
1874 
1875    emit layoutChanged();
1876 
1877    SetActiveCol(iNewActiveCol); // might still be -1. In this case deselect.
1878 }
1879 
1880 
1881 
FChargeAnalysis(ct::FAtomSet * pAtoms,FAtomIdList * pSelectedAtoms)1882 FChargeAnalysis::FChargeAnalysis(ct::FAtomSet *pAtoms, FAtomIdList *pSelectedAtoms)
1883    : m_RestrictAtoms(pSelectedAtoms != 0),
1884      m_Atoms(*pAtoms)
1885 {
1886    if (m_RestrictAtoms) {
1887       m_SelectedAtoms.insert(m_SelectedAtoms.end(), pSelectedAtoms->begin(), pSelectedAtoms->end());
1888       std::sort(m_SelectedAtoms.begin(), m_SelectedAtoms.end());
1889    }
1890 
1891 }
1892 
~FChargeAnalysis()1893 FChargeAnalysis::~FChargeAnalysis()
1894 {
1895 }
1896 
IsIncluded(int iAt) const1897 bool FChargeAnalysis::IsIncluded(int iAt) const
1898 {
1899    if (!m_RestrictAtoms)
1900       return true;
1901    FAtomIdList::const_iterator
1902       it = std::lower_bound(m_SelectedAtoms.begin(), m_SelectedAtoms.end(), iAt);
1903    return (it != m_SelectedAtoms.end() && iAt == *it);
1904 }
1905 
CountAtoms() const1906 int FChargeAnalysis::CountAtoms() const
1907 {
1908    std::set<int>
1909       iAtoms;
1910    for (FMap::const_iterator it = m_Data.begin(); it != m_Data.end(); ++ it) {
1911       iAtoms.insert(it->first.iAt);
1912    }
1913    return int(iAtoms.size());
1914 }
1915 
FindMaxL() const1916 int FChargeAnalysis::FindMaxL() const
1917 {
1918    std::set<int>
1919       iAngMoms;
1920    for (FMap::const_iterator it = m_Data.begin(); it != m_Data.end(); ++ it) {
1921       iAngMoms.insert(it->first.AngMom);
1922    }
1923    if (iAngMoms.empty())
1924       return 0;
1925    return *iAngMoms.rbegin();
1926 }
1927 
bNonzeroSpin() const1928 bool FChargeAnalysis::bNonzeroSpin() const
1929 {
1930    for (FMap::const_iterator it = m_Data.begin(); it != m_Data.end(); ++ it)
1931       if (it->second.fSpin != 0.)
1932          return true;
1933    return false;
1934 }
1935 
Add(int iAt,int AngMom,double fCharge,double fSpin)1936 void FChargeAnalysis::Add(int iAt, int AngMom, double fCharge, double fSpin)
1937 {
1938    if (!IsIncluded(iAt))
1939       return;
1940    assert(AngMom >= 0);
1941    if (AngMom < 0)
1942       AngMom = 0;
1943    FKey
1944       key(iAt, AngMom);
1945    FMap::iterator
1946       it = m_Data.find(key);
1947    if (it == m_Data.end()) {
1948       std::pair<FMap::iterator, bool>
1949          itb = m_Data.insert(FMap::value_type(key, FValue(0.,0.)));
1950       it = itb.first;
1951       assert(it != m_Data.end());
1952    }
1953 
1954    it->second.fCharge += fCharge;
1955    it->second.fSpin += fSpin;
1956 }
1957 
AtomLabel(int iAt) const1958 std::string FChargeAnalysis::AtomLabel(int iAt) const
1959 {
1960    fmt::MemoryWriter
1961       out;
1962    if (iAt < 0 || size_t(iAt) >= m_Atoms.size())
1963       out << "?? ";
1964    else
1965       out.write("{:>2} ", m_Atoms[size_t(iAt)].GetElementName());
1966    out.write("{:3}", iAt+1);
1967    return out.str();
1968 }
1969 
MakeAtomTotalCharge(int iAt,double fCharge) const1970 double FChargeAnalysis::MakeAtomTotalCharge(int iAt, double fCharge) const
1971 {
1972    double
1973       fAtomElem = 0;
1974    if (iAt >= 0 && size_t(iAt) < m_Atoms.size())
1975       fAtomElem = double(m_Atoms[size_t(iAt)].Charge);
1976    else
1977       return -fCharge;
1978    double
1979       fReducedCharge = fAtomElem - fCharge;
1980    // ^- does not take account of ECPs. Apply a hack to guess the right
1981    //    ECP charge...  we just take the charge which is closest to 0.
1982    //    (of course this won't work with ECP2s etc. But the import formats
1983    //    often do not have any ECP information, so without it we cannot
1984    //    really do better.)
1985    double
1986       fReducedChargeWithPutativeEcp = fReducedCharge - (double)iLikelyEcpCharge(m_Atoms[iAt].AtomicNumber);
1987    if (std::abs(fReducedChargeWithPutativeEcp) < std::abs(fReducedCharge))
1988       fReducedCharge = fReducedChargeWithPutativeEcp;
1989    return fReducedCharge;
1990 }
1991 
MakeReport(ct::FLog & Log)1992 void FChargeAnalysis::MakeReport(ct::FLog &Log)
1993 {
1994    int
1995       nMaxL = FindMaxL(),
1996       nAtoms = CountAtoms();
1997    bool
1998       HaveSpin = bNonzeroSpin();
1999    for (int iSpinCase = 0; iSpinCase != 1 + int(HaveSpin); ++ iSpinCase)
2000    {
2001       double
2002          fElecTotal = 0.,
2003          fChargeTotal = 0.;
2004       if (iSpinCase == 0)
2005          Log.Write(" Total charge composition:\n");
2006       else
2007          Log.Write(" Spin density composition:\n");
2008 
2009       {  // make table caption
2010          fmt::MemoryWriter
2011             out;
2012          out << "   CEN ATOM  ";
2013          for (int i = 0; i <= nMaxL; ++ i)
2014             out.write("       {}   ", "spdfghiklm"[i]);
2015          out << "    ELECTRONS";
2016          if (iSpinCase == 0)
2017             out << " ->P.CHARGE";
2018          else
2019             out << " ->SPIN.DEN";
2020          Log.Write(out.str());
2021       }
2022       char const
2023          *pChargeFmtF = " {:10.5f}",
2024          *pChargeFmtS = " {:10}";
2025       std::vector<double>
2026          AmCharges(size_t(nMaxL+1), 0.);
2027       FMap::const_iterator
2028          itBeg, itEnd;
2029       for (itBeg = m_Data.begin(); itBeg != m_Data.end(); ) {
2030          // clear per-angular momentum charge data.
2031          for (size_t i = 0; i < AmCharges.size(); ++ i)
2032             AmCharges[i] = 0.;
2033 
2034          int
2035             iAt = itBeg->first.iAt,
2036             iAtMaxL = 0;
2037          double
2038             fAtomTotal = 0.;
2039          // find all angular momentum entries on the current atom.
2040          itEnd = itBeg;
2041          while (itEnd != m_Data.end() && itEnd->first.iAt == iAt) {
2042             double f = (iSpinCase==0)? itEnd->second.fCharge : itEnd->second.fSpin;
2043             iAtMaxL = std::max(iAtMaxL, int(itEnd->first.AngMom));
2044             AmCharges[size_t(itEnd->first.AngMom)] += f;
2045             fAtomTotal += f;
2046             ++ itEnd;
2047          }
2048 
2049          fElecTotal += fAtomTotal;
2050          double
2051             fAtomCharge;
2052          if (iSpinCase == 0)
2053             fAtomCharge = MakeAtomTotalCharge(iAt, fAtomTotal);
2054          else
2055             fAtomCharge = fAtomTotal;
2056          fChargeTotal += fAtomCharge;
2057 
2058          fmt::MemoryWriter
2059             out;
2060          out.write("  {:>4} {:>3}   ", iAt+1, m_Atoms[iAt].GetElementName());
2061          for (size_t i = 0; i <= size_t(iAtMaxL); ++i)
2062             out.write(pChargeFmtF, AmCharges[i]);
2063          for (size_t i = size_t(iAtMaxL + 1); i < AmCharges.size(); ++ i)
2064             out.write(pChargeFmtS, "");
2065          out << "  ";
2066          out.write(pChargeFmtF, fAtomTotal);
2067          out.write(pChargeFmtF, fAtomCharge);
2068 
2069          Log.Write(out.str());
2070          // continue with next atom.
2071          itBeg = itEnd;
2072       }
2073       Log.WriteLine();
2074       if (nAtoms != 0) {
2075          fmt::MemoryWriter
2076             out;
2077          out << "  -> Total" << "     ";
2078          for (size_t i = 0; i < AmCharges.size(); ++ i)
2079             out.write(pChargeFmtS, "");
2080          out.write(pChargeFmtF, fElecTotal);
2081          out.write(pChargeFmtF, fChargeTotal);
2082          Log.Write(out.str());
2083          Log.WriteLine();
2084       }
2085    }
2086 }
2087 
RunChargeAnalysis(ct::FLog & Log,FAtomIdList * pSelectedAtoms)2088 void FFrame::RunChargeAnalysis(ct::FLog &Log, FAtomIdList *pSelectedAtoms)
2089 {
2090    FAtomSet
2091       *pAtoms = pGetAtoms();
2092    if (pAtoms == 0) {
2093       Log.Write(" No atoms.");
2094       return;
2095    }
2096    FChargeAnalysis
2097       ChargeAnalysis(pAtoms, pSelectedAtoms);
2098    FDataSetList::iterator
2099       itDataSet;
2100    for (itDataSet = m_Data.begin(); itDataSet != m_Data.end(); ++ itDataSet) {
2101       FOrbital
2102          *pOrb = dynamic_cast<FOrbital*>(itDataSet->get());
2103       if (!pOrb)
2104          continue;
2105       pOrb->AddChargeContributions(ChargeAnalysis);
2106    }
2107    ChargeAnalysis.MakeReport(Log);
2108 }
2109 
2110 
fChargeFactor() const2111 double FOrbitalInfo::fChargeFactor() const
2112 {
2113    return fOcc;
2114 }
2115 
fSpinFactor() const2116 double FOrbitalInfo::fSpinFactor() const
2117 {
2118    if (Spin == ORBSPIN_Alpha)
2119       return +fOcc;
2120    if (Spin == ORBSPIN_Beta)
2121       return -fOcc;
2122    return 0.;
2123    // ^- note: this is not quite right for active orbitals with MCSCF.. but we
2124    //    can't do much about it with the information we get from imported files.
2125 }
2126 
2127 
AddChargeContributions(FChargeAnalysis & Charges) const2128 void FOrbital::AddChargeContributions(FChargeAnalysis &Charges) const
2129 {
2130    if (pIaoCoeffs.empty() || pMinBasis.get() == 0)
2131       return;
2132    double
2133       fCharge = info.fChargeFactor(),
2134       fSpin = info.fSpinFactor();
2135    if (fCharge == 0. && fSpin == 0.)
2136       return; // nothing to add (e.g., virtual orbital.)
2137    uint
2138       iShOf = 0;
2139    for (uint iSh = 0; iSh < pMinBasis->Shells.size(); ++ iSh) {
2140       FBasisShell
2141          &Sh = pMinBasis->Shells[iSh];
2142       uint
2143          nShFn = Sh.nFn();
2144       double
2145          fChg = 0;
2146       for (uint iFn = 0; iFn < nShFn; ++ iFn)
2147          fChg += sqr(pIaoCoeffs[iFn + iShOf]);
2148       assert(size_t(Sh.iCenter) < pAtoms->size());
2149       Charges.Add(Sh.iCenter, Sh.l(), fChg*fCharge, fChg*fSpin);
2150       iShOf += nShFn;
2151    }
2152    assert(iShOf == pMinBasis->nFn());
2153 }
2154 
2155 
MakeIaoCharges(bool UseOccupationNumbers) const2156 TArray<double> FOrbital::MakeIaoCharges(bool UseOccupationNumbers) const
2157 {
2158    if (pIaoCoeffs.empty() || pMinBasis.get() == 0)
2159       return TArray<double>();
2160    TArray<double>
2161       Out;
2162    double
2163       fOcc1 = 1.;
2164    if (UseOccupationNumbers)
2165       fOcc1 = info.fOcc;
2166    Out.resize(pAtoms->size(), 0.);
2167    uint
2168       iShOf = 0;
2169    for (uint iSh = 0; iSh < pMinBasis->Shells.size(); ++ iSh) {
2170       FBasisShell
2171          &Sh = pMinBasis->Shells[iSh];
2172       uint
2173          nShFn = Sh.nFn();
2174       double
2175          fChg = 0;
2176       for (uint iFn = 0; iFn < nShFn; ++ iFn)
2177          fChg += sqr(pIaoCoeffs[iFn + iShOf]);
2178       assert(size_t(Sh.iCenter) < pAtoms->size());
2179 //       Out[Sh.iCenter] += info.fOcc * fChg;
2180       Out[Sh.iCenter] += fOcc1 * fChg;
2181       iShOf += nShFn;
2182    }
2183    assert(iShOf == pMinBasis->nFn());
2184    return Out;
2185 }
2186 
2187 // fix up phase a vector such it has its largest element (in absolute terms) positive.
FixVectorPhase(double * p,size_t n)2188 void FixVectorPhase(double *p, size_t n)
2189 {
2190    if (n == 0)
2191       return;
2192    double
2193       fMax = 0.;
2194    size_t
2195       iMax = 0;
2196    for (size_t i = 0; i < n; ++ i) {
2197       double fAbs = std::abs(p[i]);
2198       if (fAbs > fMax) {
2199          iMax = i;
2200          fMax = fAbs;
2201       }
2202    }
2203    if (p[iMax] < 0)
2204       for (size_t i = 0; i < n; ++ i)
2205          p[i] *= -1;
2206 }
2207 
2208 
FindAligningTrafo(double pR[9],double pD[3],FVector3 const * pAtPos,double const * pAtMass,FVector3 const * pAtPosLast,uint nAt,FMemoryStack & Mem)2209 static void FindAligningTrafo(double pR[9], double pD[3], FVector3 const *pAtPos, double const *pAtMass, FVector3 const *pAtPosLast, uint nAt, FMemoryStack &Mem)
2210 {
2211    double
2212       TotalMass = 0,
2213       pIm[9] = {0}; // inertial tensor.
2214    FVector3
2215       vMassCenter(0.,0.,0.);
2216    // compute total mass and center of mass
2217    for (uint iAt = 0; iAt < nAt; ++ iAt) {
2218       TotalMass += pAtMass[iAt];
2219       vMassCenter += pAtMass[iAt] * pAtPos[iAt];
2220    }
2221    vMassCenter /= TotalMass;
2222 
2223    // copy center translation
2224    for (uint i = 0; i < 3; ++ i)
2225       pD[i] = vMassCenter[i];
2226 
2227    if (pAtPosLast == 0) {
2228       // no reference frame given.
2229 
2230       // compute inertial tensor around the center of mass.
2231       for (uint iAt = 0; iAt < nAt; ++ iAt) {
2232          FVector3 const
2233             vAtPos = pAtPos[iAt] - vMassCenter;
2234          double
2235             Rsq = Dot(vAtPos,vAtPos);
2236          for (uint i = 0; i < 3; ++ i)
2237             for (uint j = 0; j < 3; ++ j)
2238                pIm[i + 3*j] += pAtMass[iAt] * ((i==j? 1.:0.)*Rsq - vAtPos[i]*vAtPos[j]);
2239    //             I += ElementMasses[o.iElement]*(np.eye(3)*np.dot(Xyz,Xyz) - np.outer(Xyz,Xyz))
2240       }
2241 
2242       // align along axes of inertia--largest moment along z (this way flat
2243       // molecules will be aligned in the x/y plane).
2244       for (uint i = 0; i < 9; ++ i)
2245          pR[i] = +pIm[i];
2246       double
2247          pEw[3];
2248       FMatrixView
2249          mR(pR, 3, 3);
2250       ct::Diagonalize(mR, pEw, Mem);
2251       // fix up phases of the eigenvectors, such that they have their largest element positive.
2252       for (uint iEv = 0; iEv < 3; ++ iEv)
2253          FixVectorPhase(&mR(0,iEv), 3);
2254 
2255       // check the determinant of the transformation to see if it includes an
2256       // inversion. If yes, invert one of the axes. We'd get the wrong stero-isomers
2257       // otherwise...
2258       if (1) {
2259          FVector3
2260             v0(mR(0,0), mR(1,0), mR(2,0)),
2261             v1(mR(0,1), mR(1,1), mR(2,1)),
2262             v2(mR(0,2), mR(1,2), mR(2,2));
2263          if (Dot(v0, Cross(v1,v2)) < 0.) {
2264             for (size_t i = 0; i < 3; ++ i)
2265                mR(i,2) *= -1.;
2266          }
2267       }
2268    } else {
2269       // compute overlap matrix between current and last frame (assuming that the last one
2270       // has already been moved into its own center of mass)
2271       for (uint iAt = 0; iAt < nAt; ++ iAt) {
2272          FVector3 const
2273             vAtPosI = pAtPos[iAt] - vMassCenter,
2274             vAtPosJ = pAtPosLast[iAt];
2275          for (uint i = 0; i < 3; ++ i)
2276             for (uint j = 0; j < 3; ++ j)
2277                pIm[i + 3*j] += pAtMass[iAt] * vAtPosI[i] * vAtPosJ[j];
2278       }
2279       double
2280          pU[9], pVt[9], pSig[3];
2281       FMatrixView
2282          mR(pR, 3, 3),
2283          mU(pU, 3, 3),
2284          mI(pIm, 3, 3),
2285          mVt(pVt, 3, 3);
2286       ComputeSvd(mU, pSig, mVt, mI, Mem);
2287       Mxm(mR, mU, mVt);
2288       // ^- I think it should be the other way around, but there is a strange Transpose3x3
2289       //    in the actual transformation routine...
2290    }
2291 
2292 }
2293 
2294 
FFrameCoords(FGeometry * pGeometry)2295 FFrameCoords::FFrameCoords(FGeometry *pGeometry)
2296    : pAtoms(&*pGeometry->pAtoms),
2297      pDocument(pGeometry->GetDocument())
2298 {
2299    // copy assemble positions and weights & copy into continuous vectors.
2300    pAtMass.reserve(pAtoms->size());
2301    pAtPos.reserve(pAtoms->size());
2302    QString
2303       WeightMode = pDocument->GetAtomWeightMode();
2304    for (size_t iAt = 0; iAt < pAtoms->size(); ++ iAt) {
2305       if ((pDocument->AtomFlags(iAt) & ATOM_NoAlign) != 0)
2306          // ignore this atom in setting up the transformation.
2307          continue;
2308       double
2309          AtMass = 1.;
2310       int
2311          iElement = (*pAtoms)[iAt].AtomicNumber;
2312       if (WeightMode == "charge" || WeightMode == "by_charge") {
2313          AtMass = double(iElement);
2314       } else if (WeightMode == "mass" || WeightMode == "by_mass") {
2315          AtMass = GetAtomicMass(iElement, ATMASS_StandardAtomicWeight);
2316       } else if (WeightMode == "iso_mass") {
2317          AtMass = GetAtomicMass(iElement, ATMASS_MostCommonIsotope);
2318       } else if (WeightMode == "none" || WeightMode == "coords") {
2319          AtMass = 1.;
2320       } else {
2321          IV_NOTIFY1(NOTIFY_Warning, "Can only align frames by 'mass', 'iso-mass', 'charge', and 'coords'. Alignment mode '%1' not recognized.", WeightMode);
2322       }
2323 
2324       pAtMass.push_back(AtMass);
2325       pAtPos.push_back((*pAtoms)[iAt].vPos);
2326    }
2327 
2328    assert(pAtMass.size() == pAtPos.size());
2329 }
2330 
~FFrameCoords()2331 FFrameCoords::~FFrameCoords()
2332 {}
2333 
FindAligningTrafo(double pR[9],double pD[3],FFrameCoords * pThis,FFrameCoords * pLast,FMemoryStack & Mem)2334 void FDocument::FindAligningTrafo(double pR[9], double pD[3], FFrameCoords *pThis, FFrameCoords *pLast, FMemoryStack &Mem)
2335 {
2336    if (pThis->empty()) {
2337 //       std::cerr << "WARNING: Current frame has no atoms. Nothing to align to!" << std::endl;
2338       IV_NOTIFY(NOTIFY_Warning, "Current frame has no atoms. Nothing to align to!");
2339       // return an identity transformation.
2340       memset(pR, 0, sizeof(*pR)*9);
2341       for (uint i = 0; i < 3; ++ i) {
2342          pR[4*i] = 1.;
2343          pD[i] = 0.;
2344       }
2345    } else {
2346       // make the actual transformation.
2347       if (pLast) {
2348          if (pLast->pAtMass.size() != pThis->pAtMass.size())
2349             IvNotify(NOTIFY_Error, "FindAligningTrafo: Size mismatch in frame alignment between current and last frame.");
2350       }
2351       ::FindAligningTrafo(pR, pD, &pThis->pAtPos[0], &pThis->pAtMass[0], pLast? &(pLast->pAtPos[0]) : 0, uint(pThis->pAtMass.size()), Mem);
2352    }
2353 }
2354 
2355 
2356 // find index in p[i] with largest absolute value.
2357 template <class FScalar>
ArgAbsMax(FScalar const * p,size_t n,size_t nStride=1)2358 static size_t ArgAbsMax(FScalar const *p, size_t n, size_t nStride = 1) {
2359    assert(n != 0); // not defined in this case.
2360    FScalar
2361       fMax = std::abs(p[0]);
2362    size_t
2363       iMax = 0;
2364    for (size_t i = 1; i != n; ++ i) {
2365       FScalar
2366          f = std::abs(p[i*nStride]);
2367       if (f > fMax) {
2368          fMax = f;
2369          iMax = i;
2370       }
2371    }
2372    return iMax;
2373 }
2374 
TransformOrbBasisAndCoeffs(FBasisSet * & pCommonBasis,FBasisSetPtr & pBasis,TArray<double> & pCoeffs,double R[9],double d[3],FMemoryStack & Mem)2375 static void TransformOrbBasisAndCoeffs(FBasisSet *&pCommonBasis, FBasisSetPtr &pBasis, TArray<double> &pCoeffs, double R[9], double d[3], FMemoryStack &Mem)
2376 {
2377    // transform the basis set(s), unless they are shared and already have
2378    // been transformed.
2379    if (pCommonBasis && pCommonBasis != pBasis.get())
2380       throw std::runtime_error("FDocument::AlignFrames: expected basis sets to be shared, but found something strange.");
2381    else if (pCommonBasis == 0) {
2382       pCommonBasis = pBasis.get();
2383       pBasis->Transform_3x4(R, d, Mem);
2384    }
2385 
2386    // transform the orbital's MO coefficients.
2387    pBasis->TransformMoCoeffs_3x4(FMatrixView(&pCoeffs[0], pBasis->nFn(), 1), R, Mem);
2388 }
2389 
Transpose3x3(double * R)2390 static void Transpose3x3(double *R) {
2391    std::swap(R[0 + 3*1], R[1 + 3*0]);
2392    std::swap(R[0 + 3*2], R[2 + 3*0]);
2393    std::swap(R[1 + 3*2], R[2 + 3*1]);
2394 }
2395 
MakeFrameCoords(int iFrame)2396 FFrameCoordsPtr FDocument::MakeFrameCoords(int iFrame)
2397 {
2398    if (iFrame < 0 || size_t(iFrame) >= m_Frames.size())
2399       return 0;
2400    FDataSetList
2401       &FrameData = m_Frames[iFrame]->m_Data;
2402    FGeometry
2403       *pGeometry = 0;
2404    if (!FrameData.empty())
2405       pGeometry = dynamic_cast<FGeometry*>(FrameData[0].get());
2406    if (pGeometry == 0) {
2407       IV_NOTIFY(NOTIFY_Warning, IvFmt("WARNING: Frame %1 has no geometry to align! Transformation skipped.", iFrame));
2408       return 0;
2409    }
2410    assert(pGeometry != 0);
2411 
2412    FFrameCoordsPtr
2413       pThis = new FFrameCoords(pGeometry);
2414    return pThis;
2415 }
2416 
2417 
AlignFrames(QString Mode)2418 void FDocument::AlignFrames(QString Mode)
2419 {
2420 //    IvEmit("AlignFrames invoked!");
2421    FMemoryStack2
2422       Mem(20000000); // ~20 MB.
2423 
2424    if (!Mode.isEmpty())
2425       SetAtomWeightMode(Mode);
2426 
2427    double
2428       LastEvs[9] = {0};
2429    FFrameCoordsPtr
2430       pLast;
2431    for (uint iFrame = 0; iFrame < m_Frames.size(); ++ iFrame) {
2432       FDataSetList
2433          &FrameData = m_Frames[iFrame]->m_Data;
2434       FFrameCoordsPtr
2435          pThis = MakeFrameCoords(iFrame);
2436       if (pThis == 0) {
2437          IV_NOTIFY(NOTIFY_Warning, IvFmt("WARNING: Frame %1 has no geometry to align! Transformation skipped.", iFrame));
2438          continue;
2439       }
2440 
2441       double
2442          R[9], d[3];
2443 //       ct::PrintMatrixGen(std::cout, R, 3, 1, 3, 3, "Aligning Trafo (in)");
2444       FindAligningTrafo(R, d, pThis.get(), pLast.get(), Mem);
2445 //       ct::PrintMatrixGen(std::cout, R, 3, 1, 3, 3, "Aligning Trafo (out)");
2446 #if 0
2447       if (iFrame != 0) {
2448          // in semi-degenerate cases some axes might have been swapped. align transformation with
2449          // previous transformation (technically we should do this in each degenerate subspace
2450          // individually... but atm I do not care sufficiently).
2451          FMatrixView
2452             mLastR(&LastEvs[0],3,3),
2453             mCurR(&R[0],3,3);
2454          FStackMatrix
2455             S(3,3, &Mem);
2456          Mxm(S, Transpose(mCurR), mLastR);
2457          bool
2458             iUsed[3] = {false,false,false};
2459          uint
2460             iOrd[3];
2461          for (uint iEv = 0; iEv < 3; ++ iEv) {
2462             // find other vector with largest alignment to previous ev.
2463             uint iLast;
2464             for (;;) {
2465                iLast = uint(ArgAbsMax(&S(0,iEv), 3));
2466                if (iUsed[iLast])
2467                   // already used for another dimension
2468                   S(iLast,iEv) = 0.;
2469                else {
2470                   // found one.
2471                   iUsed[iLast] = true;
2472                   break;
2473                }
2474             }
2475             iOrd[iEv] = iLast;
2476          }
2477          double
2478             R_Orderd[9];
2479          for (uint iEv = 0; iEv < 3; ++ iEv)
2480             for (uint iComp = 0; iComp != 3; ++ iComp)
2481                R_Orderd[iComp + 3*iEv] = R[iComp + 3*iOrd[iEv]];
2482          // ^- FIXME: this entire alignment stuff was written after 12hr of work.
2483          //    Chances are that there are lots of errors. Check when given time.
2484          assert(sizeof(R) == sizeof(R_Orderd));
2485          memcpy(R, R_Orderd, sizeof(R));
2486       }
2487 #endif
2488       // FIXME: DO THIS ALIGNING STUFF BEFORE FINDING MATCHING ORBITALS AND MAKING THE MULTIPOLE MOMENTS!!!
2489       //
2490       // Note: We could easily store atomic flags of atoms we do not yet have loaded.
2491       //       The flags are stored as a map anyway!
2492       //
2493       // .. hm, I think it may be too complicated. May be better to postpone the
2494       //    /matching orbital visual link determination, and to allow calling it
2495       //    manually. Would anyway be the required way if making orbitals ourselfes.
2496 
2497       // transform the original atom set (should be shared in all other data sets, too).
2498       bool
2499          t3x3 = true; // <- I wonder why I put this here.
2500       if (t3x3)
2501          Transpose3x3(R);
2502       IvEmit("* Aligned frame %1 (Weight = %3, pAtoms = %2)\n", iFrame, fmtp(pThis->pAtoms), GetAtomWeightMode());
2503       ct::PrintMatrixGen(std::cout, R, 3, 1, 3, 3, "Aligning trafo");
2504       pThis->pAtoms->Transform_3x4(R, d, Mem);
2505 
2506       FBasisSet
2507          *pCommonOrbBasis = 0,
2508          *pCommonMinBasis = 0;
2509       // transform the basis sets and the MO coefficients (basis sets should be shared, too).
2510       for (uint iMo = 0; iMo != FrameData.size(); ++ iMo) {
2511          FOrbital
2512             *pOrb = dynamic_cast<FOrbital*>(FrameData[iMo].get());
2513          if (pOrb == 0)
2514             continue; // that's not an orbital.
2515          TransformOrbBasisAndCoeffs(pCommonOrbBasis, pOrb->pBasisSet, pOrb->pCoeffs, R, d, Mem);
2516          TransformOrbBasisAndCoeffs(pCommonMinBasis, pOrb->pMinBasis, pOrb->pIaoCoeffs, R, d, Mem);
2517          pOrb->HaveMoments = false;
2518          // ^- precomputed values are broken now.
2519 
2520          // fixme: call pFrame->LinkOrbitalsToPreviousFrame(pLastFrame, Mem);
2521          // again.
2522       }
2523       if (t3x3)
2524          Transpose3x3(R);
2525 
2526       // TODO: add check if there are other data sets which are neither orbitals nor
2527       // geometries...
2528 
2529       assert(sizeof(R) == sizeof(LastEvs));
2530       memcpy(LastEvs, R, sizeof(R));
2531 //       pLast = pThis;
2532       pLast = MakeFrameCoords(iFrame); // rebuild with updated coordinates?
2533       // pThis still has the ones used before the alignment!
2534    }
2535 }
2536 
2537 
2538 
2539 
2540 
2541 // Set Out := L^T In R.
BasisChange2(FMatrixView & Out,FMatrixView const & L,FMatrixView const & In,FMatrixView const & R,FMemoryStack & Mem)2542 void BasisChange2( FMatrixView &Out, FMatrixView const &L,
2543     FMatrixView const &In, FMatrixView const &R, FMemoryStack &Mem)
2544 {
2545     assert( L.nRows == In.nRows && R.nRows == In.nCols );
2546 //     Out = FMatrixView( 0, L.nCols, R.nCols );
2547 //     Mem.Alloc(Out.pData, Out.GetStridedSize());
2548     assert( Out.nRows == L.nCols && Out.nCols == R.nCols );
2549 
2550     if ( L.nRows * L.nCols <=  R.nRows * R.nCols ) {
2551         // T1 := L^T In
2552         FStackMatrix
2553             T1(L.nCols, In.nCols, &Mem);
2554         Mxm(T1, Transpose(L), In);
2555         Mxm(Out, T1, R);
2556     } else {
2557         // T1 := In * R
2558         FStackMatrix
2559             T1(In.nRows, R.nCols, &Mem);
2560         Mxm(T1, In, R);
2561         Mxm(Out, Transpose(L), T1);
2562     }
2563 
2564     if ( L.pData == R.pData && In.IsSymmetric(1e-10) )
2565        Symmetrize(Out);
2566 }
2567 
GetActiveDataSet()2568 FDataSetPtr FDocument::GetActiveDataSet()
2569 {
2570    return GetRow(m_ActiveRow, false);
2571 };
2572 
ToggleActiveDataRow()2573 void FDocument::ToggleActiveDataRow()
2574 {
2575    ToggleDataRow(m_ActiveRow);
2576 }
2577 
ToggleDataRow(int iIndex)2578 void FDocument::ToggleDataRow(int iIndex)
2579 {
2580 //    xout << boost::format("FDocument::ToggleDataRow(): On entry m_ActiveRow = %i") % m_ActiveRow << std::endl;
2581    // toggle the row in all frames.
2582    bool
2583       ExistedSomewhere = false;
2584    for (unsigned iFrame = 0; iFrame < m_Frames.size(); ++ iFrame) {
2585       FFrame *pFrame = GetFrame(iFrame);
2586       if (iIndex >= 0 && size_t(iIndex) < pFrame->m_Data.size() ) {
2587          pFrame->m_Data[iIndex]->Active = !pFrame->m_Data[iIndex]->Active;
2588          ExistedSomewhere = true;
2589       }
2590    }
2591 
2592    if (!ExistedSomewhere) {
2593       xout << "document: tried to toggle an invalid data entry " << iIndex << ". Does not exist in any frame." << std::endl;
2594 //       xout << "document: tried to toggle an invalid data entry " << iIndex << ". Have only " << m_Data.size() << "." << std::endl;
2595    }
2596 
2597    // note: emit dataChanged first to render the orbital; otherwise the visual info would not actually be there.
2598    // (yes, it's hacky.)
2599    if (ExistedSomewhere) {
2600       emit dataChanged(createIndex(m_ActiveRow,0),createIndex(m_ActiveRow, m_Frames.size()-1)); // note: bottomRight is INCLUSIVE, not exclusive.
2601    }
2602 
2603 //    xout << boost::format("FDocument::ToggleDataRow(): invoking SetActiveRow(%i). Before: m_ActiveRow = %i") % iIndex % m_ActiveRow << std::endl;
2604    SetActiveRow(iIndex, true, false); // FIXME: don't update status because it would overwrite the iso trace result
2605 
2606 //    emit ActiveDatasetChanged();
2607 }
2608 
SetActiveCol(int iIndex)2609 void FDocument::SetActiveCol(int iIndex)
2610 {
2611    if (iIndex == m_ActiveCol)
2612       return; // do nothing---in particular do not invoke dataChanged();
2613 
2614    if (0 == GetFrame(iIndex,false)) {
2615       if (iIndex != -1)
2616          xout << "document: no frame #" << iIndex << " exists." << std::endl;
2617    } else {
2618 //       emit layoutAboutToBeChanged();
2619       m_ActiveCol = iIndex;
2620       FDataSetList *pFrameData = GetCurrentFrameData();
2621       if (pFrameData) {
2622          emit dataChanged(createIndex(0,m_ActiveCol),createIndex(pFrameData? (pFrameData->size()-1) : 0, m_ActiveCol));
2623          // ^- should this be here?
2624    //       emit layoutChanged();
2625          // ^- why are these here?
2626          emit ActiveColChanged(iIndex);
2627          emit ActiveDatasetChanged();
2628          if (nSelectedAtoms() != 0)
2629             UpdateSelectedAtomsStatusText();
2630          else
2631             IvNotify(NOTIFY_Information, IvFmt("Switched to Frame #%1", iIndex));
2632       }
2633    }
2634 }
2635 
SetActiveRow(int iIndex,bool ForceUpdate,bool UpdateStatus)2636 void FDocument::SetActiveRow(int iIndex, bool ForceUpdate, bool UpdateStatus)
2637 {
2638    if (iIndex == m_ActiveRow && !ForceUpdate)
2639       return; // do nothing---in particular do not emit signals.
2640    m_ActiveRow = iIndex;
2641    if (m_ActiveRow == -1)
2642       IvNotify(NOTIFY_Information, "");
2643    emit ActiveDatasetChanged();
2644    emit ActiveRowChanged(iIndex);
2645 
2646    if (GetActiveDataSet().get() && UpdateStatus)
2647       IvNotify(NOTIFY_Information, IvFmt("Active Set: %1", GetActiveDataSet()->GetDesc(FDataSet::DESC_Full)));
2648 }
2649 
2650 
2651 
2652 
SelectAtom(int iAt,FSelectionMode SelectMode)2653 void FDocument::SelectAtom(int iAt, FSelectionMode SelectMode)
2654 {
2655    if (SelectMode == SELECT_Select) {
2656       UnselectAll(false); // don't update selection just yet.
2657       SelectMode = SELECT_Add;
2658    }
2659 
2660    FAtomOptions
2661       &AtomOptions = this->AtomOptions(iAt);
2662    if (SelectMode == SELECT_Toggle)
2663       AtomOptions.Flags ^= ATOM_Selected;
2664    else if (SelectMode == SELECT_Add)
2665       AtomOptions.Flags |= ATOM_Selected;
2666    else
2667       IV_NOTIFY(NOTIFY_Warning, IvFmt("SelectMode %1 not recognized in FDocument::SelectAtom", (int)SelectMode));
2668 
2669    // remember atom's position in the sequence of stuff we selected (yes, it is hacky.).
2670    if (AtomOptions.Flags & ATOM_Selected) {
2671       AtomOptions.iSelectionSequenceId = m_SelectionSequenceId;
2672       ++ m_SelectionSequenceId;
2673    }
2674 
2675 //    int nSelectedAtoms_ = nSelectedAtoms();
2676    UpdateSelectedAtomsStatusText();
2677 
2678    emit SelectionChanged();
2679 }
2680 
UpdateSelectedAtomsStatusText()2681 void FDocument::UpdateSelectedAtomsStatusText()
2682 {
2683    int nSelectedAtoms_ = nSelectedAtoms();
2684    if (nSelectedAtoms_ != 0) {
2685       // make a list of the currently selected atoms for status purposes.
2686       IFrame
2687          *pFrame = GetCurrentFrame();
2688       if (pFrame) {
2689          FDocument::FAtomIdList
2690             iSelectedAtoms = FDocument::GetSelectedAtoms();
2691 //          FAtomSet const
2692 //             &Atoms = *pFrame->pGetAtoms();
2693 //          if (iSelectedAtoms.size() == 1) {
2694 //             // could get some additional info about the atom, I guess (like charges, valencies, etc?).
2695 // //             FAtom
2696 //          }
2697          QString s;
2698          QTextStream str(&s);
2699          if (nSelectedAtoms_ == 2) {
2700             str << FMeasureBondLength(iSelectedAtoms[0], iSelectedAtoms[1], this).MeasureFrame(pFrame);
2701 //             if (HaveOrbitals()) {
2702 //                str << " | " << FMeasureBondOrder(iSelectedAtoms[0], iSelectedAtoms[1], this).MeasureFrame(pFrame);
2703 //             }
2704 
2705             // HMPF!! for angles/dihedrals, etc, I need the order in which atoms were selected...
2706          } else if (nSelectedAtoms_ == 3) {
2707             str << FMeasureBondAngle(iSelectedAtoms[0], iSelectedAtoms[1], iSelectedAtoms[2], this).MeasureFrame(pFrame);
2708          } else if (nSelectedAtoms_ == 4) {
2709             str << FMeasurePlaneAngle(iSelectedAtoms[0], iSelectedAtoms[1], iSelectedAtoms[2], iSelectedAtoms[3], this).MeasureFrame(pFrame);
2710          } else {
2711             str << "Selected: ";
2712             for (size_t ii = 0; ii != iSelectedAtoms.size(); ++ ii) {
2713                if (ii > 8) {
2714                   str << "...";
2715                   break;
2716                }
2717                if (ii != 0)
2718                   str << " | ";
2719                str << AtomLabel(iSelectedAtoms[ii]);
2720    //             int
2721    //                iAt = iSelectedAtoms[ii];
2722    //             if ((size_t)iAt >= Atoms.size())
2723    //                str << "?!!ERR";
2724    //             else
2725    //                str << s2q(Atoms[iAt].GetElementName());
2726    //             str << " " << (1+iAt);
2727             }
2728          }
2729          IvNotify(NOTIFY_Information, s);
2730       }
2731    } else {
2732       IvNotify(NOTIFY_Information, "");
2733    }
2734 }
2735 
2736 
nSelectedAtoms()2737 int FDocument::nSelectedAtoms()
2738 {
2739    int nSelected = 0;
2740    FAtomOptionMap::iterator
2741       itAtomOptions;
2742    for (itAtomOptions = m_AtomOptions.begin(); itAtomOptions != m_AtomOptions.end(); ++ itAtomOptions)
2743       if (itAtomOptions->second.Flags & ATOM_Selected)
2744          nSelected += 1;
2745    return nSelected;
2746 }
2747 
nAtomsWithFlags()2748 int FDocument::nAtomsWithFlags()
2749 {
2750    int nAt = -1;
2751    FAtomOptionMap::iterator
2752       itAtomOptions;
2753    for (itAtomOptions = m_AtomOptions.begin(); itAtomOptions != m_AtomOptions.end(); ++ itAtomOptions)
2754       if (int(itAtomOptions->first) > nAt)
2755          nAt = int(itAtomOptions->first);
2756    return nAt + 1;
2757 }
2758 
2759 
UnselectAll(bool EmitUpdate)2760 void FDocument::UnselectAll(bool EmitUpdate)
2761 {
2762    FAtomOptionMap::iterator
2763       itAtomOptions;
2764    for (itAtomOptions = m_AtomOptions.begin(); itAtomOptions != m_AtomOptions.end(); ++ itAtomOptions)
2765       itAtomOptions->second.Flags &= ~ATOM_Selected;
2766    m_SelectionSequenceId = 0;
2767 
2768    IvNotify(NOTIFY_Information, "");
2769    SetActiveRow(-1);
2770    if (EmitUpdate)
2771       emit SelectionChanged();
2772 }
2773 
HideSelectedAtoms()2774 void FDocument::HideSelectedAtoms()
2775 {
2776    FAtomOptionMap::iterator
2777       itAtomOptions;
2778    for (itAtomOptions = m_AtomOptions.begin(); itAtomOptions != m_AtomOptions.end(); ++ itAtomOptions)
2779       if (itAtomOptions->second.Flags & ATOM_Selected) {
2780          itAtomOptions->second.Flags &= ~ATOM_Selected;
2781          itAtomOptions->second.Flags |= ATOM_Hidden;
2782       }
2783    emit SelectionChanged();
2784 }
2785 
ChangeSelectedBond()2786 void FDocument::ChangeSelectedBond()
2787 {
2788    FBondChangeAction
2789       *pBondAct = qobject_cast<FBondChangeAction*>(sender());
2790    if (pBondAct == 0) {
2791       IV_NOTIFY(NOTIFY_Warning, "Got a change-bond request, but sender is not a FBondChangeAction. Request ignored.");
2792       return;
2793    }
2794    int
2795       iAt = pBondAct->m_iAt + 1, // public interface has 1-based atom numbers.
2796       jAt = pBondAct->m_jAt + 1;
2797    FDocument *document = this;
2798    switch(pBondAct->m_Type) {
2799       case FBondChangeAction::ACTION_Hide: {
2800          for (uint iFrame = 0; iFrame < uint(document->GetNumFrames()); ++ iFrame)
2801             document->GetFrame(iFrame)->delete_bond(iAt, jAt);
2802          break;
2803       }
2804       case FBondChangeAction::ACTION_SetStyleDotted: {
2805          for (uint iFrame = 0; iFrame < uint(document->GetNumFrames()); ++ iFrame)
2806             document->GetFrame(iFrame)->add_bond(iAt, jAt, "gray|dotted");
2807          break;
2808       }
2809       case FBondChangeAction::ACTION_Reset: {
2810          for (uint iFrame = 0; iFrame < uint(document->GetNumFrames()); ++ iFrame)
2811             document->GetFrame(iFrame)->add_bond(iAt, jAt, "");
2812          break;
2813       }
2814       default: {
2815          IV_NOTIFY(NOTIFY_Warning, "Type of bond change not recognized. Request ignored.");
2816       }
2817    }
2818    emit VisualRepresentationChanged();
2819 }
2820 
MakeBondLinesForSelectedAtoms()2821 void FDocument::MakeBondLinesForSelectedAtoms()
2822 {
2823    QString
2824       sNewStyle = "";
2825    QAction
2826       *pAct = qobject_cast<QAction*>(sender());
2827    if (pAct) {
2828       sNewStyle = pAct->data().toString();
2829    }
2830 
2831    FDocument::FAtomIdList
2832       iSelectedAtoms = FDocument::GetSelectedAtoms();
2833    if (iSelectedAtoms.size() < 2u)
2834       return;
2835    int
2836       iAt = iSelectedAtoms[0]; // first selected is now always in place 0...
2837    for (size_t j = 1; j < iSelectedAtoms.size(); ++ j) {
2838       // make bond line from iAt to jAt.
2839       int jAt = iSelectedAtoms[j];
2840       for (uint iFrame = 0; iFrame < uint(this->GetNumFrames()); ++ iFrame)
2841          this->GetFrame(iFrame)->add_bond(iAt+1, jAt+1, sNewStyle);
2842    }
2843    emit VisualRepresentationChanged();
2844 }
2845 
ResetBondLines()2846 void FDocument::ResetBondLines()
2847 {
2848    for (uint iFrame = 0; iFrame < uint(this->GetNumFrames()); ++ iFrame)
2849       this->GetFrame(iFrame)->reset_bonds();
2850    emit VisualRepresentationChanged();
2851 }
2852 
2853 
2854 
SetNextOrbitalColorIndex(int Value)2855 void FDocument::SetNextOrbitalColorIndex(int Value)
2856 {
2857    m_iNextOrbitalColor = Value;
2858 }
2859 
SetNextOrbitalColorScheme(int Index)2860 void FDocument::SetNextOrbitalColorScheme(int Index)
2861 {
2862    m_iNextOrbitalColorScheme = Index;
2863 }
2864 
GetNextOrbitalColors(uint32_t & cIsoPlus,uint32_t & cIsoMinus)2865 void FDocument::GetNextOrbitalColors(uint32_t &cIsoPlus, uint32_t &cIsoMinus)
2866 {
2867    uint32_t cAlpha = 0x99000000; // 60%
2868    float Value = 1.;
2869    switch (m_iNextOrbitalColorScheme % 2) {
2870       case 0: { // hue spread
2871          float Hue = -(m_iNextOrbitalColor+1)*120.f;
2872          float Spread = 50.f;
2873          float Saturation = .6f;
2874          cIsoPlus = (uint32_t) ct::Hsv(Hue + Spread/2, Saturation, Value).uint32() | cAlpha;
2875          cIsoMinus = (uint32_t) ct::Hsv(Hue - Spread/2, Saturation, Value).uint32() | cAlpha;
2876          break;
2877       }
2878       case 1: { // sat spread
2879          float Hue = (m_iNextOrbitalColor)*60.f;
2880          cIsoPlus = (uint32_t) ct::Hsv(Hue, 0.6f, Value).uint32() | cAlpha;
2881          cIsoMinus = (uint32_t) ct::Hsv(Hue, 0.35f, Value).uint32() | cAlpha;
2882          break;
2883       }
2884    }
2885    m_iNextOrbitalColor += 1;
2886    emit NextOrbitalColorIndexChanged(m_iNextOrbitalColor);
2887 }
2888 
2889 
FDocument(QObject * parent)2890 FDocument::FDocument(QObject *parent)
2891    : QAbstractTableModel(parent),
2892      m_ActiveRow(-1),
2893      m_ActiveCol(-1),
2894      m_SelectionSequenceId(0),
2895      m_SkipVirtualOrbitals(!g_ShowVirtualOrbitals),
2896      m_CallEndInsertRows(-1)
2897 {
2898    int nElements = 110;
2899    m_ElementOptions.reserve(nElements);
2900    for (int iElement = 0; iElement < nElements; ++ iElement) {
2901       // note: 0 is a valid index here. used for dummy atoms.
2902       m_ElementOptions.append(FElementOptionsPtr(new FElementOptions(iElement)));
2903    }
2904 
2905    m_iNextOrbitalColor = 0;
2906    m_iNextOrbitalColorScheme = 0;
2907 
2908    m_pWfOptions = new FWfOptions(this);
2909    m_pMeasures = new FDocumentMeasures(this,this);
2910 }
2911 
GetCurrentFrame()2912 IFrame *FDocument::GetCurrentFrame()
2913 {
2914    return GetFrame(m_ActiveCol, false);
2915 }
2916 
GetCurrentFrame() const2917 IFrame *FDocument::GetCurrentFrame() const
2918 {
2919    return const_cast<FDocument*>(this)->GetFrame(m_ActiveCol, false);
2920 }
2921 
2922 
GetFrameData(int iFrame)2923 FDataSetList *FDocument::GetFrameData(int iFrame)
2924 {
2925    FFrame *pFrame = GetFrame(iFrame,false);
2926    if (pFrame)
2927       return &pFrame->m_Data;
2928    else
2929       return 0;
2930 }
2931 
GetFrame(int Idx,bool AssertExists)2932 IFrame *FDocument::GetFrame(int Idx, bool AssertExists)
2933 {
2934    if (Idx >= 0 && size_t(Idx) < m_Frames.size())
2935       return m_Frames[Idx].get();
2936    if (AssertExists)
2937       throw std::runtime_error("requested non-existent data frame.");
2938    return 0;
2939 }
2940 
GetFrame(int Idx,bool AssertExists) const2941 IFrame const *FDocument::GetFrame(int Idx, bool AssertExists) const
2942 {
2943    return const_cast<FDocument*>(this)->GetFrame(Idx, AssertExists);
2944 }
2945 
GetRow(int Idx,bool AssertExists) const2946 FDataSet const *FDocument::GetRow(int Idx, bool AssertExists) const
2947 {
2948    return const_cast<FDocument*>(this)->GetRow(Idx, AssertExists);
2949 }
2950 
GetRowCol(int iRow,int iCol,bool AssertExists) const2951 FDataSet const *FDocument::GetRowCol(int iRow, int iCol, bool AssertExists) const
2952 {
2953    return const_cast<FDocument*>(this)->GetRowCol(iRow, iCol, AssertExists);
2954 }
2955 
2956 
2957 
GetCurrentFrameData()2958 FDataSetList *FDocument::GetCurrentFrameData()
2959 {
2960    FFrame *pFrame = GetCurrentFrame();
2961    if (pFrame)
2962       return &pFrame->m_Data;
2963    else
2964       return 0;
2965 }
2966 
GetRowCol(int iRow,int iCol,bool AssertExists)2967 FDataSet *FDocument::GetRowCol(int iRow, int iCol, bool AssertExists)
2968 {
2969    FFrame *pFrame = GetFrame(iCol, AssertExists);
2970    if (pFrame) {
2971       if (size_t(iRow) < pFrame->m_Data.size())
2972          return pFrame->m_Data[iRow].get();
2973       if (AssertExists)
2974          throw std::runtime_error("requested non-existent data row.");
2975    }
2976    return 0;
2977 }
2978 
GetRow(int Idx,bool AssertExists)2979 FDataSet *FDocument::GetRow(int Idx, bool AssertExists)
2980 {
2981    return GetRowCol(Idx, m_ActiveCol, AssertExists);
2982 }
2983 
2984 
rowCount(const QModelIndex &) const2985 int FDocument::rowCount(const QModelIndex & /*parent*/) const
2986 {
2987    int RowCount = 0;
2988    for (unsigned i = 0; i < m_Frames.size(); ++ i)
2989       RowCount = std::max(RowCount, int(m_Frames[i]->m_Data.size()));
2990    return RowCount;
2991 }
2992 
columnCount(const QModelIndex &) const2993 int FDocument::columnCount(const QModelIndex & /*parent*/) const
2994 {
2995    return m_Frames.size();
2996 }
2997 
GetCurrentInputFileName()2998 QString FDocument::GetCurrentInputFileName()
2999 {
3000    FFrame
3001       *pFrame = GetCurrentFrame();
3002    if (pFrame)
3003       return pFrame->GetFullInputFileName();
3004    return "";
3005 //    return "unknown_file.xml";
3006 }
3007 
GetCurrentInputBaseFileName()3008 QString FDocument::GetCurrentInputBaseFileName()
3009 {
3010 //    std::string InputName = GetCurrentInputFileName();
3011 //    std::size_t iext = InputName.rfind(".");
3012 //    return InputName.substr(0, iext);
3013    return RemoveExt(GetCurrentInputFileName());
3014 }
3015 
GetCommonInputFileName()3016 QString FDocument::GetCommonInputFileName()
3017 {
3018    // if we have a script name, then return this script name.
3019    if (!m_InputFileName.isEmpty())
3020       return m_InputFileName;
3021 
3022    // otherwise go through all frames and check to which degree their file names
3023    // are the same, from the front.
3024    std::string Common = q2s(GetCurrentInputFileName());
3025 
3026    for (int iFrame = 0; size_t(iFrame) < m_Frames.size(); ++ iFrame) {
3027       size_t n = 0;
3028       std::string s = q2s(m_Frames[iFrame]->GetFullInputFileName());
3029       while (n < s.size() && n < Common.size() && s[n] == Common[n])
3030          n += 1;
3031       Common.erase(n);
3032    }
3033    if (RemovePath(s2q(Common)).isEmpty())
3034       return "unknown.xml";
3035 
3036    return s2q(Common);
3037 }
3038 
iFrameId(FFrame * pFrame) const3039 size_t FDocument::iFrameId(FFrame *pFrame) const
3040 {
3041    for (size_t i = 0; i < m_Frames.size(); ++ i)
3042       if (pFrame == &*m_Frames[i])
3043          return i;
3044    return m_Frames.size(); // invalid.
3045 }
3046 
3047 
HaveEnergies() const3048 bool FDocument::HaveEnergies() const
3049 {
3050    for (size_t i = 0; i < m_Frames.size(); ++ i)
3051       if (m_Frames[i]->GetEnergy() != 0.)
3052          return true;
3053    return false;
3054 }
3055 
HaveGradients() const3056 bool FDocument::HaveGradients() const
3057 {
3058    for (size_t i = 0; i < m_Frames.size(); ++ i)
3059       if (m_Frames[i]->GetGradient() != 0.)
3060          return true;
3061    return false;
3062 }
3063 
HaveOrbitals() const3064 bool FDocument::HaveOrbitals() const
3065 {
3066    for (size_t i = 0; i < m_Frames.size(); ++ i)
3067       if (m_Frames[i]->HaveOrbitals())
3068          return true;
3069    return false;
3070 }
3071 
3072 
3073 
data(const QModelIndex & index,int role) const3074 QVariant FDocument::data(const QModelIndex &index, int role) const
3075 {
3076    int iRow = index.row(),
3077        iCol = index.column();
3078    FDataSet const
3079       *pData = GetRowCol(iRow, iCol, false);
3080    if (!pData)
3081       return QVariant();
3082 
3083    if ( role == Qt::DisplayRole ) { return "[" + pData->GetType() + "] " + pData->GetDesc(); }
3084    if ( pData->Active ) {
3085       uint32_t dwBaseColor = pData->GetBaseColor();
3086       if (dwBaseColor != 0) {
3087          if ( role == Qt::BackgroundRole ) return QBrush(QColor(dwBaseColor));
3088          if ( role == Qt::ForegroundRole ) return QBrush(Qt::black);
3089       } else {
3090          if ( role == Qt::BackgroundRole ) return QBrush(QColor(64,96,64));
3091          if ( role == Qt::ForegroundRole ) return QBrush(Qt::white);
3092       }
3093    }
3094 
3095    return QVariant();
3096 }
3097 
headerData(int section,Qt::Orientation orientation,int role) const3098 QVariant FDocument::headerData(int section, Qt::Orientation orientation, int role) const
3099 {
3100    if ( orientation == Qt::Horizontal ) {
3101       if (role == Qt::DisplayRole) { return IvFmt("F#%1", section); }
3102 //       if ( role == Qt::BackgroundRole ) return QBrush((section % 2 == 0)? Qt::red : QColor(128,0,0));
3103    }
3104 
3105 // //    if ( orientation == Qt::Vertical ) {
3106 // //       if ( role == Qt::DisplayRole ) return QString("[%1]").arg(section);
3107 // //    }
3108 //    if ( orientation == Qt::Horizontal ) {
3109 //       if ( role == Qt::DisplayRole && section == 0 ) { return QString(""); }
3110 //       if ( role == Qt::DisplayRole && section == 1 ) { return QString("desc"); }
3111 //       if ( 0 ) {
3112 //          // custom decorations...
3113 //          if ( role == Qt::FontRole ) {
3114 //                QFont CaptionFont;
3115 //                CaptionFont.setBold(true);
3116 //                return CaptionFont;
3117 //          }
3118 //          if ( role == Qt::BackgroundRole ) return QBrush((section % 2 == 0)? QColor(96,96,96) : QColor(128,128,128));
3119 //          if ( role == Qt::ForegroundRole ) return QBrush(Qt::white);
3120 //       }
3121 // //       if ( role == Qt::BackgroundRole ) return QBrush((section % 2 == 0)? Qt::red : QColor(128,0,0));
3122 //    }
3123 
3124    return QVariant();
3125 }
3126 
3127 
InitForElement(int iElement_)3128 void FElementOptions::InitForElement(int iElement_)
3129 {
3130    m_iElement = iElement_;
3131    InitProperties(); // must come AFTER m_iElement assignment.
3132 }
3133 
FElementOptions(int iElement_,QObject * parent)3134 FElementOptions::FElementOptions(int iElement_, QObject *parent)
3135    : QObject(parent)
3136 {
3137    InitForElement(iElement_); // must come AFTER m_iElement assignment.
3138    assert(parent == 0);
3139    // ^- intended to be used with smart pointers, due to
3140    //    explicit sharing. Not with ownership hierarchies.
3141 }
3142 
FElementOptions(FElementOptions const * other,QObject * parent)3143 FElementOptions::FElementOptions(FElementOptions const *other, QObject *parent)
3144    : QObject(parent)
3145 {
3146    InitForElement(other->iElement()); // must come AFTER m_iElement assignment.
3147    CopyPropertiesFrom(*other);
3148    assert(parent == 0);
3149    // ^- intended to be used with smart pointers, due to
3150    //    explicit sharing. Not with ownership hierarchies.
3151 }
3152 
3153 
3154 // that's the rasmol CPKnew colors, according to jmol homepage.
3155 // See MakeColorCodes.py
3156 // uint32_t ElementColors[109] = {0xffffff,0xffc0cb,0xb22121,0xff1493,0x00ff00,0xd3d3d3,0x87cee6,0xff0000,0xdaa520,0xff1493,0x0000ff,0x228b22,0x696969,0xdaa520,0xffaa00,0xffff00,0x00ff00,0xff1493,0xff1493,0x696969,0xff1493,0x696969,0xff1493,0x696969,0x696969,0xffaa00,0xff1493,0x802828,0x802828,0x802828,0xff1493,0xff1493,0xff1493,0xff1493,0x802828,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0x696969,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xa020f0,0xff1493,0xff1493,0xffaa00,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xdaa520,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493};
3157 
3158 // as above, but replaced carbon by 0x999999 (0.6/0.6/0.6)
3159 // also inserted a number for dummies (element 0).
3160 static uint32_t ElementColors[110] = {0x404040, 0xffffff,0xffc0cb,0xb22121,0xff1493,0x00ff00,0x999999,0x87cee6,0xff0000,0xdaa520,0xff1493,0x0000ff,0x228b22,0x696969,0xdaa520,0xffaa00,0xffff00,0x00ff00,0xff1493,0xff1493,0x696969,0xff1493,0x696969,0xff1493,0x696969,0x696969,0xffaa00,0xff1493,0x802828,0x802828,0x802828,0xff1493,0xff1493,0xff1493,0xff1493,0x802828,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0x696969,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xa020f0,0xff1493,0xff1493,0xffaa00,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xdaa520,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493,0xff1493};
3161 
3162 
GetDefaultColor() const3163 uint32_t FElementOptions::GetDefaultColor() const {
3164    if (m_iElement < 0 || size_t(m_iElement) > sizeof(ElementColors)/sizeof(ElementColors[0])) {
3165       assert(0); // no entry for this number...
3166       return 0xff00ff;
3167    }
3168    return ElementColors[size_t(m_iElement)];
3169 }
3170 
GetDefaultBondColor() const3171 uint32_t FElementOptions::GetDefaultBondColor() const
3172 {
3173    return ColorNotSet;
3174 //    return GetDefaultColor();
3175 }
3176 
GetBondColor1() const3177 uint32_t FElementOptions::GetBondColor1() const
3178 {
3179    if (m_BondColor != ColorNotSet)
3180       return m_BondColor;
3181    return m_Color;
3182 }
3183 
GetDefaultCovalentRadius() const3184 double FElementOptions::GetDefaultCovalentRadius() const
3185 {
3186    return ct::GetCovalentRadius(m_iElement);
3187 }
3188 
3189 // float UffBondRadii[103] = {0.87,1.60,2.52,2.03,1.58,1.43,1.32,1.29,1.26,1.74,2.91,2.69,2.35,2.11,2.08,2.04,1.97,1.95,3.69,3.33,2.86,2.67,2.65,2.54,2.61,2.52,2.35,2.20,2.46,2.25,2.38,2.26,2.29,2.25,2.25,2.17,4.27,3.88,3.21,2.96,2.78,2.80,2.50,2.79,2.52,2.53,2.62,2.65,2.76,2.64,2.66,2.62,2.61,2.39,4.86,4.30,3.67,3.48,3.44,3.43,3.40,3.36,3.35,3.28,3.27,3.23,3.20,3.16,3.14,3.09,3.16,3.04,2.86,2.88,2.59,2.59,2.59,2.58,2.38,2.53,2.87,2.76,2.86,2.83,2.92,2.68,5.44,4.75,3.75,3.25,3.23,3.18,3.15,3.13,3.14,3.40,3.33,3.31,3.26,3.24,3.19,3.17,3.21};
3190 // // static float AtomicRadii[103] = {0.76,0.64,2.68,1.80,1.64,1.54,1.50,1.46,1.42,1.38,3.08,2.60,2.36,2.22,2.12,2.04,1.98,1.94,3.92,3.48,2.88,2.72,2.50,2.54,2.78,2.50,2.52,2.42,2.76,2.62,2.52,2.44,2.38,2.32,2.28,2.20,4.22,3.84,3.24,2.96,2.74,2.90,3.12,2.52,2.70,2.62,3.06,2.96,2.88,2.82,2.76,2.70,2.66,2.60,4.50,3.96,3.38,3.48,3.44,3.43,3.40,3.36,3.35,3.28,3.27,3.23,3.20,3.16,3.14,3.09,3.20,3.00,2.76,2.92,3.18,2.56,2.74,2.56,2.88,2.98,2.96,2.94,2.92,2.83,2.92,2.90,5.44,4.75,3.75,3.25,3.23,3.18,3.15,3.13,3.14,3.40,3.33,3.31,3.26,3.24,3.19,3.17,3.21}; // see make_atomic_radii.py
3191 static float AtomicRadii[104] = {0, 0.87,1.60,2.52,2.03,1.58,1.43,1.32,1.29,1.26,1.74,2.91,2.69,2.35,2.11,2.08,2.04,1.97,1.95,3.69,3.33,2.86,2.67,2.65,2.54,2.61,2.52,2.35,2.20,2.46,2.25,2.38,2.26,2.29,2.25,2.25,2.17,4.27,3.88,3.21,2.96,2.78,2.80,2.50,2.79,2.52,2.53,2.62,2.65,2.76,2.64,2.66,2.62,2.61,2.39,4.86,4.30,3.67,3.48,3.44,3.43,3.40,3.36,3.35,3.28,3.27,3.23,3.20,3.16,3.14,3.09,3.16,3.04,2.86,2.88,2.59,2.59,2.59,2.58,2.38,2.53,2.87,2.76,2.86,2.83,2.92,2.68,5.44,4.75,3.75,3.25,3.23,3.18,3.15,3.13,3.14,3.40,3.33,3.31,3.26,3.24,3.19,3.17,3.21};
3192 
GetAtomDrawRadius(int iElement)3193 inline float GetAtomDrawRadius(int iElement) { return AtomicRadii[iElement]; } // hmmm... looks rather non-similar to the covalent radius.
3194 // float GetAtomDrawRadius(int iElement) { return GetCovalentRadius[iElement]; }
3195 
GetDefaultDrawRadius() const3196 double FElementOptions::GetDefaultDrawRadius() const
3197 {
3198    return GetAtomDrawRadius(m_iElement);
3199 }
3200 
ElementName() const3201 char const *FElementOptions::ElementName() const
3202 {
3203    return ct::ElementNameFromNumber(m_iElement);
3204 }
3205 
pElementOptions(int iAt,ct::FAtomSet * pAtoms)3206 FElementOptions *FDocument::pElementOptions(int iAt, ct::FAtomSet *pAtoms)
3207 {
3208    FAtomOptions
3209       &AtomOpt = AtomOptions(iAt);
3210    // has there been an override? If yes, return it.
3211    if (AtomOpt.pPropertiesOverride)
3212       return &*AtomOpt.pPropertiesOverride;
3213    // if not, return the default for the atom's element.
3214    // For this we need to find the element first, however.
3215    if (pAtoms == 0) {
3216       IFrame
3217          *pFrame = GetCurrentFrame();
3218       if (pFrame)
3219          pAtoms = pFrame->pGetAtoms();
3220    }
3221    if (pAtoms != 0) {
3222       if ((size_t)iAt < pAtoms->size()) {
3223          int iElement = (*pAtoms)[iAt].AtomicNumber;
3224          if (iElement >= 0 && iElement < m_ElementOptions.size())
3225             return &*m_ElementOptions[iElement];
3226       }
3227    }
3228    IvNotify(NOTIFY_Error, IvFmt("Failed to find element data for atom %1. Bad karma!", iAt));
3229    return 0;
3230 }
3231 
3232 
3233 
3234 #include "prop_FWfOptions.cpp.inl"
3235 #include "prop_FElementOptions.cpp.inl"
3236