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