1 /******************************************************************************
2 
3   This source file is part of the Avogadro project.
4 
5   Copyright 2012 Kitware, Inc.
6 
7   This source code is released under the New BSD License, (the "License").
8 
9   Unless required by applicable law or agreed to in writing, software
10   distributed under the License is distributed on an "AS IS" BASIS,
11   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   See the License for the specific language governing permissions and
13   limitations under the License.
14 
15 ******************************************************************************/
16 
17 #include "gamessinputdialog.h"
18 #include "gamesshighlighter.h"
19 
20 #include <avogadro/core/atom.h>
21 #include <avogadro/core/bond.h>
22 #include <avogadro/core/elements.h>
23 
24 #include <avogadro/molequeue/molequeuedialog.h>
25 #include <avogadro/molequeue/molequeuemanager.h>
26 #include <avogadro/qtgui/molecule.h>
27 
28 #include <avogadro/molequeue/client/jobobject.h>
29 #include <qjsonarray.h>
30 #include <qjsonobject.h>
31 #include <qjsonvalue.h>
32 
33 #include <QtWidgets/QFileDialog>
34 #include <QtWidgets/QMessageBox>
35 #include <QtWidgets/QProgressDialog>
36 
37 #include <QtCore/QFile>
38 #include <QtCore/QSettings>
39 #include <QtCore/QString>
40 #include <QtCore/QTimer>
41 
42 using Avogadro::MoleQueue::MoleQueueDialog;
43 using Avogadro::MoleQueue::MoleQueueManager;
44 using Avogadro::MoleQueue::JobObject;
45 
46 namespace Avogadro {
47 namespace QtPlugins {
48 
49 enum CalculateOption
50 {
51   CalculateSinglePoint = 0,
52   CalculateEquilibriumGeometry,
53   CalculateTransitionState,
54   CalculateFrequencies,
55 
56   CalculateCount
57 };
58 
59 enum TheoryOption
60 {
61   TheoryAM1 = 0,
62   TheoryPM3,
63   TheoryRHF,
64   TheoryB3LYP,
65   TheoryMP2,
66   TheoryCCSDT,
67 
68   TheoryCount
69 };
70 
71 enum BasisOption
72 {
73   BasisSTO3G = 0,
74   BasisMINI,
75   Basis321G,
76   Basis631Gd,
77   Basis631Gdp,
78   Basis631PlusGdp,
79   Basis631PlusG2dp,
80   Basis6311PlusPlusG2dp,
81   BasisCorePotential,
82 
83   BasisCount
84 };
85 
86 enum StateOption
87 {
88   StateGas = 0,
89   StateWater,
90 
91   StateCount
92 };
93 
94 enum MultiplicityOption
95 {
96   MultiplicitySinglet = 0,
97   MultiplicityDoublet,
98   MultiplicityTriplet,
99 
100   MultiplicityCount
101 };
102 
103 enum ChargeOption
104 {
105   ChargeDication = 0,
106   ChargeCation,
107   ChargeNeutral,
108   ChargeAnion,
109   ChargeDianion,
110 
111   ChargeCount
112 };
113 
GamessInputDialog(QWidget * parent_,Qt::WindowFlags f)114 GamessInputDialog::GamessInputDialog(QWidget* parent_, Qt::WindowFlags f)
115   : QDialog(parent_, f), m_molecule(nullptr), m_highlighter(nullptr),
116     m_updatePending(false)
117 {
118   ui.setupUi(this);
119   m_highlighter = new GamessHighlighter(ui.previewText->document());
120 
121   buildOptions();
122 
123   connectBasic();
124   connectPreview();
125   connectButtons();
126 
127   setBasicDefaults();
128 
129   updatePreviewText();
130 }
131 
~GamessInputDialog()132 GamessInputDialog::~GamessInputDialog()
133 {
134 }
135 
setMolecule(QtGui::Molecule * mol)136 void GamessInputDialog::setMolecule(QtGui::Molecule* mol)
137 {
138   if (mol == m_molecule)
139     return;
140 
141   if (m_molecule)
142     m_molecule->disconnect(this);
143 
144   m_molecule = mol;
145 
146   connect(mol, SIGNAL(changed(unsigned int)), SLOT(updatePreviewText()));
147   connect(mol, SIGNAL(changed(unsigned int)), SLOT(updateTitlePlaceholder()));
148 
149   updateTitlePlaceholder();
150   updatePreviewText();
151 }
152 
showEvent(QShowEvent * e)153 void GamessInputDialog::showEvent(QShowEvent* e)
154 {
155   QWidget::showEvent(e);
156 
157   // Update the preview text if an update was requested while hidden. Use a
158   // single shot to allow the dialog to show before popping up any warnings.
159   if (m_updatePending)
160     QTimer::singleShot(0, this, SLOT(updatePreviewText()));
161 }
162 
connectBasic()163 void GamessInputDialog::connectBasic()
164 {
165   connect(ui.titleEdit, SIGNAL(textChanged(QString)), this,
166           SLOT(updatePreviewText()));
167   connect(ui.calculateCombo, SIGNAL(currentIndexChanged(int)), this,
168           SLOT(updatePreviewText()));
169   connect(ui.calculateCombo, SIGNAL(currentIndexChanged(int)), this,
170           SLOT(updateTitlePlaceholder()));
171   connect(ui.theoryCombo, SIGNAL(currentIndexChanged(int)), this,
172           SLOT(updatePreviewText()));
173   connect(ui.theoryCombo, SIGNAL(currentIndexChanged(int)), this,
174           SLOT(updateTitlePlaceholder()));
175   connect(ui.basisCombo, SIGNAL(currentIndexChanged(int)), this,
176           SLOT(updatePreviewText()));
177   connect(ui.basisCombo, SIGNAL(currentIndexChanged(int)), this,
178           SLOT(updateTitlePlaceholder()));
179   connect(ui.stateCombo, SIGNAL(currentIndexChanged(int)), this,
180           SLOT(updatePreviewText()));
181   connect(ui.multiplicityCombo, SIGNAL(currentIndexChanged(int)), this,
182           SLOT(updatePreviewText()));
183   connect(ui.chargeCombo, SIGNAL(currentIndexChanged(int)), this,
184           SLOT(updatePreviewText()));
185 }
186 
connectPreview()187 void GamessInputDialog::connectPreview()
188 {
189 }
190 
connectButtons()191 void GamessInputDialog::connectButtons()
192 {
193   connect(ui.resetAllButton, SIGNAL(clicked()), SLOT(resetClicked()));
194   connect(ui.defaultsButton, SIGNAL(clicked()), SLOT(defaultsClicked()));
195   connect(ui.generateButton, SIGNAL(clicked()), SLOT(generateClicked()));
196   connect(ui.computeButton, SIGNAL(clicked()), SLOT(computeClicked()));
197   connect(ui.closeButton, SIGNAL(clicked()), SLOT(close()));
198 }
199 
buildOptions()200 void GamessInputDialog::buildOptions()
201 {
202   buildCalculateOptions();
203   buildTheoryOptions();
204   buildBasisOptions();
205   buildStateOptions();
206   buildMultiplicityOptions();
207   buildChargeOptions();
208 }
209 
updateOptionCache()210 void GamessInputDialog::updateOptionCache()
211 {
212   m_optionCache.clear();
213   m_optionCache.insert(ui.calculateCombo, ui.calculateCombo->currentIndex());
214   m_optionCache.insert(ui.theoryCombo, ui.theoryCombo->currentIndex());
215   m_optionCache.insert(ui.basisCombo, ui.basisCombo->currentIndex());
216   m_optionCache.insert(ui.stateCombo, ui.stateCombo->currentIndex());
217   m_optionCache.insert(ui.multiplicityCombo,
218                        ui.multiplicityCombo->currentIndex());
219   m_optionCache.insert(ui.chargeCombo, ui.chargeCombo->currentIndex());
220 }
221 
restoreOptionCache()222 void GamessInputDialog::restoreOptionCache()
223 {
224   foreach (QComboBox* combo, m_optionCache.keys()) {
225     combo->blockSignals(true);
226     combo->setCurrentIndex(m_optionCache.value(combo, 0));
227     combo->blockSignals(false);
228   }
229 }
230 
buildCalculateOptions()231 void GamessInputDialog::buildCalculateOptions()
232 {
233   for (int i = 0; i < static_cast<int>(CalculateCount); ++i) {
234     QString text = "";
235     switch (static_cast<CalculateOption>(i)) {
236       case CalculateSinglePoint:
237         text = tr("Single Point");
238         break;
239       case CalculateEquilibriumGeometry:
240         text = tr("Equilibrium Geometry");
241         break;
242       case CalculateTransitionState:
243         text = tr("Transition State");
244         break;
245       case CalculateFrequencies:
246         text = tr("Frequencies");
247         break;
248       default:
249         break;
250     }
251     ui.calculateCombo->addItem(text);
252   }
253 }
254 
buildTheoryOptions()255 void GamessInputDialog::buildTheoryOptions()
256 {
257   for (int i = 0; i < static_cast<int>(TheoryCount); ++i) {
258     QString text = "";
259     switch (static_cast<TheoryOption>(i)) {
260       case TheoryAM1:
261         text = "AM1";
262         break;
263       case TheoryPM3:
264         text = "PM3";
265         break;
266       case TheoryRHF:
267         text = "RHF";
268         break;
269       case TheoryB3LYP:
270         text = "B3LYP";
271         break;
272       case TheoryMP2:
273         text = "MP2";
274         break;
275       case TheoryCCSDT:
276         text = "CCSD(T)";
277         break;
278       default:
279         break;
280     }
281     ui.theoryCombo->addItem(text);
282   }
283 }
284 
buildBasisOptions()285 void GamessInputDialog::buildBasisOptions()
286 {
287   for (int i = 0; i < static_cast<int>(BasisCount); ++i) {
288     QString text = "";
289     switch (static_cast<BasisOption>(i)) {
290       case BasisSTO3G:
291         text = "STO-3G";
292         break;
293       case BasisMINI:
294         text = "MINI";
295         break;
296       case Basis321G:
297         text = "3-21 G";
298         break;
299       case Basis631Gd:
300         text = "6-31 G(d)";
301         break;
302       case Basis631Gdp:
303         text = "6-31 G(d,p)";
304         break;
305       case Basis631PlusGdp:
306         text = "6-31+G(d,p)";
307         break;
308       case Basis631PlusG2dp:
309         text = "6-31+G(2d,p)";
310         break;
311       case Basis6311PlusPlusG2dp:
312         text = "6-311++G(2d,p)";
313         break;
314       case BasisCorePotential:
315         text = tr("Core Potential");
316         break;
317       default:
318         break;
319     }
320     ui.basisCombo->addItem(text);
321   }
322 }
323 
buildStateOptions()324 void GamessInputDialog::buildStateOptions()
325 {
326   for (int i = 0; i < static_cast<int>(StateCount); ++i) {
327     QString text = "";
328     switch (static_cast<StateOption>(i)) {
329       case StateGas:
330         text = tr("Gas");
331         break;
332       case StateWater:
333         text = tr("Water");
334         break;
335       default:
336         break;
337     }
338     ui.stateCombo->addItem(text);
339   }
340 }
341 
buildMultiplicityOptions()342 void GamessInputDialog::buildMultiplicityOptions()
343 {
344   for (int i = 0; i < static_cast<int>(MultiplicityCount); ++i) {
345     QString text = "";
346     switch (static_cast<MultiplicityOption>(i)) {
347       case MultiplicitySinglet:
348         text = tr("Singlet");
349         break;
350       case MultiplicityDoublet:
351         text = tr("Doublet");
352         break;
353       case MultiplicityTriplet:
354         text = tr("Triplet");
355         break;
356       default:
357         break;
358     }
359     ui.multiplicityCombo->addItem(text);
360   }
361 }
362 
buildChargeOptions()363 void GamessInputDialog::buildChargeOptions()
364 {
365   for (int i = 0; i < static_cast<int>(ChargeCount); ++i) {
366     QString text = "";
367     switch (static_cast<ChargeOption>(i)) {
368       case ChargeDication:
369         text = tr("Dication");
370         break;
371       case ChargeCation:
372         text = tr("Cation");
373         break;
374       case ChargeNeutral:
375         text = tr("Neutral");
376         break;
377       case ChargeAnion:
378         text = tr("Anion");
379         break;
380       case ChargeDianion:
381         text = tr("Dianion");
382         break;
383       default:
384         break;
385     }
386     ui.chargeCombo->addItem(text);
387   }
388 }
389 
setBasicDefaults()390 void GamessInputDialog::setBasicDefaults()
391 {
392   ui.titleEdit->setText(QString());
393   ui.calculateCombo->setCurrentIndex(CalculateSinglePoint);
394   ui.theoryCombo->setCurrentIndex(TheoryB3LYP);
395   ui.basisCombo->setCurrentIndex(Basis321G);
396   ui.stateCombo->setCurrentIndex(StateGas);
397   ui.multiplicityCombo->setCurrentIndex(MultiplicitySinglet);
398   ui.chargeCombo->setCurrentIndex(ChargeNeutral);
399 }
400 
generateJobTitle() const401 QString GamessInputDialog::generateJobTitle() const
402 {
403   QString calculation(ui.calculateCombo->currentText());
404   QString theory(ui.theoryCombo->currentText());
405   QString basis(ui.basisCombo->currentText());
406   QString formula(m_molecule ? QString::fromStdString(m_molecule->formula())
407                              : tr("[no molecule]"));
408 
409   // Merge theory/basis into theory
410   theory += "/" + basis;
411   theory.replace(QRegExp("\\s+"), "");
412 
413   return QString("%1 | %2 | %3").arg(formula, calculation, theory);
414 }
415 
updatePreviewText()416 void GamessInputDialog::updatePreviewText()
417 {
418   // If the dialog is not shown, delay the update in case we need to prompt the
419   // user to overwrite changes. Set the m_updatePending flag to true so we'll
420   // know to update in the show event.
421   if (!isVisible()) {
422     m_updatePending = true;
423     return;
424   }
425 
426   m_updatePending = false;
427 
428   // Has the preview text been modified?
429   if (ui.previewText->document()->isModified()) {
430     QString message = tr("The input file has been modified. "
431                          "Would you like to overwrite your changes to reflect "
432                          "the new geometry or job options?");
433     int response = QMessageBox::question(
434       this, tr("Overwrite modified input file?"), message,
435       QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
436     if (static_cast<QMessageBox::StandardButton>(response) !=
437         QMessageBox::Yes) {
438       restoreOptionCache();
439       return;
440     }
441   }
442 
443   // Gather options:
444   QString title(ui.titleEdit->text());
445   if (title.isEmpty())
446     title = generateJobTitle();
447 
448   CalculateOption calculate(
449     static_cast<CalculateOption>(ui.calculateCombo->currentIndex()));
450   TheoryOption theory(
451     static_cast<TheoryOption>(ui.theoryCombo->currentIndex()));
452   BasisOption basis(static_cast<BasisOption>(ui.basisCombo->currentIndex()));
453   StateOption state(static_cast<StateOption>(ui.stateCombo->currentIndex()));
454   MultiplicityOption multiplicity(
455     static_cast<MultiplicityOption>(ui.multiplicityCombo->currentIndex()));
456   ChargeOption charge(
457     static_cast<ChargeOption>(ui.chargeCombo->currentIndex()));
458 
459   // Disable basis selection for semiempirical methods.
460   ui.basisCombo->setEnabled(theory != TheoryAM1 && theory != TheoryPM3);
461 
462   // Generate text.
463   //   Variables:
464   QString runTyp;
465   QString scfTyp;
466   QString gBasis;
467   QString mult;
468   QString iCharg;
469 
470   // Extra options for lines
471   QString extraBasis;
472   QString extraContrl;
473 
474   // Optional lines
475   QString statPt;
476   QString force;
477   QString pcm;
478 
479   switch (calculate) {
480     case CalculateSinglePoint:
481       runTyp = "ENERGY";
482       break;
483     case CalculateEquilibriumGeometry:
484       runTyp = "OPTIMIZE";
485       statPt = " $STATPT OPTTOL=0.0001 NSTEP=20 $END\n";
486       break;
487     case CalculateTransitionState:
488       runTyp = "SADPOINT";
489       statPt = " $STATPT OPTTOL=0.0001 NSTEP=20 $END\n";
490       break;
491     case CalculateFrequencies:
492       runTyp = "HESSIAN";
493       force = " $FORCE METHOD=ANALYTIC VIBANL=.TRUE. $END\n";
494       break;
495     default:
496       break;
497   }
498 
499   switch (theory) {
500     case TheoryAM1:
501       gBasis = "AM1";
502       break;
503     case TheoryPM3:
504       gBasis = "PM3";
505       break;
506     case TheoryRHF:
507       break;
508     case TheoryB3LYP:
509       extraContrl += " DFTTYP=B3LYP";
510       break;
511     case TheoryMP2:
512       extraContrl += " MPLEVL=2";
513       break;
514     case TheoryCCSDT:
515       extraContrl += " CCTYP=CCSD(T)";
516       break;
517     default:
518       break;
519   }
520 
521   if (theory != TheoryAM1 && theory != TheoryPM3) {
522     switch (basis) {
523       case BasisSTO3G:
524         gBasis = "STO";
525         extraBasis += " NGAUSS=3";
526         break;
527       case BasisMINI:
528         gBasis = "MINI";
529         break;
530       case Basis321G:
531         gBasis = "N21";
532         extraBasis += " NGAUSS=3";
533         break;
534       case Basis631Gd:
535         gBasis = "N31";
536         extraBasis += " NGAUSS=6 NDFUNC=1";
537         break;
538       case Basis631Gdp:
539         gBasis = "N31";
540         extraBasis += " NGAUSS=6 NDFUNC=1 NPFUNC=1";
541         break;
542       case Basis631PlusGdp:
543         gBasis = "N31";
544         extraBasis += " NGAUSS=6 NDFUNC=1 NPFUNC=1 DIFFSP=.TRUE.";
545         break;
546       case Basis631PlusG2dp:
547         gBasis = "N31";
548         extraBasis += " NGAUSS=6 NDFUNC=2 NPFUNC=1 DIFFSP=.TRUE.";
549         break;
550       case Basis6311PlusPlusG2dp:
551         gBasis = "N311";
552         extraBasis += " NGAUSS=6 NDFUNC=2 NPFUNC=1 DIFFSP=.TRUE. DIFFS=.TRUE.";
553         break;
554       case BasisCorePotential:
555         gBasis = "SBK";
556         extraBasis += " NGAUSS=3 NDFUNC=1";
557         extraContrl += " ECP=SBK";
558         break;
559       default:
560         break;
561     }
562   }
563 
564   switch (state) {
565     case StateGas:
566       break;
567     case StateWater:
568       pcm = " $PCM SOLVNT=WATER $END\n";
569       break;
570     default:
571       break;
572   }
573 
574   switch (multiplicity) {
575     case MultiplicitySinglet:
576       scfTyp = "RHF";
577       mult = "1";
578       break;
579     case MultiplicityDoublet:
580       scfTyp = "ROHF";
581       mult = "2";
582       break;
583     case MultiplicityTriplet:
584       scfTyp = "ROHF";
585       mult = "3";
586       break;
587     default:
588       break;
589   }
590 
591   switch (charge) {
592     case ChargeDication:
593       iCharg = "2";
594       break;
595     case ChargeCation:
596       iCharg = "1";
597       break;
598     case ChargeNeutral:
599       iCharg = "0";
600       break;
601     case ChargeAnion:
602       iCharg = "-1";
603       break;
604     case ChargeDianion:
605       iCharg = "-2";
606       break;
607     default:
608       break;
609   }
610 
611   // build up the input file:
612   QString file;
613   file += QString("! %1\n").arg(title);
614   file += QString(" $BASIS GBASIS=%1%2 $END\n").arg(gBasis, extraBasis);
615   file += pcm;
616   file += QString(" $CONTRL SCFTYP=%1 RUNTYP=%2 ICHARG=%3 MULT=%4%5 $END\n")
617             .arg(scfTyp, runTyp, iCharg, mult, extraContrl);
618   file += statPt;
619   file += force;
620   file += "\n";
621   file += " $DATA\n";
622   file += "Title\n";
623   file += "C1\n";
624 
625   if (m_molecule) {
626     for (size_t i = 0; i < m_molecule->atomCount(); ++i) {
627       Core::Atom atom = m_molecule->atom(i);
628       file += QString("%1 %2 %3 %4 %5\n")
629                 .arg(Core::Elements::symbol(atom.atomicNumber()), -3)
630                 .arg(static_cast<float>(atom.atomicNumber()), 5, 'f', 1)
631                 .arg(atom.position3d().x(), 9, 'f', 5)
632                 .arg(atom.position3d().y(), 9, 'f', 5)
633                 .arg(atom.position3d().z(), 9, 'f', 5);
634     }
635   }
636 
637   file += " $END\n";
638 
639   ui.previewText->setText(file);
640   ui.previewText->document()->setModified(false);
641   updateOptionCache();
642 }
643 
resetClicked()644 void GamessInputDialog::resetClicked()
645 {
646   setBasicDefaults();
647   updatePreviewText();
648 }
649 
defaultsClicked()650 void GamessInputDialog::defaultsClicked()
651 {
652   setBasicDefaults();
653   updatePreviewText();
654 }
655 
generateClicked()656 void GamessInputDialog::generateClicked()
657 {
658   QSettings settings;
659   QString fileName =
660     (ui.baseNameEdit->text().isEmpty() ? ui.baseNameEdit->placeholderText()
661                                        : ui.baseNameEdit->text()) +
662     ".inp";
663   QString targetFile =
664     settings.value("gamessInput/outputDirectory", QDir::homePath()).toString();
665   targetFile =
666     QDir(QFileInfo(targetFile).absoluteDir()).absoluteFilePath(fileName);
667 
668   fileName = QFileDialog::getSaveFileName(this, tr("Save GAMESS input file"),
669                                           targetFile);
670 
671   // User cancel:
672   if (fileName.isNull())
673     return;
674 
675   settings.setValue("gamessInput/outputDirectory", fileName);
676 
677   QFile file(fileName);
678   bool success = false;
679   if (file.open(QFile::WriteOnly | QFile::Text)) {
680     if (file.write(ui.previewText->toPlainText().toLocal8Bit()) > 0) {
681       success = true;
682     }
683     file.close();
684   }
685 
686   if (!success) {
687     QMessageBox::critical(this, tr("Output Error"),
688                           tr("Failed to write to file %1.").arg(fileName));
689   }
690 }
691 
computeClicked()692 void GamessInputDialog::computeClicked()
693 {
694   // Verify that molequeue is running:
695   MoleQueueManager& mqManager = MoleQueueManager::instance();
696   if (!mqManager.connectIfNeeded()) {
697     QMessageBox::information(this, tr("Cannot connect to MoleQueue"),
698                              tr("Cannot connect to MoleQueue server. Please "
699                                 "ensure that it is running and try again."));
700     return;
701   }
702 
703   QString description(ui.titleEdit->text());
704   if (description.isEmpty())
705     description = generateJobTitle();
706 
707   QString fileNameBase = ui.baseNameEdit->text().isEmpty()
708                            ? ui.baseNameEdit->placeholderText()
709                            : ui.baseNameEdit->text();
710 
711   JobObject job;
712   job.setProgram("GAMESS");
713   job.setDescription(description);
714   job.setInputFile(QString("%1.inp").arg(fileNameBase),
715                    ui.previewText->toPlainText());
716 
717   MoleQueueDialog::SubmitStatus submitStatus =
718     MoleQueueDialog::submitJob(this, tr("Submit GAMESS Calculation"), job,
719                                MoleQueueDialog::WaitForSubmissionResponse |
720                                  MoleQueueDialog::SelectProgramFromTemplate);
721 
722   switch (submitStatus) {
723     default:
724     case MoleQueueDialog::SubmissionSuccessful:
725     case MoleQueueDialog::SubmissionFailed:
726     case MoleQueueDialog::SubmissionAttempted:
727     case MoleQueueDialog::SubmissionAborted:
728       // The dialog handles these cases adequately, we don't need to do
729       // anything.
730       break;
731 
732     case MoleQueueDialog::JobFailed:
733       // Inform the user:
734       QMessageBox::information(this, tr("Job Failed"),
735                                tr("The job did not complete successfully."),
736                                QMessageBox::Ok);
737       break;
738 
739     case MoleQueueDialog::JobFinished:
740       // Let the world know that the job is ready to open. job has been
741       // overwritten with the final job details.
742       emit openJobOutput(job);
743       hide();
744       break;
745   }
746 }
747 
updateTitlePlaceholder()748 void GamessInputDialog::updateTitlePlaceholder()
749 {
750   ui.titleEdit->setPlaceholderText(generateJobTitle());
751 }
752 
753 } // end namespace QtPlugins
754 } // end namespace Avogadro
755