From 9b69a530586cdb703ffb20e20832a25f79e11f87 Mon Sep 17 00:00:00 2001 From: Jared Swift Date: Mon, 13 Nov 2023 08:52:49 +0000 Subject: [PATCH] refactor: Reorganise CIF Import GUI, Molecular Supercell Generation & Configuration Outputs (#1679) Co-authored-by: Tristan Youngs --- src/gui/importCIFDialog.h | 6 +- src/gui/importCIFDialog.ui | 1567 ++++++++--------- src/gui/importCIFDialogFuncs.cpp | 144 +- src/io/import/cif.cpp | 275 +-- src/io/import/cif.h | 30 +- src/io/import/cifClasses.cpp | 2 + src/io/import/cifClasses.h | 1 + ...17789.cif => Bisphen_n_arenes_1517789.cif} | 0 tests/io/cif.cpp | 17 +- 9 files changed, 1041 insertions(+), 1001 deletions(-) rename tests/data/cif/{1517789.cif => Bisphen_n_arenes_1517789.cif} (100%) diff --git a/src/gui/importCIFDialog.h b/src/gui/importCIFDialog.h index 0d34345903..96920e40b0 100644 --- a/src/gui/importCIFDialog.h +++ b/src/gui/importCIFDialog.h @@ -41,6 +41,7 @@ class ImportCIFDialog : public WizardDialog CIFHandler cifHandler_; // Flags Flags updateFlags_; + Flags outputFlags_; private: // Apply CIF bond definitions within specified species @@ -58,8 +59,7 @@ class ImportCIFDialog : public WizardDialog CIFInfoPage, /* Basic CIF info page to check parsing */ StructurePage, /* Structure page */ CleanedPage, /* Cleaned structure page */ - SupercellPage, /* Options to create supercell */ - OutputSpeciesPage /* Output Species page */ + OutputSpeciesPage, /* Output Species page */ }; protected: @@ -146,6 +146,8 @@ class ImportCIFDialog : public WizardDialog Configuration *partitioningConfiguration_; private slots: + void on_OutputMolecularRadio_clicked(bool checked); void on_OutputFrameworkRadio_clicked(bool checked); void on_OutputSupermoleculeRadio_clicked(bool checked); + void on_OutputConfigurationCheck_clicked(bool checked); }; diff --git a/src/gui/importCIFDialog.ui b/src/gui/importCIFDialog.ui index 0ed2f56ffc..4dba20ba0d 100644 --- a/src/gui/importCIFDialog.ui +++ b/src/gui/importCIFDialog.ui @@ -10,7 +10,7 @@ 0 0 840 - 511 + 903 @@ -28,7 +28,7 @@ - 1 + 0 @@ -755,886 +755,811 @@ - - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - + - - - - 4 - - - 0 - - - 0 - - - 0 - - - - - Repeats + + + + + Supercell + + + + 4 - - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - - - - - - 0 - 0 - - - - A - - - - - - - 1 - - - 10000 - - - - - - - - 0 - 0 - - - - B - - - - - - - 1 - - - 10000 - - - - - - - - 0 - 0 - - - - C - - - - - - - 1 - - - 10000 - - - - - - - - - - - 0 - 0 - + + 0 - - Supercell Box + + 0 - - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - - - - - - 0 - 0 - - - - - 10 - true - - - - Type - - - - - - - - 0 - 0 - - - - - 10 - true - - - - α - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - 10 - true - - - - ρ - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - 10 - false - - - - Number of atoms in supercell - - - 0 - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - Supercell angle alpha - - - 0.0 - - - Qt::RichText - - - - - - - - 0 - 0 - - - - Supercell length along B - - - 0.0 - - - Qt::RichText - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 10 - false - - - - Orthorhombic + + 0 + + + + + Repeats + + + + 4 - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + 4 - - - - - - - 0 - 0 - + + 4 - - - 10 - true - + + 4 - - N + + 4 - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + 0 + 0 + + + + A + + + + + + + 1 + + + 10000 + + + + + + + + 0 + 0 + + + + B + + + + + + + 1 + + + 10000 + + + + + + + + 0 + 0 + + + + C + + + + + + + 1 + + + 10000 + + + + + + + + + + + 0 + 0 + + + + Supercell Box + + + + 4 - - - - - - - 0 - 0 - + + 4 - - Supercell length along A + + 4 - - 0.0 + + 4 - - Qt::RichText + + 4 - - - - - - - 0 - 0 - - - - Supercell length along C - - - 0.0 - - - Qt::RichText - - - - - - - - 0 - 0 - - - - - 10 - true - - - - V - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - Supercell angle gamma - - - 0.0 - - - Qt::RichText - - - - - - - - 0 - 0 - - - - - 10 - true - - - - A - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - 10 - true - - - - C - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - 10 - false - - - - Supercell volume - - - 0.0 - - - Qt::RichText - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - 10 - true - - - - γ - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - 10 - false - - - - Supercell density - - - 0.0 - - - Qt::RichText - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - 10 - true - - - - β - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - Supercell angle beta - - - 0.0 - - - Qt::RichText - - - - - - - - 0 - 0 - - - - - 10 - true - - - - B - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - 3 - 0 - - - - - 300 - 300 - - - - QFrame::StyledPanel - - - QFrame::Sunken - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 1 - 0 - - - - - - - - - - - - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - - - - - - 1 - 0 - - - - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - - - - - Output Species - - - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - - - - - Periodic Framework - - - true - - - - - - - + + + + + 0 + 0 + + + + + 10 + 75 + true + + + + Type + + + + + + + + 0 + 0 + + + + + 10 + 75 + true + + + + α + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 10 + 75 + true + + + + ρ + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 10 + 50 + false + + + + Number of atoms in supercell + + + 0 + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Supercell angle alpha + + + 0.0 + + + Qt::RichText + + + + + + + + 0 + 0 + + + + Supercell length along B + + + 0.0 + + + Qt::RichText + + + + + Qt::Horizontal - - QSizePolicy::Fixed + + + + + + + 0 + 0 + + + + + 10 + 50 + false + - - - 40 - 20 - + + Orthorhombic + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - + - - + + + + + 0 + 0 + + 10 - true + 75 + true - Create a single species with a periodic box and all atoms in the unit cell. Suitable for framework-style models. + N - - true + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - Supermolecule - - - false - - - - - - - - - Qt::Horizontal + + + + + 0 + 0 + - - QSizePolicy::Fixed + + Supercell length along A - - - 40 - 20 - + + 0.0 - + + Qt::RichText + + - - + + + + + 0 + 0 + + + + Supercell length along C + + + 0.0 + + + Qt::RichText + + + + + + + + 0 + 0 + + 10 - true + 75 + true - Create a single non-periodic species. + V - - true + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Supercell angle gamma + + + 0.0 + + + Qt::RichText + + + + + + + + 0 + 0 + + + + + 10 + 75 + true + + + + A + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 10 + 75 + true + + + + C + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 10 + 50 + false + + + + Supercell volume + + + 0.0 + + + Qt::RichText + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 10 + 75 + true + + + + γ + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 10 + 50 + false + + + + Supercell density + + + 0.0 + + + Qt::RichText + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 10 + 75 + true + + + + β + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Supercell angle beta + + + 0.0 + + + Qt::RichText + + + + + + + + 0 + 0 + + + + + 10 + 75 + true + + + + B + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal - - - - - - - - Qt::Vertical - - - - 20 - 263 - - - - - - + + + + + + + + + Output Species + + - - - - 0 - 0 - - - - - 16 - 16 - + + + Molecular Species + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + + true + + + + Create molecular species + + + true + + + + + + + + + + - + Periodic Framework - + true - + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + + 10 + true + + + + Create a single species with a periodic box and all atoms in the unit cell. Suitable for framework-style models. + + + true + + + + + + + + + Supermolecule + + + false + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + + 10 + true + + + + Create a single non-periodic species. + + + true + + + + + + + - ??? + Output a Configuration? - + true + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + true + + + + + + + ??? + + + true + + + + + - - - + + + - + 3 @@ -1653,7 +1578,7 @@ QFrame::Sunken - + 0 @@ -1670,7 +1595,7 @@ 0 - + 1 diff --git a/src/gui/importCIFDialogFuncs.cpp b/src/gui/importCIFDialogFuncs.cpp index 0c72d25130..6b4a570427 100644 --- a/src/gui/importCIFDialogFuncs.cpp +++ b/src/gui/importCIFDialogFuncs.cpp @@ -26,9 +26,8 @@ ImportCIFDialog::ImportCIFDialog(QWidget *parent, Dissolve &dissolve) registerPage(ImportCIFDialog::SelectSpaceGroupPage, "Choose Space Group", ImportCIFDialog::CIFInfoPage); registerPage(ImportCIFDialog::CIFInfoPage, "CIF Information", ImportCIFDialog::StructurePage); registerPage(ImportCIFDialog::StructurePage, "Basic Structure", ImportCIFDialog::CleanedPage); - registerPage(ImportCIFDialog::CleanedPage, "Clean Structure", ImportCIFDialog::SupercellPage); - registerPage(ImportCIFDialog::SupercellPage, "Create Supercell", ImportCIFDialog::OutputSpeciesPage); - registerPage(ImportCIFDialog::OutputSpeciesPage, "Species Partitioning"); + registerPage(ImportCIFDialog::CleanedPage, "Clean Structure", ImportCIFDialog::OutputSpeciesPage); + registerPage(ImportCIFDialog::OutputSpeciesPage, "Output"); // Add spacegroup list for (auto n = 1; n < SpaceGroups::nSpaceGroupIds; ++n) @@ -47,6 +46,10 @@ ImportCIFDialog::ImportCIFDialog(QWidget *parent, Dissolve &dissolve) // Update moiety NETA createMoietyRemovalNETA(ui_.MoietyNETARemovalEdit->text().toStdString()); + // Set default flags. + outputFlags_.setFlag(CIFHandler::OutputFlags::OutputFramework); + outputFlags_.setFlag(CIFHandler::OutputFlags::OutputConfiguration); + // Init the wizard initialise(this, ui_.MainStack, ImportCIFDialog::SelectCIFFilePage); } @@ -68,9 +71,7 @@ bool ImportCIFDialog::progressionAllowed(int index) const case (ImportCIFDialog::SelectSpaceGroupPage): return ui_.SpaceGroupsList->currentRow() != -1; case (ImportCIFDialog::OutputSpeciesPage): - // If the "Framework" or "Supermolecule" options are chosen, the "Crystal" species must be a single moiety - if (cifHandler_.isValid()) - return ui_.PartitioningIndicator->state() == CheckIndicator::OKState; + return ui_.OutputIndicator->state() == CheckIndicator::OKState; break; default: break; @@ -78,7 +79,6 @@ bool ImportCIFDialog::progressionAllowed(int index) const return true; } - // Perform any necessary actions before moving to the next page bool ImportCIFDialog::prepareForNextPage(int currentIndex) { @@ -109,13 +109,10 @@ bool ImportCIFDialog::prepareForNextPage(int currentIndex) return false; break; case (ImportCIFDialog::CleanedPage): + ui_.OutputMolecularRadio->setChecked(!cifHandler_.molecularSpecies().empty()); + outputFlags_.setFlag(CIFHandler::OutputFlags::OutputMolecularSpecies); update(); - if (!cifHandler_.supercellSpecies()) - return false; - break; - case (ImportCIFDialog::SupercellPage): - update(); - if (!cifHandler_.partitionedSpecies()) + if (!cifHandler_.supercellConfiguration()) return false; break; default: @@ -151,7 +148,7 @@ bool ImportCIFDialog::prepareForPreviousPage(int currentIndex) } // Perform any final actions before the wizard is closed -void ImportCIFDialog::finalise() { cifHandler_.finalise(dissolve_.coreData()); } +void ImportCIFDialog::finalise() { cifHandler_.finalise(dissolve_.coreData(), outputFlags_); } /* * Select CIF File Page @@ -345,20 +342,46 @@ void ImportCIFDialog::on_RepeatCSpin_valueChanged(int value) { update(); } * Species Partitioning Page */ +void ImportCIFDialog::on_OutputMolecularRadio_clicked(bool checked) +{ + if (checked) + outputFlags_.setFlag(CIFHandler::OutputFlags::OutputMolecularSpecies); + else + outputFlags_.removeFlag(CIFHandler::OutputFlags::OutputMolecularSpecies); + update(); +} + void ImportCIFDialog::on_OutputFrameworkRadio_clicked(bool checked) { if (checked) - updateFlags_.removeFlag(CIFHandler::UpdateFlags::CreateSupermolecule); + outputFlags_.setFlag(CIFHandler::OutputFlags::OutputFramework); else - updateFlags_.setFlag(CIFHandler::UpdateFlags::CreateSupermolecule); + outputFlags_.removeFlag(CIFHandler::OutputFlags::OutputFramework); + update(); } void ImportCIFDialog::on_OutputSupermoleculeRadio_clicked(bool checked) { if (checked) - updateFlags_.setFlag(CIFHandler::UpdateFlags::CreateSupermolecule); + { + outputFlags_.setFlag(CIFHandler::OutputFlags::OutputSupermolecule); + ui_.OutputConfigurationCheck->setChecked(false); + outputFlags_.removeFlag(CIFHandler::OutputFlags::OutputConfiguration); + } else - updateFlags_.removeFlag(CIFHandler::UpdateFlags::CreateSupermolecule); + { + outputFlags_.removeFlag(CIFHandler::OutputFlags::OutputSupermolecule); + } + update(); +} + +void ImportCIFDialog::on_OutputConfigurationCheck_clicked(bool checked) +{ + if (checked) + outputFlags_.setFlag(CIFHandler::OutputFlags::OutputConfiguration); + else + outputFlags_.removeFlag(CIFHandler::OutputFlags::OutputConfiguration); + update(); } bool ImportCIFDialog::update() @@ -372,54 +395,73 @@ bool ImportCIFDialog::update() ui_.StructureViewer->setConfiguration(cifHandler_.structuralUnitCellConfiguration()); ui_.CleanedViewer->setConfiguration(cifHandler_.cleanedUnitCellConfiguration()); - ui_.SupercellViewer->setConfiguration(cifHandler_.supercellConfiguration()); - ui_.PartitioningViewer->setConfiguration(cifHandler_.partitionedConfiguration()); - auto *supercell = cifHandler_.supercellSpecies(); + ui_.OutputViewer->setConfiguration(cifHandler_.supercellConfiguration()); + + auto *supercellConfiguration = cifHandler_.supercellConfiguration(); + + // Update the information panel + ui_.SupercellBoxALabel->setText(QString::number(supercellConfiguration->box()->axisLengths().x) + " Å"); + ui_.SupercellBoxBLabel->setText(QString::number(supercellConfiguration->box()->axisLengths().y) + " Å"); + ui_.SupercellBoxCLabel->setText(QString::number(supercellConfiguration->box()->axisLengths().z) + " Å"); + ui_.SupercellBoxAlphaLabel->setText(QString::number(supercellConfiguration->box()->axisAngles().x) + "°"); + ui_.SupercellBoxBetaLabel->setText(QString::number(supercellConfiguration->box()->axisAngles().y) + "°"); + ui_.SupercellBoxGammaLabel->setText(QString::number(supercellConfiguration->box()->axisAngles().z) + "°"); + auto chemicalDensity = cifHandler_.supercellConfiguration()->chemicalDensity(); + ui_.SupercellDensityLabel->setText(chemicalDensity ? QString::number(*chemicalDensity) + " g cm3" + : "-- g cm3"); + ui_.SupercellVolumeLabel->setText(QString::number(supercellConfiguration->box()->volume()) + " Å3"); + ui_.SupercellNAtomsLabel->setText(QString::number(supercellConfiguration->nAtoms())); + + if (ui_.OutputMolecularRadio->isChecked()) + { + ui_.OutputMolecularSpeciesList->setVisible(true); + ui_.OutputMolecularSpeciesList->clear(); + for (auto &molecularSp : cifHandler_.molecularSpecies()) + { + ui_.OutputMolecularSpeciesList->addItem(QString::fromStdString(std::string(molecularSp.species()->name()))); + } + } + else + { + ui_.OutputMolecularSpeciesList->setVisible(false); + } - if (supercell) + ui_.OutputConfigurationCheck->setEnabled(ui_.OutputMolecularRadio->isChecked() || ui_.OutputFrameworkRadio->isChecked()); + + if (ui_.OutputSupermoleculeRadio->isChecked()) { - // Update the information panel - ui_.SupercellBoxALabel->setText(QString::number(supercell->box()->axisLengths().x) + " Å"); - ui_.SupercellBoxBLabel->setText(QString::number(supercell->box()->axisLengths().y) + " Å"); - ui_.SupercellBoxCLabel->setText(QString::number(supercell->box()->axisLengths().z) + " Å"); - ui_.SupercellBoxAlphaLabel->setText(QString::number(supercell->box()->axisAngles().x) + "°"); - ui_.SupercellBoxBetaLabel->setText(QString::number(supercell->box()->axisAngles().y) + "°"); - ui_.SupercellBoxGammaLabel->setText(QString::number(supercell->box()->axisAngles().z) + "°"); - auto chemicalDensity = cifHandler_.supercellConfiguration()->chemicalDensity(); - ui_.SupercellDensityLabel->setText(chemicalDensity ? QString::number(*chemicalDensity) + " g cm3" - : "-- g cm3"); - ui_.SupercellVolumeLabel->setText(QString::number(cifHandler_.supercellConfiguration()->box()->volume()) + - " Å3"); - ui_.SupercellNAtomsLabel->setText(QString::number(cifHandler_.supercellConfiguration()->nAtoms())); + ui_.OutputConfigurationCheck->setChecked(false); + outputFlags_.removeFlag(CIFHandler::OutputFlags::OutputConfiguration); } auto validSpecies = true; + QString indicatorText("Species are valid."); - if (cifHandler_.molecularSpecies().empty()) + if (ui_.OutputMolecularRadio->isChecked()) { - // Update the indicator and label - cifHandler_.partitionedSpecies()->clearAtomSelection(); - if (cifHandler_.partitionedSpecies()->fragment(0).size() != cifHandler_.partitionedSpecies()->nAtoms()) + if (cifHandler_.molecularSpecies().empty()) { - ui_.PartitioningIndicator->setOK(false); - ui_.PartitioningLabel->setText("Species contains more than one molecule/fragment, and cannot be used in a " - "simulation. Choose a different partitioning."); - validSpecies = false; + indicatorText = + QString("Unable to generate molecular partitioning from Species, so cannot be used in a simulation. " + "Choose a different partitioning."); } } else { - ui_.PartitioningLayoutWidget->setEnabled(false); - ui_.PartitioningViewFrame->setEnabled(false); - ui_.PartitioningIndicator->setOK(true); + auto *supercell = cifHandler_.supercellSpecies(); + if (!supercell || supercell->nAtoms() != supercellConfiguration->molecule(0)->nAtoms()) + { + validSpecies = false; + indicatorText = QString("Species contains more than one molecule/fragment, and cannot be used in a " + "simulation. Choose a different partitioning."); + } } - if (validSpecies) - { - ui_.PartitioningIndicator->setOK(true); - ui_.PartitioningLabel->setText("Species are valid."); - } + ui_.OutputLabel->setText(indicatorText); + ui_.OutputIndicator->setOK(validSpecies); + + updateProgressionControls(); return true; } diff --git a/src/io/import/cif.cpp b/src/io/import/cif.cpp index d0cdb5b0a2..6e9c237c7e 100644 --- a/src/io/import/cif.cpp +++ b/src/io/import/cif.cpp @@ -27,8 +27,6 @@ CIFHandler::CIFHandler() molecularUnitCellConfiguration_->setName("Molecular"); supercellConfiguration_ = coreData_.addConfiguration(); supercellConfiguration_->setName("Supercell"); - partitionedConfiguration_ = coreData_.addConfiguration(); - partitionedConfiguration_->setName("Partitioned"); } /* @@ -621,7 +619,7 @@ bool CIFHandler::detectMolecules() } // Determine coordinates - auto coords = speciesCopiesCoordinates(cleanedUnitCellSpecies_, copies); + auto coords = speciesCopiesCoordinatesFromUnitCell(sp, cleanedUnitCellSpecies_->box(), copies); // Fix geometry fixGeometry(sp, cleanedUnitCellSpecies_->box()); @@ -650,10 +648,11 @@ bool CIFHandler::detectMolecules() // Create supercell species bool CIFHandler::createSupercell() { - supercellSpecies_ = coreData_.addSpecies(); - supercellSpecies_->setName("Supercell"); auto supercellLengths = cleanedUnitCellSpecies_->box()->axisLengths(); supercellLengths.multiply(supercellRepeat_.x, supercellRepeat_.y, supercellRepeat_.z); + + supercellSpecies_ = coreData_.addSpecies(); + supercellSpecies_->setName("Supercell"); supercellSpecies_->createBox(supercellLengths, cleanedUnitCellSpecies_->box()->axisAngles(), false); // Configuration @@ -662,25 +661,62 @@ bool CIFHandler::createSupercell() Messenger::setQuiet(false); // Copy atoms from the Crystal species - we'll do the bonding afterwards - supercellSpecies_->atoms().reserve(supercellRepeat_.x * supercellRepeat_.y * supercellRepeat_.z * - cleanedUnitCellSpecies_->nAtoms()); - for (auto ix = 0; ix < supercellRepeat_.x; ++ix) - for (auto iy = 0; iy < supercellRepeat_.y; ++iy) - for (auto iz = 0; iz < supercellRepeat_.z; ++iz) - { - Vec3 deltaR = cleanedUnitCellSpecies_->box()->axes() * Vec3(ix, iy, iz); - for (const auto &i : cleanedUnitCellSpecies_->atoms()) - supercellSpecies_->addAtom(i.Z(), i.r() + deltaR, 0.0, i.atomType()); - } - if (flags_.isSet(UpdateFlags::CalculateBonding)) - supercellSpecies_->addMissingBonds(); + if (molecularUnitCellSpecies_.empty()) + { + supercellSpecies_->atoms().reserve(supercellRepeat_.x * supercellRepeat_.y * supercellRepeat_.z * + cleanedUnitCellSpecies_->nAtoms()); + for (auto ix = 0; ix < supercellRepeat_.x; ++ix) + for (auto iy = 0; iy < supercellRepeat_.y; ++iy) + for (auto iz = 0; iz < supercellRepeat_.z; ++iz) + { + Vec3 deltaR = cleanedUnitCellSpecies_->box()->axes() * Vec3(ix, iy, iz); + for (const auto &i : cleanedUnitCellSpecies_->atoms()) + supercellSpecies_->addAtom(i.Z(), i.r() + deltaR, 0.0, i.atomType()); + } + if (flags_.isSet(UpdateFlags::CalculateBonding)) + supercellSpecies_->addMissingBonds(); + else + applyCIFBonding(supercellSpecies_, flags_.isSet(UpdateFlags::PreventMetallicBonding)); + + // Add the structural species to the configuration + supercellConfiguration_->addMolecule(supercellSpecies_); + supercellConfiguration_->updateObjectRelationships(); + } else - applyCIFBonding(supercellSpecies_, flags_.isSet(UpdateFlags::PreventMetallicBonding)); - - // Add the structural species to the configuration - supercellConfiguration_->addMolecule(supercellSpecies_); - supercellConfiguration_->updateObjectRelationships(); + { + for (auto &molecularSpecies : molecularUnitCellSpecies_) + { + const auto *sp = molecularSpecies.species(); + auto coordinates = molecularSpecies.coordinates(); + molecularSpecies.coordinates().clear(); + for (auto &instance : coordinates) + { + for (auto ix = 0; ix < supercellRepeat_.x; ++ix) + for (auto iy = 0; iy < supercellRepeat_.y; ++iy) + for (auto iz = 0; iz < supercellRepeat_.z; ++iz) + { + auto *spCopy = coreData_.addSpecies(); + spCopy->createBox(supercellLengths, cleanedUnitCellSpecies_->box()->axisAngles(), false); + Vec3 deltaR = cleanedUnitCellSpecies_->box()->axes() * Vec3(ix, iy, iz); + std::vector> repeated(instance.size()); + std::transform(instance.begin(), instance.end(), repeated.begin(), + [&](auto &coord) { return coord + deltaR; }); + for (const auto &&[i, r] : zip(sp->atoms(), repeated)) + spCopy->addAtom(i.Z(), r, 0.0, i.atomType()); + + if (flags_.isSet(UpdateFlags::CalculateBonding)) + spCopy->addMissingBonds(); + else + applyCIFBonding(spCopy, flags_.isSet(UpdateFlags::PreventMetallicBonding)); + + supercellConfiguration_->addMolecule(spCopy); + supercellConfiguration_->updateObjectRelationships(); + molecularSpecies.coordinates().insert(molecularSpecies.coordinates().end(), repeated); + } + } + } + } Messenger::print("Created ({}, {}, {}) supercell - {} atoms total.\n", supercellRepeat_.x, supercellRepeat_.y, supercellRepeat_.z, supercellConfiguration_->nAtoms()); @@ -688,25 +724,6 @@ bool CIFHandler::createSupercell() return true; } -// Create partitioned setup -bool CIFHandler::createPartitionedCell() -{ - partitionedSpecies_ = coreData_.addSpecies(); - partitionedSpecies_->copyBasic(supercellSpecies_); - partitionedConfiguration_->createBoxAndCells(supercellSpecies_->box()->axisLengths(), - supercellSpecies_->box()->axisAngles(), false, 1.0); - if (flags_.isSet(UpdateFlags::CreateSupermolecule)) - { - partitionedSpecies_->removePeriodicBonds(); - partitionedSpecies_->updateIntramolecularTerms(); - partitionedSpecies_->removeBox(); - } - // Add the partitioned species to the configuration - partitionedConfiguration_->addMolecule(partitionedSpecies_); - partitionedConfiguration_->updateObjectRelationships(); - return true; -} - // Reset all objects void CIFHandler::resetSpeciesAndConfigurations() { @@ -715,11 +732,9 @@ void CIFHandler::resetSpeciesAndConfigurations() cleanedUnitCellConfiguration_->empty(); cleanedUnitCellSpecies_ = nullptr; molecularUnitCellSpecies_.clear(); - molecularUnitCellConfiguration_->clear(); + molecularUnitCellConfiguration_->empty(); supercellSpecies_ = nullptr; supercellConfiguration_->empty(); - partitionedSpecies_ = nullptr; - partitionedConfiguration_->empty(); coreData_.species().clear(); coreData_.atomTypes().clear(); } @@ -752,88 +767,127 @@ bool CIFHandler::generate(std::optional> newFlags if (!createCleanedUnitCell()) return false; - // Try to detect molecules detectMolecules(); - // Create supercell if (!createSupercell()) return false; - if (molecularUnitCellSpecies_.empty()) - { - if (!createPartitionedCell()) - return false; - } - return true; } // Return whether the generated data is valid bool CIFHandler::isValid() const { - return !molecularUnitCellSpecies_.empty() || partitionedSpecies_->fragment(0).size() != partitionedSpecies_->nAtoms(); + return !molecularUnitCellSpecies_.empty() || supercellSpecies_->fragment(0).size() != supercellSpecies_->nAtoms(); } -std::pair, Configuration *> CIFHandler::finalise(CoreData &coreData) const +std::pair, Configuration *> CIFHandler::finalise(CoreData &coreData, + std::optional> flags) const { - std::vector species; + std::vector species; Configuration *configuration; - if (!molecularUnitCellSpecies_.empty()) + + if (flags->isSet(OutputFlags::OutputMolecularSpecies)) { - configuration = coreData.addConfiguration(); - configuration->setName(chemicalFormula()); + if (flags && flags->isSet(OutputFlags::OutputConfiguration)) + { - // Grab the generator - auto &generator = configuration->generator(); + configuration = coreData.addConfiguration(); + configuration->setName(chemicalFormula()); - // Add Box - auto boxNode = generator.createRootNode({}); - auto cellLengths = getCellLengths().value(); - auto cellAngles = getCellAngles().value(); - boxNode->keywords().set("Lengths", Vec3(cellLengths.get(0), cellLengths.get(1), cellLengths.get(2))); - boxNode->keywords().set("Angles", Vec3(cellAngles.get(0), cellAngles.get(1), cellAngles.get(2))); + // Grab the generator + auto &generator = configuration->generator(); - for (auto &cifMolecularSp : molecularUnitCellSpecies_) - { - auto *sp = cifMolecularSp.species(); - // Add the species - sp = coreData.copySpecies(cifMolecularSp.species()); - - // Determine a unique suffix - auto base = sp->name(); - std::string uniqueSuffix{base}; - if (!generator.nodes().empty()) + // Add Box + auto boxNode = generator.createRootNode({}); + auto cellLengths = supercellConfiguration_->box()->axisLengths(); + auto cellAngles = supercellConfiguration_->box()->axisAngles(); + boxNode->keywords().set("Lengths", Vec3(cellLengths.get(0), cellLengths.get(1), cellLengths.get(2))); + boxNode->keywords().set("Angles", Vec3(cellAngles.get(0), cellAngles.get(1), cellAngles.get(2))); + + for (auto &cifMolecularSp : molecularUnitCellSpecies_) { - // Start from the last root node - auto root = generator.nodes().back(); - auto suffix = 0; + // Add the species + auto *sp = coreData.copySpecies(cifMolecularSp.species()); + species.push_back(sp); + + // Determine a unique suffix + auto base = sp->name(); + std::string uniqueSuffix{base}; + if (!generator.nodes().empty()) + { + // Start from the last root node + auto root = generator.nodes().back(); + auto suffix = 0; + + while (generator.rootSequence().nodeInScope(root, fmt::format("SymmetryCopies_{}", uniqueSuffix)) != + nullptr) + uniqueSuffix = fmt::format("{}_{:02d}", base, ++suffix); + } // We use 'CoordinateSets' here, because in this instance we are working with (CoordinateSet, Add) pairs - while (generator.rootSequence().nodeInScope(root, fmt::format("SymmetryCopies_{}", uniqueSuffix)) != nullptr) - uniqueSuffix = fmt::format("{}_{:02d}", base, ++suffix); + + // CoordinateSets + auto coordsNode = + generator.createRootNode(fmt::format("SymmetryCopies_{}", uniqueSuffix), sp); + coordsNode->keywords().setEnumeration("Source", CoordinateSetsProcedureNode::CoordinateSetSource::File); + coordsNode->setSets(cifMolecularSp.coordinates()); + + // Add + auto addNode = generator.createRootNode(fmt::format("Add_{}", uniqueSuffix), coordsNode); + addNode->keywords().set("Population", NodeValueProxy(int(cifMolecularSp.coordinates().size()))); + addNode->keywords().setEnumeration("Positioning", AddProcedureNode::PositioningType::Current); + addNode->keywords().set("Rotate", false); + addNode->keywords().setEnumeration("BoxAction", AddProcedureNode::BoxActionStyle::None); } + } + else + { + for (auto &cifMolecularSp : molecularUnitCellSpecies_) + { + auto *sp = cifMolecularSp.species(); + // Add the species + sp = coreData.copySpecies(cifMolecularSp.species()); + species.push_back(sp); + } + } + } + else + { + auto *sp = coreData.addSpecies(); + sp->copyBasic(supercellSpecies_); + species.push_back(sp); + if (flags->isSet(OutputFlags::OutputFramework)) + { + sp->removePeriodicBonds(); + sp->updateIntramolecularTerms(); + sp->removeBox(); + } - // CoordinateSets - auto coordsNode = - generator.createRootNode(fmt::format("SymmetryCopies_{}", uniqueSuffix), sp); - coordsNode->keywords().setEnumeration("Source", CoordinateSetsProcedureNode::CoordinateSetSource::File); - coordsNode->setSets(cifMolecularSp.coordinates()); + if (flags->isSet(OutputFlags::OutputConfiguration)) + { + configuration = coreData.addConfiguration(); + configuration->setName(chemicalFormula()); + + // Grab the generator + auto &generator = configuration->generator(); + + // Add Box + auto boxNode = generator.createRootNode({}); + auto cellLengths = supercellConfiguration_->box()->axisLengths(); + auto cellAngles = supercellConfiguration_->box()->axisAngles(); + boxNode->keywords().set("Lengths", Vec3(cellLengths.get(0), cellLengths.get(1), cellLengths.get(2))); + boxNode->keywords().set("Angles", Vec3(cellAngles.get(0), cellAngles.get(1), cellAngles.get(2))); // Add - auto addNode = generator.createRootNode(fmt::format("Add_{}", uniqueSuffix), coordsNode); - addNode->keywords().set("Population", NodeValueProxy(int(cifMolecularSp.coordinates().size()))); + auto addNode = generator.createRootNode(fmt::format("Add_{}", sp->name()), sp); + addNode->keywords().set("Population", NodeValueProxy(1)); addNode->keywords().setEnumeration("Positioning", AddProcedureNode::PositioningType::Current); addNode->keywords().set("Rotate", false); addNode->keywords().setEnumeration("BoxAction", AddProcedureNode::BoxActionStyle::None); } } - else if (supercellSpecies_) - { - auto *sp = coreData.addSpecies(); - sp->setName(chemicalFormula()); - sp->copyBasic(supercellSpecies_); - species.push_back(sp); - } + return {species, configuration}; } @@ -847,18 +901,12 @@ Configuration *CIFHandler::cleanedUnitCellConfiguration() { return cleanedUnitCe // Molecular const std::vector &CIFHandler::molecularSpecies() const { return molecularUnitCellSpecies_; } - Configuration *CIFHandler::molecularConfiguration() { return molecularUnitCellConfiguration_; } // Supercell Species *CIFHandler::supercellSpecies() { return supercellSpecies_; } Configuration *CIFHandler::supercellConfiguration() { return supercellConfiguration_; } -// Partitioned -Species *CIFHandler::partitionedSpecies() { return partitionedSpecies_; } - -Configuration *CIFHandler::partitionedConfiguration() { return partitionedConfiguration_; } - /* * Helpers */ @@ -932,14 +980,31 @@ std::vector> CIFHandler::speciesCopies(Species *sp, NETADefinit } // Determine coordinates of copies -std::vector>> CIFHandler::speciesCopiesCoordinates(Species *sp, std::vector> copies) +std::vector>> +CIFHandler::speciesCopiesCoordinatesFromUnitCell(Species *moleculeSp, const Box *box, + const std::vector> &copies) { std::vector>> coordinates; for (auto © : copies) { - std::vector> coords(copy.size()); - std::transform(copy.begin(), copy.end(), coords.begin(), [&](auto i) { return sp->atom(i).r(); }); - coordinates.push_back(std::move(coords)); + auto &coords = coordinates.emplace_back(); + coords.resize(copy.size()); + std::transform(copy.begin(), copy.end(), coords.begin(), [&](auto i) { return cleanedUnitCellSpecies_->atom(i).r(); }); + + std::shared_ptr mol = std::make_shared(); + std::vector molAtoms(moleculeSp->nAtoms()); + for (auto &&[spAtom, atom, r] : zip(moleculeSp->atoms(), molAtoms, coords)) + { + atom.setSpeciesAtom(&spAtom); + atom.setCoordinates(r); + mol->addAtom(&atom); + } + + // Unfold the molecule + mol->unFold(box); + + for (auto &&[r, atom] : zip(coords, molAtoms)) + r = atom.r(); } return coordinates; } diff --git a/src/io/import/cif.h b/src/io/import/cif.h index 1917f31f75..f3427ecbc8 100644 --- a/src/io/import/cif.h +++ b/src/io/import/cif.h @@ -100,12 +100,20 @@ class CIFHandler enum UpdateFlags { CleanMoietyRemoveAtomics, /* Remove atoms of single moiety */ - CleanMoietyRemoveWater, /* Remove water molecules of single moiety */ - CleanMoietyRemoveNETA, /* Remove single atoms by NETA definition */ - CleanRemoveBoundFragments, /* Remove entire fragments when using NETA definition */ + CleanMoietyRemoveWater, /* Remove water molecules of single moiety */ + CleanMoietyRemoveNETA, /* Remove single atoms by NETA definition */ + CleanRemoveBoundFragments, /* Remove entire fragments when using NETA definition */ CalculateBonding, /* Calculate bonding */ PreventMetallicBonding, /* Prevent metallic bonding */ - CreateSupermolecule + }; + + // CIF Species Output Flags + enum OutputFlags + { + OutputConfiguration, /* Output a Configuration */ + OutputMolecularSpecies, /* Partitioning - output molecular species */ + OutputFramework, /* Partitioning - output a framework species */ + OutputSupermolecule /* Partitioning - output a supermolecule */ }; private: @@ -129,9 +137,6 @@ class CIFHandler // Supercell Species *supercellSpecies_; Configuration *supercellConfiguration_; - // Final supercell, partitioned accordingly - Species *partitionedSpecies_; - Configuration *partitionedConfiguration_; private: // Create basic unit cell @@ -142,8 +147,6 @@ class CIFHandler bool detectMolecules(); // Create supercell species bool createSupercell(); - // Create partitioned cell - bool createPartitionedCell(); public: // Reset all objects @@ -159,7 +162,8 @@ class CIFHandler // Recreate the data bool generate(std::optional> newFlags = {}); // Finalise, returning the required species and resulting configuration - std::pair, Configuration *> finalise(CoreData &coreData) const; + std::pair, Configuration *> finalise(CoreData &coreData, + std::optional> flags = {}) const; // Return whether the generated data is valid bool isValid() const; // Structural @@ -174,9 +178,6 @@ class CIFHandler // Supercell Species *supercellSpecies(); Configuration *supercellConfiguration(); - // Partitioned - Species *partitionedSpecies(); - Configuration *partitionedConfiguration(); /* * Helpers @@ -189,7 +190,8 @@ class CIFHandler // Determine instances of a NETA definition in a given species std::vector> speciesCopies(Species *sp, NETADefinition neta); // Determine coordinates of instances in a given species - std::vector>> speciesCopiesCoordinates(Species *sp, std::vector> copies); + std::vector>> speciesCopiesCoordinatesFromUnitCell(Species *moleculeSp, const Box *box, + const std::vector> &copies); // 'Fix' the geometry of a given species void fixGeometry(Species *sp, const Box *box); }; diff --git a/src/io/import/cifClasses.cpp b/src/io/import/cifClasses.cpp index abefff87ef..fbf4fb40c3 100644 --- a/src/io/import/cifClasses.cpp +++ b/src/io/import/cifClasses.cpp @@ -110,3 +110,5 @@ const std::vector> &CIFMolecularSpecies::instances() const { re // Return coordinates of instances const std::vector>> &CIFMolecularSpecies::coordinates() const { return coordinates_; } + +std::vector>> &CIFMolecularSpecies::coordinates() { return coordinates_; } \ No newline at end of file diff --git a/src/io/import/cifClasses.h b/src/io/import/cifClasses.h index 325092b526..89a5362ae9 100644 --- a/src/io/import/cifClasses.h +++ b/src/io/import/cifClasses.h @@ -140,4 +140,5 @@ class CIFMolecularSpecies const std::vector> &instances() const; // Return coordinates of instances const std::vector>> &coordinates() const; + std::vector>> &coordinates(); }; diff --git a/tests/data/cif/1517789.cif b/tests/data/cif/Bisphen_n_arenes_1517789.cif similarity index 100% rename from tests/data/cif/1517789.cif rename to tests/data/cif/Bisphen_n_arenes_1517789.cif diff --git a/tests/io/cif.cpp b/tests/io/cif.cpp index 5b28f8d728..04b5561dfb 100644 --- a/tests/io/cif.cpp +++ b/tests/io/cif.cpp @@ -14,13 +14,16 @@ class ImportCIFTest : public ::testing::Test // Molecular species information using MolecularSpeciesInfo = std::tuple; // Test Box definition - void testBox(const Configuration *cfg, const Vec3 &lengths, int nAtoms) + void testBox(const Configuration *cfg, const Vec3 &lengths, const Vec3 &angles, int nAtoms) { ASSERT_TRUE(cfg); EXPECT_EQ(cfg->nAtoms(), nAtoms); EXPECT_NEAR(cfg->box()->axisLengths().x, lengths.x, 1.0e-6); EXPECT_NEAR(cfg->box()->axisLengths().y, lengths.y, 1.0e-6); EXPECT_NEAR(cfg->box()->axisLengths().z, lengths.z, 1.0e-6); + EXPECT_NEAR(cfg->box()->axisAngles().x, angles.x, 1.0e-6); + EXPECT_NEAR(cfg->box()->axisAngles().y, angles.y, 1.0e-6); + EXPECT_NEAR(cfg->box()->axisAngles().z, angles.z, 1.0e-6); } // Test molecular species information provided void testMolecularSpecies(const CIFMolecularSpecies &molSp, const MolecularSpeciesInfo &info) @@ -36,8 +39,7 @@ TEST_F(ImportCIFTest, Parse) { // Test files auto cifPath = "cif/"; - std::vector cifs = {"1517789.cif", "1557470.cif", "1557599.cif", "7705246.cif", - "9000004.cif", "9000095.cif", "9000418.cif"}; + std::vector cifs = {"1557470.cif", "1557599.cif", "7705246.cif", "9000004.cif", "9000095.cif", "9000418.cif"}; for (auto &cif : cifs) { @@ -56,7 +58,7 @@ TEST_F(ImportCIFTest, NaCl) EXPECT_EQ(cifHandler.spaceGroup(), SpaceGroups::SpaceGroup_225); auto *cfg = cifHandler.cleanedUnitCellConfiguration(); constexpr double A = 5.62; - testBox(cifHandler.cleanedUnitCellConfiguration(), {A, A, A}, 8); + testBox(cifHandler.cleanedUnitCellConfiguration(), {A, A, A}, {90, 90, 90}, 8); // Check molecular species EXPECT_EQ(cifHandler.molecularSpecies().size(), 2); @@ -71,7 +73,7 @@ TEST_F(ImportCIFTest, NaCl) // 2x2x2 supercell cifHandler.setSupercellRepeat({2, 2, 2}); EXPECT_TRUE(cifHandler.generate()); - testBox(cifHandler.supercellConfiguration(), {A * 2, A * 2, A * 2}, 8 * 8); + testBox(cifHandler.supercellConfiguration(), {A * 2, A * 2, A * 2}, {90, 90, 90}, 8 * 8); } TEST_F(ImportCIFTest, NaClO3) @@ -83,7 +85,7 @@ TEST_F(ImportCIFTest, NaClO3) // Check basic info EXPECT_EQ(cifHandler.spaceGroup(), SpaceGroups::SpaceGroup_198); constexpr double A = 6.55; - testBox(cifHandler.structuralUnitCellConfiguration(), {A, A, A}, 20); + testBox(cifHandler.structuralUnitCellConfiguration(), {A, A, A}, {90, 90, 90}, 20); // No geometry definitions present in the CIF file, so we expect species for each atomic component (4 Na, 4 Cl, and 12 O) auto &cifMols = cifHandler.molecularSpecies(); @@ -108,7 +110,7 @@ TEST_F(ImportCIFTest, CuBTC) // Check basic info EXPECT_EQ(cifHandler.spaceGroup(), SpaceGroups::SpaceGroup_225); constexpr auto A = 26.3336; - testBox(cifHandler.structuralUnitCellConfiguration(), {A, A, A}, 672); + testBox(cifHandler.structuralUnitCellConfiguration(), {A, A, A}, {90, 90, 90}, 672); // 16 basic formula units per unit cell constexpr auto N = 16; @@ -131,5 +133,4 @@ TEST_F(ImportCIFTest, CuBTC) EXPECT_EQ(EmpiricalFormula::formula(cifHandler.cleanedUnitCellSpecies()->atoms(), [](const auto &i) { return i.Z(); }), EmpiricalFormula::formula(cellFormulaNH2)); } - } // namespace UnitTest