From 3bd34d10199b3f77da4f8ad159bf5261b220af47 Mon Sep 17 00:00:00 2001 From: Geoff Hutchison Date: Tue, 26 Jul 2022 23:05:28 -0400 Subject: [PATCH] Initial support for total charge and spin through CJSON. Further support needed in input generators, etc. Signed-off-by: Geoff Hutchison --- avogadro/core/molecule.cpp | 17 +++++++++ avogadro/core/molecule.h | 11 +++++- avogadro/io/cjsonformat.cpp | 69 +++++++++++++++++++++++++------------ 3 files changed, 74 insertions(+), 23 deletions(-) diff --git a/avogadro/core/molecule.cpp b/avogadro/core/molecule.cpp index da1963898..ab49fa537 100644 --- a/avogadro/core/molecule.cpp +++ b/avogadro/core/molecule.cpp @@ -321,6 +321,23 @@ const Array& Molecule::formalCharges() const return m_formalCharges; } +signed char Molecule::totalCharge() const +{ + signed char charge = 0; + + // check the data map first + if (m_data.hasValue("totalCharge")) { + charge = m_data.value("totalCharge").toInt(); + } + + if (m_formalCharges.size() > 0) { + for (Index i = 0; i < m_formalCharges.size(); ++i) + charge += m_formalCharges[i]; + return charge; + } + return charge; // should be zero +} + Array& Molecule::colors() { return m_colors; diff --git a/avogadro/core/molecule.h b/avogadro/core/molecule.h index 8fbb6465b..3460ad1c3 100644 --- a/avogadro/core/molecule.h +++ b/avogadro/core/molecule.h @@ -136,6 +136,15 @@ class AVOGADROCORE_EXPORT Molecule /** \overload */ const Array& formalCharges() const; + /** + * Get the total charge on the molecule. + * The method will first check to see if a total charge has been set. If not, + * it will calculate the total charge from the formal charges (if set). + * If neither has been set, it will assume the total charge is zero. + * @return The total charge of the molecule. + */ + signed char totalCharge() const; + /** * Get the formal charge for the requested atom. * @param atomId The index of the atom. @@ -159,7 +168,7 @@ class AVOGADROCORE_EXPORT Molecule */ bool setFormalCharge(Index atomId, signed char charge); - /** Returns a vector of colors for the atoms in the moleucle. */ + /** \returns a vector of colors for the atoms in the moleucle. */ Array& colors(); /** \overload */ diff --git a/avogadro/io/cjsonformat.cpp b/avogadro/io/cjsonformat.cpp index a30dcb554..0b817cde1 100644 --- a/avogadro/io/cjsonformat.cpp +++ b/avogadro/io/cjsonformat.cpp @@ -119,7 +119,7 @@ bool CjsonFormat::read(std::istream& file, Molecule& molecule) // This represents our minimal spec for a molecule - atoms that have an // atomic number. if (isNumericArray(atomicNumbers) && atomicNumbers.size() > 0) { - for (auto & atomicNumber : atomicNumbers) + for (auto& atomicNumber : atomicNumbers) molecule.addAtom(atomicNumber); } else { appendError("Malformed array for in atoms.elements.number"); @@ -367,21 +367,21 @@ bool CjsonFormat::read(std::istream& file, Molecule& molecule) json occupations = orbitals["occupations"]; if (isNumericArray(occupations)) { std::vector occs; - for (auto & occupation : occupations) + for (auto& occupation : occupations) occs.push_back(static_cast(occupation)); basis->setMolecularOrbitalOccupancy(occupations); } json energies = orbitals["energies"]; if (isNumericArray(energies)) { std::vector energyArray; - for (auto & energie : energies) + for (auto& energie : energies) energyArray.push_back(static_cast(energie)); basis->setMolecularOrbitalEnergy(energyArray); } json numbers = orbitals["numbers"]; if (isNumericArray(numbers)) { std::vector numArray; - for (auto & number : numbers) + for (auto& number : numbers) numArray.push_back(static_cast(number)); basis->setMolecularOrbitalNumber(numArray); } @@ -390,16 +390,16 @@ bool CjsonFormat::read(std::istream& file, Molecule& molecule) json moCoefficientsB = orbitals["betaCoefficients"]; if (isNumericArray(moCoefficients)) { std::vector coeffs; - for (auto & moCoefficient : moCoefficients) + for (auto& moCoefficient : moCoefficients) coeffs.push_back(static_cast(moCoefficient)); basis->setMolecularOrbitals(coeffs); } else if (isNumericArray(moCoefficientsA) && isNumericArray(moCoefficientsB)) { std::vector coeffsA; - for (auto & i : moCoefficientsA) + for (auto& i : moCoefficientsA) coeffsA.push_back(static_cast(i)); std::vector coeffsB; - for (auto & i : moCoefficientsB) + for (auto& i : moCoefficientsB) coeffsB.push_back(static_cast(i)); basis->setMolecularOrbitals(coeffsA, BasisSet::Alpha); basis->setMolecularOrbitals(coeffsB, BasisSet::Beta); @@ -416,16 +416,16 @@ bool CjsonFormat::read(std::istream& file, Molecule& molecule) moCoefficientsB = orbSets[idx]["betaCoefficients"]; if (isNumericArray(moCoefficients)) { std::vector coeffs; - for (auto & moCoefficient : moCoefficients) + for (auto& moCoefficient : moCoefficients) coeffs.push_back(static_cast(moCoefficient)); basis->setMolecularOrbitals(coeffs, BasisSet::Paired, idx); } else if (isNumericArray(moCoefficientsA) && isNumericArray(moCoefficientsB)) { std::vector coeffsA; - for (auto & i : moCoefficientsA) + for (auto& i : moCoefficientsA) coeffsA.push_back(static_cast(i)); std::vector coeffsB; - for (auto & i : moCoefficientsB) + for (auto& i : moCoefficientsB) coeffsB.push_back(static_cast(i)); basis->setMolecularOrbitals(coeffsA, BasisSet::Alpha, idx); basis->setMolecularOrbitals(coeffsB, BasisSet::Beta, idx); @@ -444,7 +444,7 @@ bool CjsonFormat::read(std::istream& file, Molecule& molecule) json frequencies = vibrations["frequencies"]; if (isNumericArray(frequencies)) { Array freqs; - for (auto & frequencie : frequencies) { + for (auto& frequencie : frequencies) { freqs.push_back(static_cast(frequencie)); } molecule.setVibrationFrequencies(freqs); @@ -452,7 +452,7 @@ bool CjsonFormat::read(std::istream& file, Molecule& molecule) json intensities = vibrations["intensities"]; if (isNumericArray(intensities)) { Array intens; - for (auto & intensitie : intensities) { + for (auto& intensitie : intensities) { intens.push_back(static_cast(intensitie)); } molecule.setVibrationIRIntensities(intens); @@ -460,7 +460,7 @@ bool CjsonFormat::read(std::istream& file, Molecule& molecule) json raman = vibrations["ramanIntensities"]; if (isNumericArray(raman)) { Array intens; - for (auto & i : raman) { + for (auto& i : raman) { intens.push_back(static_cast(i)); } molecule.setVibrationRamanIntensities(intens); @@ -473,7 +473,7 @@ bool CjsonFormat::read(std::istream& file, Molecule& molecule) Array mode; mode.resize(arr.size() / 3); double* ptr = &mode[0][0]; - for (auto & j : arr) { + for (auto& j : arr) { *(ptr++) = static_cast(j); } disps.push_back(mode); @@ -483,6 +483,22 @@ bool CjsonFormat::read(std::istream& file, Molecule& molecule) } } + // properties + if (jsonRoot.find("properties") != jsonRoot.end()) { + json properties = jsonRoot["properties"]; + if (properties.is_object()) { + if (properties.find("totalCharge") != properties.end()) { + molecule.setData("totalCharge", + static_cast(properties["totalCharge"])); + } + if (properties.find("totalSpinMultiplicity") != properties.end()) { + molecule.setData("totalSpinMultiplicity", + static_cast(properties["totalSpinMultiplicity"])); + } + // todo - put everything into the data map + } + } + if (jsonRoot.find("layer") != jsonRoot.end()) { auto names = LayerManager::getMoleculeInfo(&molecule); json visible = jsonRoot["layer"]["visible"]; @@ -537,6 +553,15 @@ bool CjsonFormat::write(std::ostream& file, const Molecule& molecule) root["inchi"] = molecule.data("inchi").toString().c_str(); } + json properties; + properties["totalCharge"] = molecule.totalCharge(); + if (molecule.data("totalSpinMultiplicity").type() == Variant::Int) + properties["totalSpinMultiplicity"] = + molecule.data("totalSpinMultiplicity").toInt(); + else + properties["totalSpinMultiplicity"] = 1; // assume singlet if not specified + root["properties"] = properties; + if (molecule.unitCell()) { json unitCell; unitCell["a"] = molecule.unitCell()->a(); @@ -605,7 +630,7 @@ bool CjsonFormat::write(std::ostream& file, const Molecule& molecule) auto atomIndices = gaussian->atomIndices(); json shellToAtomMap; - for (unsigned int & atomIndice : atomIndices) + for (unsigned int& atomIndice : atomIndices) shellToAtomMap.push_back(atomIndice); basis["shellToAtomMap"] = shellToAtomMap; @@ -648,7 +673,7 @@ bool CjsonFormat::write(std::ostream& file, const Molecule& molecule) auto energies = gaussian->moEnergy(); if (energies.size() > 0) { json energyData; - for (double & energie : energies) { + for (double& energie : energies) { energyData.push_back(energie); } root["orbitals"]["energies"] = energyData; @@ -656,14 +681,14 @@ bool CjsonFormat::write(std::ostream& file, const Molecule& molecule) auto occ = gaussian->moOccupancy(); if (occ.size() > 0) { json occData; - for (unsigned char & it : occ) + for (unsigned char& it : occ) occData.push_back(static_cast(it)); root["orbitals"]["occupations"] = occData; } auto num = gaussian->moNumber(); if (num.size() > 0) { json numData; - for (unsigned int & it : num) + for (unsigned int& it : num) numData.push_back(it); root["orbitals"]["numbers"] = numData; } @@ -726,7 +751,7 @@ bool CjsonFormat::write(std::ostream& file, const Molecule& molecule) if (molecule.atomPositions3d().size() == molecule.atomCount()) { // everything gets real-space Cartesians json coords3d; - for (const auto & it : molecule.atomPositions3d()) { + for (const auto& it : molecule.atomPositions3d()) { coords3d.push_back(it.x()); coords3d.push_back(it.y()); coords3d.push_back(it.z()); @@ -739,7 +764,7 @@ bool CjsonFormat::write(std::ostream& file, const Molecule& molecule) Array fcoords; CrystalTools::fractionalCoordinates( *molecule.unitCell(), molecule.atomPositions3d(), fcoords); - for (auto & fcoord : fcoords) { + for (auto& fcoord : fcoords) { coordsFractional.push_back(fcoord.x()); coordsFractional.push_back(fcoord.y()); coordsFractional.push_back(fcoord.z()); @@ -751,7 +776,7 @@ bool CjsonFormat::write(std::ostream& file, const Molecule& molecule) // 2d positions: if (molecule.atomPositions2d().size() == molecule.atomCount()) { json coords2d; - for (const auto & it : molecule.atomPositions2d()) { + for (const auto& it : molecule.atomPositions2d()) { coords2d.push_back(it.x()); coords2d.push_back(it.y()); } @@ -903,4 +928,4 @@ vector CjsonFormat::mimeTypes() const return mime; } -} // namespace Avogadro +} // namespace Avogadro::Io