From 5e4285febe8d6dd0e8ca0a6806bf9409ed06a747 Mon Sep 17 00:00:00 2001 From: Geoff Hutchison Date: Mon, 25 Nov 2024 10:34:53 -0500 Subject: [PATCH 1/3] Make sure point group is saved to the molecule data Signed-off-by: Geoff Hutchison --- .../molecularproperties/molecularmodel.cpp | 14 ++++++++++++++ avogadro/qtplugins/symmetry/symmetrywidget.cpp | 1 + 2 files changed, 15 insertions(+) diff --git a/avogadro/qtplugins/molecularproperties/molecularmodel.cpp b/avogadro/qtplugins/molecularproperties/molecularmodel.cpp index ccd2ae7408..05eacecfe7 100644 --- a/avogadro/qtplugins/molecularproperties/molecularmodel.cpp +++ b/avogadro/qtplugins/molecularproperties/molecularmodel.cpp @@ -197,6 +197,16 @@ QString formatFormula(Molecule* molecule) return formula; } +QString formatPointGroup(std::string pointgroup) +{ + // first character is in capital + // then everything else is in subscript using ... + QString formatted = QString::fromStdString(pointgroup); + QString output = formatted.at(0).toUpper(); + output += QString("%1").arg(formatted.mid(1)); + return output; +} + // Qt calls this for multiple "roles" across row / columns in the index // we also combine multiple types into this class, so lots of special cases QVariant MolecularModel::data(const QModelIndex& index, int role) const @@ -247,6 +257,8 @@ QVariant MolecularModel::data(const QModelIndex& index, int role) const else if (key == " 9totalSpinMultiplicity") return QVariant::fromValue( static_cast(m_molecule->totalSpinMultiplicity())); + else if (key == "pointgroup") + return formatPointGroup(it->second.toString()); return QString::fromStdString(it->second.toString()); } @@ -317,6 +329,8 @@ QVariant MolecularModel::headerData(int section, Qt::Orientation orientation, return tr("Entropy (kcal/mol•K)"); else if (it->first == "gibbs") return tr("Gibbs Free Energy (kcal/mol)"); + else if (it->first == "pointgroup") + return tr("Point Group", "point group symmetry"); else if (it != map.end()) return QString::fromStdString(it->first); diff --git a/avogadro/qtplugins/symmetry/symmetrywidget.cpp b/avogadro/qtplugins/symmetry/symmetrywidget.cpp index c3a6500d3a..5c368cc630 100644 --- a/avogadro/qtplugins/symmetry/symmetrywidget.cpp +++ b/avogadro/qtplugins/symmetry/symmetrywidget.cpp @@ -301,6 +301,7 @@ void SymmetryWidget::setCenterOfMass(double cm[3]) void SymmetryWidget::setPointGroupSymbol(QString pg) { m_ui->pointGroupLabel->setText(pg); + m_molecule->setData("pointgroup", pg.toStdString()); } void SymmetryWidget::setSymmetryOperations( From a27910c92411467819996fb03a935a9b57f3226c Mon Sep 17 00:00:00 2001 From: Geoff Hutchison Date: Mon, 25 Nov 2024 13:09:04 -0500 Subject: [PATCH 2/3] Add support for Hirshfeld charge analysis in Orca Signed-off-by: Geoff Hutchison --- avogadro/quantumio/orca.cpp | 33 +++++++++++++++++++++++++++++++++ avogadro/quantumio/orca.h | 1 + 2 files changed, 34 insertions(+) diff --git a/avogadro/quantumio/orca.cpp b/avogadro/quantumio/orca.cpp index 3ce4a32562..4a317db3f8 100644 --- a/avogadro/quantumio/orca.cpp +++ b/avogadro/quantumio/orca.cpp @@ -273,6 +273,11 @@ void ORCAOutput::processLine(std::istream& in, GaussianSet* basis) } else if (Core::contains(key, "MOLECULAR ORBITALS")) { m_currentMode = MO; getline(in, key); //------------ + } else if (Core::contains(key, "HIRSHFELD ANALYSIS")) { + m_currentMode = HirshfeldCharges; + for (unsigned int i = 0; i < 6; ++i) { + getline(in, key); // skip header + } } else if (Core::contains(key, "ATOMIC CHARGES")) { m_currentMode = Charges; // figure out what type of charges we have @@ -352,6 +357,34 @@ void ORCAOutput::processLine(std::istream& in, GaussianSet* basis) m_currentMode = NotParsing; break; } + case HirshfeldCharges: { + // should start at the first atom + if (key.empty()) + break; + + Eigen::MatrixXd charges(m_atomNums.size(), 1); + charges.setZero(); + + list = Core::split(key, ' '); + while (!key.empty()) { + if (list.size() != 4) { + break; + } + // e.g. index atom charge spin + // e.g. 0 O -0.714286 0.000 + int atomIndex = Core::lexicalCast(list[0]); + double charge = Core::lexicalCast(list[2]); + charges(atomIndex, 0) = charge; + + getline(in, key); + key = Core::trimmed(key); + list = Core::split(key, ' '); + } + + m_partialCharges["Hirshfeld"] = charges; + m_currentMode = NotParsing; + break; + } case Charges: { // should start at the first atom if (key.empty()) diff --git a/avogadro/quantumio/orca.h b/avogadro/quantumio/orca.h index 581937f802..728429225d 100644 --- a/avogadro/quantumio/orca.h +++ b/avogadro/quantumio/orca.h @@ -83,6 +83,7 @@ class AVOGADROQUANTUMIO_EXPORT ORCAOutput : public Io::FileFormat MO, OrbitalEnergies, Charges, + HirshfeldCharges, Frequencies, VibrationalModes, IR, From 3e1149c4cdabdb5e2d81fb63303f097c39ecf78d Mon Sep 17 00:00:00 2001 From: Geoff Hutchison Date: Mon, 25 Nov 2024 15:16:34 -0500 Subject: [PATCH 3/3] Fixup partial charge parsing in Orca and display in colors Signed-off-by: Geoff Hutchison --- .../qtplugins/applycolors/applycolors.cpp | 16 ++++++++++++---- avogadro/quantumio/orca.cpp | 19 +++++++++++-------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/avogadro/qtplugins/applycolors/applycolors.cpp b/avogadro/qtplugins/applycolors/applycolors.cpp index 4ca45fc4a7..28c5f66fd6 100644 --- a/avogadro/qtplugins/applycolors/applycolors.cpp +++ b/avogadro/qtplugins/applycolors/applycolors.cpp @@ -12,10 +12,12 @@ #include #include -#include #include #include #include +#include + +#include using namespace tinycolormap; @@ -248,14 +250,14 @@ void ApplyColors::applyChargeColors() // populate the dialog to choose the model and colormap ChargeColorDialog dialog; - for (const auto &model : identifiers) { + for (const auto& model : identifiers) { auto name = Calc::ChargeManager::instance().nameForModel(model); dialog.modelCombo->addItem(name.c_str(), model.c_str()); } dialog.exec(); if (dialog.result() != QDialog::Accepted) return; - + // get the model and colormap const auto model = dialog.modelCombo->currentData().toString().toStdString(); const auto colormapName = dialog.colorMapCombo->currentText(); @@ -267,6 +269,12 @@ void ApplyColors::applyChargeColors() float maxCharge = 0.0f; auto charges = Calc::ChargeManager::instance().partialCharges(model, *m_molecule); + // check if the model string is already a partial charge type + if (m_molecule->partialChargeTypes().find(model) != + m_molecule->partialChargeTypes().end()) { + charges = m_molecule->partialCharges(model); + } + for (Index i = 0; i < numAtoms; ++i) { float charge = charges(i, 0); minCharge = std::min(minCharge, charge); @@ -568,4 +576,4 @@ void ApplyColors::applyShapelyColors() m_molecule->emitChanged(QtGui::Molecule::Atoms); } -} // namespace Avogadro +} // namespace Avogadro::QtPlugins diff --git a/avogadro/quantumio/orca.cpp b/avogadro/quantumio/orca.cpp index 4a317db3f8..52c15598f6 100644 --- a/avogadro/quantumio/orca.cpp +++ b/avogadro/quantumio/orca.cpp @@ -60,14 +60,6 @@ bool ORCAOutput::read(std::istream& in, Core::Molecule& molecule) return false; } - // add the partial charges - if (m_partialCharges.size() > 0) { - for (auto it = m_partialCharges.begin(); it != m_partialCharges.end(); - ++it) { - molecule.setPartialCharges(it->first, it->second); - } - } - if (m_frequencies.size() > 0 && m_frequencies.size() == m_vibDisplacements.size() && m_frequencies.size() == m_IRintensities.size()) { @@ -143,6 +135,17 @@ bool ORCAOutput::read(std::istream& in, Core::Molecule& molecule) basis->setMolecule(&molecule); load(basis); + // we have to do a few things *after* any modifications to bonds / atoms + // because those automatically clear partial charges and data + + // add the partial charges + if (m_partialCharges.size() > 0) { + for (auto it = m_partialCharges.begin(); it != m_partialCharges.end(); + ++it) { + molecule.setPartialCharges(it->first, it->second); + } + } + molecule.setData("totalCharge", m_charge); molecule.setData("totalSpinMultiplicity", m_spin); molecule.setData("dipoleMoment", m_dipoleMoment);