diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0cebc05..816fb79 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,17 @@ Changelog ========= +Release 1.4.0 +------------- + +Changes (DB schema changing) + - Remove `MODEL_TRANSFORMATION` elementary step type + +Additions (backwards compatible): + - Exact type of stored integers in the database is checked before retrieval in case fields have been manipulated with other MongoDB applications + - Projection options are added to almost all internal database queries to reduce the bandwidth requirements. + - Added support for list-of-lists of integers as elements in value collections (calculation settings etc.). + Release 1.3.0 ------------- @@ -13,8 +24,12 @@ Changes (DB schema changing): Additions (backwards compatible): - Python package now includes pure Python code for more complex query utilities. This code is added in the modules - `queries`, `energy_query_functions`, `compound_and_flask_creation`, `concentration_query_functions`, and `insert_concentration`. - All of them are top level modules (`import scine_database.energy_query_functions`) + - `queries` + - `energy_query_functions` + - `compound_and_flask_creation` + - `concentration_query_functions` + - `insert_concentration`. + All of them are top level modules (`import scine_database.energy_query_functions`) - Added the possibility to configure the connection based on a custom URI instead of credentials. Release 1.2.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 98acc25..7207e8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.9) project(Database - VERSION 1.3.0 + VERSION 1.4.0 DESCRIPTION "The SCINE database interface." ) diff --git a/README.rst b/README.rst index 92b8b3d..11b637f 100644 --- a/README.rst +++ b/README.rst @@ -66,6 +66,13 @@ J. P. Unsleber, S. A. Grimmel, M. Reiher, "Chemoton 2.0: Autonomous Exploration of Chemical Reaction Networks", *J. Chem. Theory Comput.*, **2022**, *18*, 5393. +Furthermore, when publishing results obtained with any SCINE module, please cite the following paper: + +T. Weymuth, J. P. Unsleber, P. L. Türtscher, M. Steiner, J.-G. Sobez, C. H. Müller, M. Mörchen, +V. Klasovita, S. A. Grimmel, M. Eckhoff, K.-S. Csizi, F. Bosia, M. Bensberg, M. Reiher, +"SCINE—Software for chemical interaction networks", *J. Chem. Phys.*, **2024**, *160*, 222501 +(DOI `10.1063/5.0206974 `_). + Support and Contact ------------------- diff --git a/conanfile.py b/conanfile.py index e96b2c7..b9a2255 100644 --- a/conanfile.py +++ b/conanfile.py @@ -10,7 +10,7 @@ class ScineDatabaseConan(ScineConan): name = "scine_database" - version = "1.3.0" + version = "1.4.0" url = "https://github.com/qcscine/database" description = """ The SCINE Database is a database wrapper for a MongoDB encoding reaction @@ -48,7 +48,7 @@ class ScineDatabaseConan(ScineConan): requires = [ "mongo-cxx-driver/3.6.0", "eigen/[~=3.3.7]", - "scine_utilities/9.0.0" + "scine_utilities/10.0.0" ] cmake_name = "Database" cmake_definitions = { diff --git a/dev b/dev index 518ab3c..2712fd3 160000 --- a/dev +++ b/dev @@ -1 +1 @@ -Subproject commit 518ab3c7f8a0a724081fcd7ed518c669724bcd37 +Subproject commit 2712fd3204d703c777244b077066ad75c6bc0db1 diff --git a/src/Database/Database/Exceptions.h b/src/Database/Database/Exceptions.h index bd82e35..fa81a99 100644 --- a/src/Database/Database/Exceptions.h +++ b/src/Database/Database/Exceptions.h @@ -25,6 +25,17 @@ class DuplicateIDException : public std::exception { } }; +/** + * @class SelfDuplicateException + * @brief An exception to throw if a given structure is a duplicate of itself. + */ +class SelfDuplicateException : public std::exception { + public: + const char* what() const throw() { + return "The given structure is a duplicate of itself. This cannot be."; + } +}; + /** * @class MissingCredentialsException * @brief An exception to throw if no credentials are present to establish a connection to a database. diff --git a/src/Database/Database/Layout.cpp b/src/Database/Database/Layout.cpp index 72e5a2c..61795ac 100644 --- a/src/Database/Database/Layout.cpp +++ b/src/Database/Database/Layout.cpp @@ -45,13 +45,11 @@ const std::map EnumMaps::status2str = { // clang-format off const std::map EnumMaps::str2estype = { {"regular", ElementaryStepType::REGULAR}, - {"barrierless", ElementaryStepType::BARRIERLESS}, - {"model_transformation", ElementaryStepType::MODEL_TRANSFORMATION} + {"barrierless", ElementaryStepType::BARRIERLESS} }; const std::map EnumMaps::estype2str = { {ElementaryStepType::REGULAR, "regular"}, - {ElementaryStepType::BARRIERLESS, "barrierless"}, - {ElementaryStepType::MODEL_TRANSFORMATION, "model_transformation"} + {ElementaryStepType::BARRIERLESS, "barrierless"} }; // clang-format on diff --git a/src/Database/Database/Layout.h b/src/Database/Database/Layout.h index 718a60e..a64defc 100644 --- a/src/Database/Database/Layout.h +++ b/src/Database/Database/Layout.h @@ -57,7 +57,7 @@ enum class StructureLabel : unsigned { SCAN_OBSERVER = 107, }; -enum class ElementaryStepType : unsigned { REGULAR = 0, BARRIERLESS = 1, MODEL_TRANSFORMATION = 2 }; +enum class ElementaryStepType : unsigned { REGULAR = 0, BARRIERLESS = 1 }; namespace Layout { diff --git a/src/Database/Database/Manager.cpp b/src/Database/Database/Manager.cpp index 555b92e..20c49bc 100644 --- a/src/Database/Database/Manager.cpp +++ b/src/Database/Database/Manager.cpp @@ -13,6 +13,7 @@ #include "Database/Objects/Calculation.h" #include "Database/Objects/Compound.h" #include "Database/Objects/ElementaryStep.h" +#include "Database/Objects/Impl/Fields.h" #include "Database/Objects/Property.h" #include "Database/Objects/Reaction.h" #include "Database/Objects/Structure.h" @@ -20,7 +21,6 @@ /* External Includes */ #include #include -#include #include #include #include @@ -200,9 +200,10 @@ std::tuple Manager::getDBVersion() { if (!optional) return {0, 0, 0}; auto view = optional.value().view(); - int major = view["version"]["major"].get_int32(); - int minor = view["version"]["minor"].get_int32(); - int patch = view["version"]["patch"].get_int32(); + auto versionView = view["version"]; + const int major = Fields::getIntegerFromElement(versionView["major"]); + const int minor = Fields::getIntegerFromElement(versionView["minor"]); + const int patch = Fields::getIntegerFromElement(versionView["patch"]); return {major, minor, patch}; } void Manager::init(bool moreIndices) { diff --git a/src/Database/Database/Objects/Calculation.cpp b/src/Database/Database/Objects/Calculation.cpp index f1a6b26..2de1f8f 100644 --- a/src/Database/Database/Objects/Calculation.cpp +++ b/src/Database/Database/Objects/Calculation.cpp @@ -144,7 +144,7 @@ Calculation::Job Calculation::getJob() const { Calculation::Job job("dummy"); job.order = jobDoc["order"].get_utf8().value.to_string(); job.memory = jobDoc["memory"].get_double(); - job.cores = jobDoc["cores"].get_int32(); + job.cores = Fields::getInteger(jobDoc, "cores"); job.disk = jobDoc["disk"].get_double(); return job; } @@ -167,7 +167,9 @@ void Calculation::setJob(const Calculation::Job& job) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } /*=========* @@ -201,7 +203,9 @@ void Calculation::addStructure(const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Calculation::removeStructure(const ID& id) const { @@ -219,7 +223,9 @@ void Calculation::removeStructure(const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } bool Calculation::hasStructure(const ID& id) const { @@ -275,7 +281,9 @@ void Calculation::setSetting(const std::string& key, const Utils::UniversalSetti << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } Utils::UniversalSettings::GenericValue Calculation::getSetting(const std::string& key) const { @@ -319,7 +327,9 @@ void Calculation::removeSetting(const std::string& key) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Calculation::clearSettings() const { @@ -337,7 +347,9 @@ void Calculation::clearSettings() const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } Utils::UniversalSettings::ValueCollection Calculation::getSettings() const { @@ -372,7 +384,9 @@ void Calculation::setSettings(const Utils::UniversalSettings::ValueCollection& s << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } /*===========* @@ -410,7 +424,9 @@ void Calculation::setResults(Calculation::Results& results) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Calculation::clearResults() const { @@ -435,7 +451,9 @@ void Calculation::clearResults() const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } Calculation::Results Calculation::getResults() const { @@ -487,7 +505,9 @@ void Calculation::setAuxiliary(std::string key, const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } ID Calculation::getAuxiliary(std::string key) const { @@ -531,7 +551,9 @@ void Calculation::removeAuxiliary(std::string key) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Calculation::setAuxiliaries(std::map auxiliaries) const { @@ -553,7 +575,9 @@ void Calculation::setAuxiliaries(std::map auxiliaries) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Calculation::clearAuxiliaries() const { @@ -571,7 +595,9 @@ void Calculation::clearAuxiliaries() const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } std::map Calculation::getAuxiliaries() const { @@ -615,7 +641,9 @@ void Calculation::setRestartInformation(const std::string& key, const ID& id) co << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } ID Calculation::getRestartInformation(const std::string& key) const { @@ -659,7 +687,9 @@ void Calculation::removeRestartInformation(const std::string& key) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Calculation::setRestartInformation(const std::map& restart_information) const { @@ -681,7 +711,9 @@ void Calculation::setRestartInformation(const std::map& restart << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Calculation::clearRestartInformation() const { @@ -699,7 +731,9 @@ void Calculation::clearRestartInformation() const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } std::map Calculation::getRestartInformation() const { diff --git a/src/Database/Database/Objects/Compound.cpp b/src/Database/Database/Objects/Compound.cpp index 773d12b..0269e7c 100644 --- a/src/Database/Database/Objects/Compound.cpp +++ b/src/Database/Database/Objects/Compound.cpp @@ -29,7 +29,7 @@ namespace Scine { namespace Database { namespace { -ID createImpl(const std::vector& structures, const Compound::CollectionPtr& collection) { +ID createImpl(const std::vector& structures, const Compound::CollectionPtr& collection, bool explorationDisabled) { // Build structure array bsoncxx::builder::basic::array structureArray; for (const auto& id : structures) { @@ -41,7 +41,7 @@ ID createImpl(const std::vector& structures, const Compound::CollectionPtr& auto doc = document{} << "_created" << now << "_lastmodified" << now << "analysis_disabled" << false - << "exploration_disabled" << false + << "exploration_disabled" << explorationDisabled << "_objecttype" << Compound::objecttype << "structures" << structureArray << "reactions" << open_array << close_array @@ -55,20 +55,20 @@ ID createImpl(const std::vector& structures, const Compound::CollectionPtr& constexpr const char* Compound::objecttype; -Compound Compound::create(const std::vector& structures, const CollectionPtr& collection) { +Compound Compound::create(const std::vector& structures, const CollectionPtr& collection, bool explorationDisabled) { if (!collection) { throw Exceptions::MissingCollectionException(); } - return Compound{createImpl(structures, collection), collection}; + return Compound{createImpl(structures, collection, explorationDisabled), collection}; } -ID Compound::create(const std::vector& structures) { +ID Compound::create(const std::vector& structures, bool explorationDisabled) { if (!_collection) { throw Exceptions::MissingLinkedCollectionException(); } - this->_id = std::make_unique(createImpl(structures, _collection)); + this->_id = std::make_unique(createImpl(structures, _collection, explorationDisabled)); return *this->_id; } @@ -112,7 +112,9 @@ void Compound::addReaction(const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Compound::removeReaction(const ID& id) const { @@ -128,7 +130,9 @@ void Compound::removeReaction(const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } int Compound::hasReactions() const { @@ -178,7 +182,9 @@ void Compound::addStructure(const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Compound::removeStructure(const ID& id) const { @@ -194,7 +200,9 @@ void Compound::removeStructure(const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } int Compound::hasStructures() const { diff --git a/src/Database/Database/Objects/Compound.h b/src/Database/Database/Objects/Compound.h index dabc9d0..3a877ca 100644 --- a/src/Database/Database/Objects/Compound.h +++ b/src/Database/Database/Objects/Compound.h @@ -51,18 +51,20 @@ class Compound : public Object { * * @param collection The collection to generate the compound in * @param structures The initial list of structures in the compound. + * @param explorationDisabled Disable this Compound for further explorations. * * @throws MissingLinkedCollectionException If @p collection does not hold a collection * @returns The new Compound instance. */ - static Compound create(const std::vector& structures, const CollectionPtr& collection); + static Compound create(const std::vector& structures, const CollectionPtr& collection, bool explorationDisabled = false); /** * @brief Creates a new Compound in the remote database. * @throws MissingLinkedCollectionException Thrown if no collection is linked. * @param structures The initial list of structures in the compound. + * @param explorationDisabled Disable this Compound for further explorations. * @return ID The ID of the newly inserted Compound. */ - ID create(const std::vector& structures); + ID create(const std::vector& structures, bool explorationDisabled = false); /** * @brief Get the centroid structure. * diff --git a/src/Database/Database/Objects/ElementaryStep.cpp b/src/Database/Database/Objects/ElementaryStep.cpp index 0a1e68d..f6b5f78 100644 --- a/src/Database/Database/Objects/ElementaryStep.cpp +++ b/src/Database/Database/Objects/ElementaryStep.cpp @@ -174,7 +174,9 @@ void ElementaryStep::addReactant(const ID& id, const SIDE side) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } if (side == SIDE::BOTH || side == SIDE::RHS) { auto selection = document{} << "_id" << this->id().bsoncxx() << finalize; @@ -187,7 +189,9 @@ void ElementaryStep::addReactant(const ID& id, const SIDE side) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } } @@ -205,7 +209,9 @@ void ElementaryStep::removeReactant(const ID& id, const SIDE side) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } if (side == SIDE::BOTH || side == SIDE::RHS) { auto selection = document{} << "_id" << this->id().bsoncxx() << finalize; @@ -218,7 +224,9 @@ void ElementaryStep::removeReactant(const ID& id, const SIDE side) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } } @@ -240,7 +248,9 @@ void ElementaryStep::setReactants(const std::vector& ids, const SIDE side) c << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } if (side == SIDE::BOTH || side == SIDE::RHS) { auto selection = document{} << "_id" << this->id().bsoncxx() << finalize; @@ -253,7 +263,9 @@ void ElementaryStep::setReactants(const std::vector& ids, const SIDE side) c << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } } @@ -314,7 +326,9 @@ void ElementaryStep::clearReactants(const SIDE side) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } if (side == SIDE::BOTH || side == SIDE::RHS) { auto selection = document{} << "_id" << this->id().bsoncxx() << finalize; @@ -327,7 +341,9 @@ void ElementaryStep::clearReactants(const SIDE side) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } } @@ -395,7 +411,9 @@ void ElementaryStep::setSpline(const Utils::BSplines::TrajectorySpline& spline) << finalize; // clang-format on auto selection = document{} << "_id" << this->id().bsoncxx() << finalize; - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void ElementaryStep::clearSpline() const { @@ -444,7 +462,9 @@ void ElementaryStep::addIdxMaps(const std::vector& lhsRhsMap, const boost:: << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } else { // clang-format off @@ -456,7 +476,9 @@ void ElementaryStep::addIdxMaps(const std::vector& lhsRhsMap, const boost:: << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } } @@ -474,7 +496,9 @@ void ElementaryStep::removeIdxMaps() const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } bool ElementaryStep::hasIdxMap(const IdxMapType& mapType) const { @@ -541,7 +565,9 @@ bool ElementaryStep::hasIdxMapByKey(const std::string& key) const { << close_array << finalize; // clang-format on - auto optional = _collection->mongocxx().find_one(selection.view()); + mongocxx::options::find options{}; + options.projection(document{} << "idx_maps" << 1 << finalize); + auto optional = _collection->mongocxx().find_one(selection.view(), options); return static_cast(optional); } @@ -562,10 +588,10 @@ std::vector ElementaryStep::getIdxMapByKey(const std::string& key) const { if (findIter == mapIt.end()) { throw Exceptions::MissingIdOrField(); } - bsoncxx::array::view wantedMapArray = findIter->get_array(); + const bsoncxx::array::view wantedMapArray = findIter->get_array(); std::vector wantedMap; - for (bsoncxx::array::element ele : wantedMapArray) { - wantedMap.emplace_back(ele.get_int32()); + for (const bsoncxx::array::element ele : wantedMapArray) { + wantedMap.emplace_back(Fields::getIntegerFromElement(ele)); } return wantedMap; } diff --git a/src/Database/Database/Objects/Flask.cpp b/src/Database/Database/Objects/Flask.cpp index 96d789e..9a86cee 100644 --- a/src/Database/Database/Objects/Flask.cpp +++ b/src/Database/Database/Objects/Flask.cpp @@ -29,7 +29,8 @@ namespace Scine { namespace Database { namespace { -ID createImpl(const std::vector& structures, const std::vector& compounds, const Flask::CollectionPtr& collection) { +ID createImpl(const std::vector& structures, const std::vector& compounds, + const Flask::CollectionPtr& collection, bool explorationDisabled) { // Build arrays bsoncxx::builder::basic::array structureArray; for (const auto& id : structures) { @@ -45,7 +46,7 @@ ID createImpl(const std::vector& structures, const std::vector& compound auto doc = document{} << "_created" << now << "_lastmodified" << now << "analysis_disabled" << false - << "exploration_disabled" << false + << "exploration_disabled" << explorationDisabled << "_objecttype" << Flask::objecttype << "structures" << structureArray << "compounds" << compoundArray @@ -60,20 +61,21 @@ ID createImpl(const std::vector& structures, const std::vector& compound constexpr const char* Flask::objecttype; -Flask Flask::create(const std::vector& structures, const std::vector& compounds, const CollectionPtr& collection) { +Flask Flask::create(const std::vector& structures, const std::vector& compounds, + const CollectionPtr& collection, bool explorationDisabled) { if (!collection) { throw Exceptions::MissingCollectionException(); } - return Flask{createImpl(structures, compounds, collection), collection}; + return Flask{createImpl(structures, compounds, collection, explorationDisabled), collection}; } -ID Flask::create(const std::vector& structures, const std::vector& compounds) { +ID Flask::create(const std::vector& structures, const std::vector& compounds, bool explorationDisabled) { if (!_collection) { throw Exceptions::MissingLinkedCollectionException(); } - this->_id = std::make_unique(createImpl(structures, compounds, _collection)); + this->_id = std::make_unique(createImpl(structures, compounds, _collection, explorationDisabled)); return *this->_id; } @@ -115,7 +117,9 @@ bool Flask::hasReaction(const ID& id) const { << close_array << finalize; // clang-format on - auto optional = _collection->mongocxx().find_one(selection.view()); + auto options = mongocxx::options::find(); + options.projection(document{} << "_id" << 1 << finalize); + auto optional = _collection->mongocxx().find_one(selection.view(), options); return static_cast(optional); } @@ -132,7 +136,9 @@ void Flask::addReaction(const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Flask::removeReaction(const ID& id) const { @@ -148,7 +154,9 @@ void Flask::removeReaction(const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } int Flask::hasReactions() const { @@ -196,7 +204,9 @@ bool Flask::hasStructure(const ID& id) const { << close_array << finalize; // clang-format on - auto optional = _collection->mongocxx().find_one(selection.view()); + auto options = mongocxx::options::find(); + options.projection(document{} << "_id" << 1 << finalize); + auto optional = _collection->mongocxx().find_one(selection.view(), options); return static_cast(optional); } @@ -213,7 +223,9 @@ void Flask::addStructure(const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Flask::removeStructure(const ID& id) const { @@ -229,7 +241,9 @@ void Flask::removeStructure(const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } int Flask::hasStructures() const { @@ -277,7 +291,9 @@ bool Flask::hasCompound(const ID& id) const { << close_array << finalize; // clang-format on - auto optional = _collection->mongocxx().find_one(selection.view()); + auto options = mongocxx::options::find(); + options.projection(document{} << "_id" << 1 << finalize); + auto optional = _collection->mongocxx().find_one(selection.view(), options); return static_cast(optional); } diff --git a/src/Database/Database/Objects/Flask.h b/src/Database/Database/Objects/Flask.h index 79b58b0..7aaeac9 100644 --- a/src/Database/Database/Objects/Flask.h +++ b/src/Database/Database/Objects/Flask.h @@ -51,19 +51,22 @@ class Flask : public Object { * @param collection The collection to generate the Flask in * @param structures The initial list of Structures in the Flask. * @param compounds The list of Compounds that are combined in the Flask. + * @param explorationDisabled Disable this Flask for further explorations. * * @throws MissingLinkedCollectionException If @p collection does not hold a collection * @returns The new Flask instance. */ - static Flask create(const std::vector& structures, const std::vector& compounds, const CollectionPtr& collection); + static Flask create(const std::vector& structures, const std::vector& compounds, + const CollectionPtr& collection, bool explorationDisabled = false); /** * @brief Creates a new Flask in the remote database. * @throws MissingLinkedCollectionException Thrown if no collection is linked. * @param structures The initial list of Structures in the Flask. * @param compounds The list of Compounds that are combined in the Flask. + * @param explorationDisabled Disable this Flask for further explorations. * @return ID The ID of the newly inserted Flask. */ - ID create(const std::vector& structures, const std::vector& compounds); + ID create(const std::vector& structures, const std::vector& compounds, bool explorationDisabled = false); /** * @brief Get the centroid structure. * diff --git a/src/Database/Database/Objects/Impl/DerivedProperty.h b/src/Database/Database/Objects/Impl/DerivedProperty.h index 06b95b2..3341c06 100644 --- a/src/Database/Database/Objects/Impl/DerivedProperty.h +++ b/src/Database/Database/Objects/Impl/DerivedProperty.h @@ -13,6 +13,7 @@ #include "Database/Id.h" #include "Database/Objects/Model.h" #include "Database/Objects/Property.h" +#include "Fields.h" #include #include #include @@ -27,23 +28,6 @@ namespace Scine { namespace Database { -namespace { - -template -long getIntegerFromElement(T v) { - try { - return v.get_int64(); - } - catch (const bsoncxx::exception&) { - return static_cast(v.get_int32()); - } -} - -long getInteger(bsoncxx::document::view& view, const std::string& key) { - return getIntegerFromElement(view[key]); -} -} // namespace - namespace Serialization { template @@ -146,7 +130,7 @@ struct Serializer { } static Eigen::VectorXd deserialize(bsoncxx::document::view view) { - const Eigen::Index size = getInteger(view, "size"); + const Eigen::Index size = Fields::getInteger(view, "size"); Eigen::VectorXd ret(size); bsoncxx::array::view data = view["data"].get_array(); Eigen::Index idx = 0; @@ -181,8 +165,8 @@ struct Serializer { } static Eigen::MatrixXd deserialize(bsoncxx::document::view view) { - const Eigen::Index cols = getInteger(view, "cols"); - const Eigen::Index rows = getInteger(view, "rows"); + const Eigen::Index cols = Fields::getInteger(view, "cols"); + const Eigen::Index rows = Fields::getInteger(view, "rows"); Eigen::MatrixXd ret(rows, cols); bsoncxx::array::view data = view["data"].get_array(); Eigen::Index idx = 0; @@ -230,9 +214,9 @@ struct Serializer> { } static Eigen::SparseMatrix deserialize(bsoncxx::document::view view) { - const Eigen::Index cols = getInteger(view, "cols"); - const Eigen::Index rows = getInteger(view, "rows"); - const Eigen::Index size = getInteger(view, "size"); + const Eigen::Index cols = Fields::getInteger(view, "cols"); + const Eigen::Index rows = Fields::getInteger(view, "rows"); + const Eigen::Index size = Fields::getInteger(view, "size"); // Read triplets std::vector> triplets; @@ -248,8 +232,8 @@ struct Serializer> { auto v = *values_it++; auto c = *col_it++; auto r = *row_it++; - triplets.emplace_back(getIntegerFromElement(r), - getIntegerFromElement(c), v.get_double()); + triplets.emplace_back(Fields::getIntegerFromElement(r), + Fields::getIntegerFromElement(c), v.get_double()); } Eigen::SparseMatrix ret(rows, cols); @@ -271,7 +255,7 @@ DerivedType create(std::shared_ptr collection, const Model& model, c throw Exceptions::MissingCollectionException(); } - document builder{}; + bsoncxx::builder::basic::document builder{}; const bsoncxx::types::b_date now{std::chrono::system_clock::now()}; builder.append(kvp("_created", now)); builder.append(kvp("_lastmodified", now)); @@ -292,8 +276,8 @@ DerivedType create(std::shared_ptr collection, const Model& model, c auto doc = builder.extract(); auto result = collection->mongocxx().insert_one(doc.view()); - ID id{result->inserted_id().get_oid().value}; - return DerivedType{std::move(id), collection}; + const bsoncxx::v_noabi::oid oid = result->inserted_id().get_oid().value; + return DerivedType{ID(oid), collection}; } template @@ -304,9 +288,9 @@ void updateData(DerivedProperty& derived, const T& data) { throw Exceptions::MissingLinkedCollectionException(); } using namespace bsoncxx::builder::basic; - document selection{}; + bsoncxx::builder::basic::document selection{}; selection.append(kvp("_id", derived.id().bsoncxx())); - document update{}; + bsoncxx::builder::basic::document update{}; update.append(kvp("$set", [&data](sub_document updates) { Serialization::Serializer::serialize(updates, data); })); update.append(kvp("$currentDate", [](sub_document dates) { dates.append(kvp("_lastmodified", true)); })); collection->mongocxx().find_one_and_update(selection.view(), update.view()); @@ -322,7 +306,7 @@ T getData(DerivedProperty& derived) { } using namespace bsoncxx::builder::basic; - document selection{}; + bsoncxx::builder::basic::document selection{}; selection.append(kvp("_id", derived.id().bsoncxx())); mongocxx::options::find options{}; Serialization::Serializer::project(options); diff --git a/src/Database/Database/Objects/Impl/Fields.h b/src/Database/Database/Objects/Impl/Fields.h index ec980eb..014a733 100644 --- a/src/Database/Database/Objects/Impl/Fields.h +++ b/src/Database/Database/Objects/Impl/Fields.h @@ -14,6 +14,7 @@ #include "Database/Objects/Object.h" #include #include +#include #include #include @@ -229,7 +230,9 @@ void set(const Object& obj, const std::string& field, const T& value) { << close_document << finalize; // clang-format on - collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } /** @@ -281,7 +284,9 @@ inline void unset(const Object& obj, const std::string& field) { << close_document << finalize; // clang-format on - collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } //! Check whether a field exists @@ -324,10 +329,43 @@ inline bool nonNull(const Object& obj, const std::string& field) { << close_array << finalize; // clang-format on - auto optional = collection->mongocxx().find_one(selection.view()); + auto options = mongocxx::options::find(); + options.projection(document{} << "_id" << 1 << finalize); + auto optional = collection->mongocxx().find_one(selection.view(), options); return static_cast(optional); } +template +inline R getIntegerFromElement(T v) { + if (v.type() == bsoncxx::type::k_int64) { + return static_cast(v.get_int64()); + } + if (v.type() == bsoncxx::type::k_int32) { + return static_cast(v.get_int32()); + } + if (v.type() == bsoncxx::type::k_double) { + std::cerr << "Warning: The database contains a double value for an integer field." << std::endl; + return static_cast(v.get_double()); + } + throw std::runtime_error("The database contains a non-integer value for an integer field."); +} + +template +inline R getInteger(bsoncxx::document::view& view, const std::string& key) { + return getIntegerFromElement(view[key]); +} + +// define template of getInteger explicitly for int return type to include check for overflow +template<> +inline int getInteger(bsoncxx::document::view& view, const std::string& key) { + const long x = getInteger(view, key); + if (x > std::numeric_limits::max() || x < std::numeric_limits::min()) { + throw std::runtime_error("The database contains an 64bit integer that cannot be represented as 32bit integer " + "as defined in our database schema."); + } + return getIntegerFromElement(view[key]); +} + } /* namespace Fields */ } /* namespace Database */ } /* namespace Scine */ diff --git a/src/Database/Database/Objects/Model.cpp b/src/Database/Database/Objects/Model.cpp index 3b2cc71..4e4961c 100644 --- a/src/Database/Database/Objects/Model.cpp +++ b/src/Database/Database/Objects/Model.cpp @@ -197,6 +197,14 @@ const std::vector& Model::skipFields() { } bool Model::operator==(const Model& rhs) const { + return equality(rhs, true); +} + +bool Model::equalWithoutPeriodicBoundaryCheck(const Model& rhs) const { + return equality(rhs, false); +} + +bool Model::equality(const Model& rhs, bool comparePbc) const { auto lhsPairs = this->getConstSettingsModelPairs(); // necessary to call here because of pybind auto rhsPairs = rhs.getConstSettingsModelPairs(); auto rhsIterator = rhsPairs.begin(); @@ -234,6 +242,10 @@ bool Model::operator==(const Model& rhs) const { } // make sure we don't have equivalent periodic boundaries else if (name == Utils::SettingsNames::periodicBoundaries) { + if (!comparePbc) { + rhsIterator++; + continue; + } try { auto lhsPbc = Utils::PeriodicBoundaries(lhsModelEntryRef.get()); auto rhsPbc = Utils::PeriodicBoundaries(rhsModelEntryRef.get()); diff --git a/src/Database/Database/Objects/Model.h b/src/Database/Database/Objects/Model.h index ed7c10d..4ba9f90 100644 --- a/src/Database/Database/Objects/Model.h +++ b/src/Database/Database/Objects/Model.h @@ -113,6 +113,7 @@ class Model { */ bool operator==(const Model& rhs) const; bool operator!=(const Model& rhs) const; + bool equalWithoutPeriodicBoundaryCheck(const Model& rhs) const; /** * @brief If the given entry is interpreted as None, i.e., and empty string or case insensitive 'none' @@ -136,6 +137,8 @@ class Model { private: static const std::vector& skipFields(); + bool equality(const Model& rhs, bool comparePbc) const; + std::map> getSettingsModelPairs() { // reference wrapper instead of unique ptr because of std::pair std::map> settingsModelPairs; diff --git a/src/Database/Database/Objects/Object.cpp b/src/Database/Database/Objects/Object.cpp index 5654184..a8ab440 100644 --- a/src/Database/Database/Objects/Object.cpp +++ b/src/Database/Database/Objects/Object.cpp @@ -87,6 +87,7 @@ bsoncxx::document::value Object::getRawContent() const { if (!_collection) throw Exceptions::MissingLinkedCollectionException(); auto selection = document{} << "_id" << this->id().bsoncxx() << finalize; + // do not add projection here, because we want the whole document auto optional = _collection->mongocxx().find_one(selection.view()); if (!optional) throw Exceptions::IDNotFoundException(); @@ -125,7 +126,9 @@ void Object::wipe(bool expectPresence) { throw Exceptions::MissingLinkedCollectionException(); auto selection = document{} << "_id" << this->id().bsoncxx() << finalize; if (expectPresence) { - auto optional = _collection->mongocxx().find_one(selection.view()); + auto options = mongocxx::options::find(); + options.projection(document{} << "_id" << 1 << finalize); + auto optional = _collection->mongocxx().find_one(selection.view(), options); if (!optional) throw Exceptions::IDNotFoundException(); } @@ -187,7 +190,9 @@ void Object::touch() const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Object::enable_analysis() { @@ -203,7 +208,9 @@ void Object::enable_analysis() { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Object::enable_exploration() { @@ -219,7 +226,9 @@ void Object::enable_exploration() { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Object::disable_analysis() { @@ -235,7 +244,9 @@ void Object::disable_analysis() { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Object::disable_exploration() { @@ -251,7 +262,9 @@ void Object::disable_exploration() { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } bool Object::analyze() { diff --git a/src/Database/Database/Objects/Property.cpp b/src/Database/Database/Objects/Property.cpp index 85d9361..b6285ef 100644 --- a/src/Database/Database/Objects/Property.cpp +++ b/src/Database/Database/Objects/Property.cpp @@ -81,7 +81,9 @@ void Property::setPropertyName(const std::string& name) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } Model Property::getModel() const { diff --git a/src/Database/Database/Objects/Reaction.cpp b/src/Database/Database/Objects/Reaction.cpp index 8ea9245..c30af5c 100644 --- a/src/Database/Database/Objects/Reaction.cpp +++ b/src/Database/Database/Objects/Reaction.cpp @@ -129,7 +129,9 @@ void Reaction::addElementaryStep(const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Reaction::removeElementaryStep(const ID& id) const { @@ -145,7 +147,9 @@ void Reaction::removeElementaryStep(const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } int Reaction::hasElementarySteps() const { @@ -223,7 +227,9 @@ void Reaction::addReactant(const ID& id, const SIDE side, COMPOUND_OR_FLASK type << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } if (side == SIDE::BOTH || side == SIDE::RHS) { auto selection = document{} << "_id" << this->id().bsoncxx() << finalize; @@ -239,7 +245,9 @@ void Reaction::addReactant(const ID& id, const SIDE side, COMPOUND_OR_FLASK type << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } } @@ -257,7 +265,9 @@ void Reaction::removeReactant(const ID& id, const SIDE side) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } if (side == SIDE::BOTH || side == SIDE::RHS) { auto selection = document{} << "_id" << this->id().bsoncxx() << finalize; @@ -270,7 +280,9 @@ void Reaction::removeReactant(const ID& id, const SIDE side) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } } @@ -307,7 +319,9 @@ void Reaction::setReactants(const std::vector& ids, const SIDE side, const s << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } if (side == SIDE::BOTH || side == SIDE::RHS) { auto selection = document{} << "_id" << this->id().bsoncxx() << finalize; @@ -320,7 +334,9 @@ void Reaction::setReactants(const std::vector& ids, const SIDE side, const s << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } } @@ -415,7 +431,7 @@ COMPOUND_OR_FLASK Reaction::getReactantType(const ID& id) const { // clang-format on mongocxx::options::find options{}; options.projection(document{} << "lhs" << 1 << "rhs" << 1 << finalize); - auto optional = _collection->mongocxx().find_one(selection.view()); + auto optional = _collection->mongocxx().find_one(selection.view(), options); if (!optional) throw Exceptions::MissingIdOrField(); auto view = optional.value().view(); @@ -454,7 +470,9 @@ void Reaction::clearReactants(const SIDE side) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } if (side == SIDE::BOTH || side == SIDE::RHS) { auto selection = document{} << "_id" << this->id().bsoncxx() << finalize; @@ -467,7 +485,9 @@ void Reaction::clearReactants(const SIDE side) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } } diff --git a/src/Database/Database/Objects/Structure.cpp b/src/Database/Database/Objects/Structure.cpp index abde495..3b17d29 100644 --- a/src/Database/Database/Objects/Structure.cpp +++ b/src/Database/Database/Objects/Structure.cpp @@ -132,21 +132,36 @@ Utils::AtomCollection Structure::getAtoms() const { throw Exceptions::MissingIdOrField(); auto view = optional.value().view(); // Read number of atoms - auto nAtoms = view["nAtoms"].get_int32(); - // Read atoms in AtomCollection - Utils::AtomCollection atoms; - atoms.resize(nAtoms); + auto nAtoms = Fields::getInteger(view, "nAtoms"); bsoncxx::array::view atomsView = view["atoms"].get_array(); - for (int i = 0; i < nAtoms; i++) { - const auto atom = atomsView[i]; - std::string symbol = atom["element"].get_utf8().value.to_string(); - const auto e = Utils::ElementInfo::elementTypeForSymbol(symbol); - const double x = atom["x"].get_double(); - const double y = atom["y"].get_double(); - const double z = atom["z"].get_double(); - atoms.setElement(i, e); - atoms.setPosition(i, Eigen::Vector3d(x, y, z)); - } + // Read atoms in AtomCollection + std::vector elements; + Eigen::Matrix coordinates = + Eigen::Matrix::Zero(nAtoms, 3); + /* + * Note that the loop + * for(const auto& atom : atomsView) { + * ... + * } + * is significantly faster than its index-based equivalent + * for (int i = 0; i < nAtoms; i++) { + * const auto atom = atomsView[i]; + * ... + * } + * Therefore, we use a somewhat awkward counter for the atom index. + * + */ + unsigned int iAtom = 0; + for (const auto& atom : atomsView) { + const std::string symbol = atom["element"].get_utf8().value.to_string(); + elements.push_back(Utils::ElementInfo::elementTypeForSymbol(symbol)); + coordinates(iAtom, 0) = atom["x"].get_double(); + coordinates(iAtom, 1) = atom["y"].get_double(); + coordinates(iAtom, 2) = atom["z"].get_double(); + iAtom++; + } + + Utils::AtomCollection atoms(elements, coordinates); return atoms; } @@ -178,7 +193,9 @@ void Structure::setAtoms(const Utils::AtomCollection& atoms) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto projection = document{} << "_id" << 1 << "_objecttype" << 1 << finalize; + auto options = mongocxx::options::find_one_and_update(); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } int Structure::hasAtoms() const { @@ -193,9 +210,7 @@ int Structure::hasAtoms() const { if (!optional) throw Exceptions::MissingIdOrField(); auto view = optional.value().view(); - if (view["nAtoms"].type() != bsoncxx::types::b_int32::type_id) - throw Exceptions::MissingIdOrField(); - return view["nAtoms"].get_int32(); + return Fields::getInteger(view, "nAtoms"); } void Structure::clearAtoms() const { @@ -212,7 +227,9 @@ void Structure::clearAtoms() const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } /*=========* @@ -348,7 +365,9 @@ void Structure::addProperty(const std::string& key, const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Structure::removeProperty(const std::string& key, const ID& id) const { @@ -364,7 +383,9 @@ void Structure::removeProperty(const std::string& key, const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Structure::setProperties(const std::string& key, const std::vector& ids) const { @@ -384,7 +405,9 @@ void Structure::setProperties(const std::string& key, const std::vector& ids << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } std::vector Structure::getProperties(const std::string& key) const { @@ -419,11 +442,13 @@ std::vector Structure::queryProperties(const std::string& key, const Model& return {}; const auto& ids = it->second; + mongocxx::options::find options{}; + options.projection(document{} << "model" << 1 << finalize); // Setup selection for query in properties std::vector ret; for (const auto& id : ids) { auto selection = document{} << "_id" << id.bsoncxx() << finalize; - auto optional = collection->mongocxx().find_one(selection.view()); + auto optional = collection->mongocxx().find_one(selection.view(), options); if (!optional) throw Exceptions::MissingIdOrField(); auto doc = optional.value().view(); @@ -457,7 +482,9 @@ void Structure::clearProperties(const std::string& key) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } std::map> Structure::getAllProperties() const { @@ -504,7 +531,9 @@ void Structure::setAllProperties(const std::map>& p << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Structure::clearAllProperties() const { @@ -520,7 +549,9 @@ void Structure::clearAllProperties() const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } /*================* @@ -568,7 +599,9 @@ void Structure::addCalculation(const std::string& key, const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Structure::addCalculations(const std::string& key, const std::vector& ids) const { @@ -588,7 +621,9 @@ void Structure::addCalculations(const std::string& key, const std::vector& i << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Structure::removeCalculation(const std::string& key, const ID& id) const { @@ -604,7 +639,9 @@ void Structure::removeCalculation(const std::string& key, const ID& id) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Structure::setCalculations(const std::string& key, const std::vector& ids) const { @@ -624,7 +661,9 @@ void Structure::setCalculations(const std::string& key, const std::vector& i << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } std::vector Structure::getCalculations(const std::string& key) const { @@ -666,7 +705,9 @@ std::vector Structure::queryCalculations(const std::string& key, const Model } auto selection = document{} << "_id" << open_document << "$in" << array << close_document << finalize; - auto cursor = collection->mongocxx().find(selection.view()); + mongocxx::options::find options{}; + options.projection(document{} << "model" << 1 << finalize); + auto cursor = collection->mongocxx().find(selection.view(), options); std::vector ret; for (const auto& doc : cursor) { Model docModel(doc["model"].get_document().view()); @@ -699,7 +740,9 @@ void Structure::clearCalculations(const std::string& key) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } std::map> Structure::getAllCalculations() const { @@ -746,7 +789,9 @@ void Structure::setAllCalculations(const std::map>& << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Structure::clearAllCalculations() const { @@ -762,7 +807,9 @@ void Structure::clearAllCalculations() const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } /*===================* @@ -800,7 +847,9 @@ void Structure::setGraph(const std::string& key, const std::string& graph) const << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } void Structure::removeGraph(const std::string& key) const { @@ -816,7 +865,9 @@ void Structure::removeGraph(const std::string& key) const { << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } bool Structure::hasGraph(const std::string& key) const { @@ -869,7 +920,9 @@ void Structure::setGraphs(const std::map& graphs) cons << close_document << finalize; // clang-format on - _collection->mongocxx().find_one_and_update(selection.view(), update.view()); + auto options = mongocxx::options::find_one_and_update(); + options.projection(document{} << "_id" << 1 << finalize); + _collection->mongocxx().find_one_and_update(selection.view(), update.view(), options); } /*===========* @@ -910,6 +963,9 @@ ID Structure::getOriginal() const { if (!this->hasOriginal()) { throw Exceptions::MissingIdOrField(); } + if (*this->_id == Fields::get(*this, "duplicate_of")) { + throw Exceptions::SelfDuplicateException(); + } return Fields::get(*this, "duplicate_of"); } diff --git a/src/Database/Database/Objects/ValueCollection.cpp b/src/Database/Database/Objects/ValueCollection.cpp index 020ff5a..b6a54bb 100644 --- a/src/Database/Database/Objects/ValueCollection.cpp +++ b/src/Database/Database/Objects/ValueCollection.cpp @@ -7,10 +7,10 @@ #include "Database/Objects/ValueCollection.h" #include "Database/Exceptions.h" +#include "Database/Objects/Impl/Fields.h" #include "Utils/UniversalSettings/GenericValueVariant.h" #include #include -#include #include namespace Scine { @@ -63,6 +63,15 @@ void array_append(bsoncxx::builder::basic::array& array, const std::string& v) { void array_append(bsoncxx::builder::basic::array& array, const Utils::UniversalSettings::ValueCollection& v) { array.append(ValueCollection::serialize(v)); } +void array_append(bsoncxx::builder::basic::array& array, const std::vector& v) { + /* + * MongoDB does not support arrays of arrays. Therefore, we construct an array of value collections. + * These value collections then contain exactly one array which is the inner array. + */ + Utils::UniversalSettings::ValueCollection newValueCollection; + newValueCollection.addIntList(subListKey, v); + array.append(ValueCollection::serialize(newValueCollection)); +} // Blanket impl for IntList, DoubleList, StringList and CollectionList template @@ -103,6 +112,13 @@ struct ListTypeHint { } }; +template<> +struct ListTypeHint> { + static std::string value() { + return "list_of_lists"; + } +}; + template void anon_serialize(bsoncxx::builder::basic::document& document, const std::string& key, const std::vector& v) { bsoncxx::builder::basic::document builder{}; @@ -133,16 +149,16 @@ Utils::UniversalSettings::GenericValue GenericValue::deserialize(const BsonValue if (type == bsoncxx::type::k_bool) { return Utils::UniversalSettings::GenericValue::fromBool(value.get_bool()); } - else if (type == bsoncxx::type::k_int32) { - return Utils::UniversalSettings::GenericValue::fromInt(value.get_int32()); + if (type == bsoncxx::type::k_int32) { + return Utils::UniversalSettings::GenericValue::fromInt(Fields::getIntegerFromElement(value)); } - else if (type == bsoncxx::type::k_double) { + if (type == bsoncxx::type::k_double) { return Utils::UniversalSettings::GenericValue::fromDouble(value.get_double()); } - else if (type == bsoncxx::type::k_utf8) { + if (type == bsoncxx::type::k_utf8) { return Utils::UniversalSettings::GenericValue::fromString(value.get_utf8().value.to_string()); } - else if (type == bsoncxx::type::k_document) { + if (type == bsoncxx::type::k_document) { /* Possible types: * - IntList, DoubleList, StringList, CollectionList (has type and list elements) * - ParametrizedOptionValue (has selectedOption member) @@ -156,45 +172,50 @@ Utils::UniversalSettings::GenericValue GenericValue::deserialize(const BsonValue auto array = document.view()["list"].get_array(); if (typeString == ListTypeHint::value()) { std::vector vs; - for (auto v : array.value) { - vs.push_back(v.get_int32()); + for (const auto v : array.value) { + vs.push_back(Fields::getIntegerFromElement(v)); } return Utils::UniversalSettings::GenericValue::fromIntList(std::move(vs)); } - else if (typeString == ListTypeHint::value()) { + if (typeString == ListTypeHint::value()) { std::vector vs; - for (auto v : array.value) { + for (const auto v : array.value) { vs.push_back(v.get_double()); } return Utils::UniversalSettings::GenericValue::fromDoubleList(std::move(vs)); } - else if (typeString == ListTypeHint::value()) { + if (typeString == ListTypeHint::value()) { std::vector vs; - for (auto v : array.value) { + for (const auto v : array.value) { vs.push_back(v.get_utf8().value.to_string()); } return Utils::UniversalSettings::GenericValue::fromStringList(std::move(vs)); } - else if (typeString == ListTypeHint::value()) { + if (typeString == ListTypeHint::value()) { std::vector vs; for (auto v : array.value) { vs.push_back(ValueCollection::deserialize(v.get_document())); } return Utils::UniversalSettings::GenericValue::fromCollectionList(std::move(vs)); } - else { - throw Exceptions::MissingIdOrField(); + if (typeString == ListTypeHint>::value()) { + std::vector> listOfLists; + for (auto v : array.value) { + auto collection = ValueCollection::deserialize(v.get_document()); + listOfLists.push_back(collection.getIntList(subListKey)); + } + return Utils::UniversalSettings::GenericValue::fromIntListList(std::move(listOfLists)); } + + throw Exceptions::MissingIdOrField(); } - else if (document.view().find("selectedOption") != document.view().end()) { + if (document.view().find("selectedOption") != document.view().end()) { Utils::UniversalSettings::ParametrizedOptionValue option{ document.view()["selectedOption"].get_utf8().value.to_string(), ValueCollection::deserialize(document.view()["optionSettings"].get_document())}; return Utils::UniversalSettings::GenericValue::fromOptionWithSettings(std::move(option)); } - else { - return Utils::UniversalSettings::GenericValue::fromCollection(ValueCollection::deserialize(document)); - } + return Utils::UniversalSettings::GenericValue::fromCollection(ValueCollection::deserialize(document)); } throw Exceptions::MissingIdOrField(); diff --git a/src/Database/Database/Objects/ValueCollection.h b/src/Database/Database/Objects/ValueCollection.h index f4779fd..9f4bd34 100644 --- a/src/Database/Database/Objects/ValueCollection.h +++ b/src/Database/Database/Objects/ValueCollection.h @@ -13,6 +13,7 @@ #include "boost/optional.hpp" #include #include +#include namespace Scine { namespace Database { @@ -29,6 +30,7 @@ struct GenericValue { const Utils::UniversalSettings::GenericValue& value); static Utils::UniversalSettings::GenericValue deserialize(const BsonValueType& value); }; +const static std::string subListKey = "sub_list"; struct ValueCollection { static bsoncxx::v_noabi::document::value serialize(const Utils::UniversalSettings::ValueCollection& collection); diff --git a/src/Database/Database/Version.h b/src/Database/Database/Version.h index 24090c7..909d333 100644 --- a/src/Database/Database/Version.h +++ b/src/Database/Database/Version.h @@ -16,7 +16,7 @@ namespace Database { namespace Version { constexpr static const int major = 1; -constexpr static const int minor = 2; +constexpr static const int minor = 3; constexpr static const int patch = 0; } // namespace Version diff --git a/src/Database/Python/CompoundPython.cpp b/src/Database/Python/CompoundPython.cpp index 0288752..7686d7e 100644 --- a/src/Database/Python/CompoundPython.cpp +++ b/src/Database/Python/CompoundPython.cpp @@ -25,10 +25,12 @@ void init_compound(pybind11::class_& compound) { )delim"); compound.def(pybind11::init(), pybind11::arg("id"), pybind11::arg("collection")); - compound.def_static("make", pybind11::overload_cast&, const Object::CollectionPtr&>(&Compound::create), - pybind11::arg("structure_ids"), pybind11::arg("collection")); + compound.def_static( + "make", pybind11::overload_cast&, const Object::CollectionPtr&, bool>(&Compound::create), + pybind11::arg("structure_ids"), pybind11::arg("collection"), pybind11::arg("exploration_disabled") = false); - compound.def("create", pybind11::overload_cast&>(&Compound::create), pybind11::arg("structure_ids"), + compound.def("create", pybind11::overload_cast&, bool>(&Compound::create), + pybind11::arg("structure_ids"), pybind11::arg("exploration_disabled") = false, R"delim( Generates a new compound in the linked collection from a list of structure IDs. diff --git a/src/Database/Python/ElementaryStepPython.cpp b/src/Database/Python/ElementaryStepPython.cpp index 87ecb81..ddfecb5 100644 --- a/src/Database/Python/ElementaryStepPython.cpp +++ b/src/Database/Python/ElementaryStepPython.cpp @@ -131,9 +131,6 @@ void init_elementary_step(pybind11::module& m) { pybind11::enum_ estype(m, "ElementaryStepType"); estype.value("REGULAR", ElementaryStepType::REGULAR, "A regular elementary step that involves exactly one transition state."); estype.value("BARRIERLESS", ElementaryStepType::BARRIERLESS, "An elementary step that has no barrier/ transition state"); - estype.value("MODEL_TRANSFORMATION", ElementaryStepType::MODEL_TRANSFORMATION, - "An elementary step that describes the transformation between" - "electronic structure models."); elementaryStep.def("get_type", &ElementaryStep::getType); elementaryStep.def("set_type", &ElementaryStep::setType); elementaryStep.def("get_barrier_from_spline", &ElementaryStep::getBarrierFromSpline, R"delim( diff --git a/src/Database/Python/FlaskPython.cpp b/src/Database/Python/FlaskPython.cpp index 86557dd..3cd03f9 100644 --- a/src/Database/Python/FlaskPython.cpp +++ b/src/Database/Python/FlaskPython.cpp @@ -26,18 +26,19 @@ void init_flask(pybind11::class_& flask) { )delim"); flask.def(pybind11::init(), pybind11::arg("id"), pybind11::arg("collection")); - flask.def_static( - "make", - pybind11::overload_cast&, const std::vector&, const Object::CollectionPtr&>(&Flask::create), - pybind11::arg("structure_ids"), pybind11::arg("compound_ids"), pybind11::arg("collection"), - R"delim( + flask.def_static("make", + pybind11::overload_cast&, const std::vector&, const Object::CollectionPtr&, bool>( + &Flask::create), + pybind11::arg("structure_ids"), pybind11::arg("compound_ids"), pybind11::arg("collection"), + pybind11::arg("exploration_disabled") = false, + R"delim( Create a new flask with a database ID. :returns: A Flask object linked to the given collection. )delim"); - flask.def("create", pybind11::overload_cast&, const std::vector&>(&Flask::create), - pybind11::arg("structure_ids"), pybind11::arg("compound_ids"), + flask.def("create", pybind11::overload_cast&, const std::vector&, bool>(&Flask::create), + pybind11::arg("structure_ids"), pybind11::arg("compound_ids"), pybind11::arg("exploration_disabled") = false, R"delim( Generates a new flask in the linked collection from a list of structure IDs and compoud IDs. Stores the generated ID in the diff --git a/src/Database/Python/ModelPython.cpp b/src/Database/Python/ModelPython.cpp index b995d27..be6d3db 100644 --- a/src/Database/Python/ModelPython.cpp +++ b/src/Database/Python/ModelPython.cpp @@ -121,6 +121,8 @@ void init_model(pybind11::module& m) { model.def("__copy__", [](const Model& self) -> Model { return Model(self); }); model.def("__deepcopy__", [](const Model& self, pybind11::dict /* memo */) -> Model { return Model(self); }); // Comparison operators + model.def("equal_without_periodic_boundary_check", &Model::equalWithoutPeriodicBoundaryCheck, pybind11::arg("rhs"), + "Compares to another model without checking for equal periodic boundaries"); model.def(pybind11::self == pybind11::self); model.def(pybind11::self != pybind11::self); model.def(pybind11::pickle( diff --git a/src/Database/Python/Tests/elementary_step_test.py b/src/Database/Python/Tests/elementary_step_test.py index e9b1405..236fd56 100644 --- a/src/Database/Python/Tests/elementary_step_test.py +++ b/src/Database/Python/Tests/elementary_step_test.py @@ -183,10 +183,6 @@ def test_type(self): step.set_type(db.ElementaryStepType.BARRIERLESS) assert db.ElementaryStepType.BARRIERLESS == step.get_type() assert db.ElementaryStepType.REGULAR != step.get_type() - step.set_type(db.ElementaryStepType.MODEL_TRANSFORMATION) - assert db.ElementaryStepType.MODEL_TRANSFORMATION == step.get_type() - assert db.ElementaryStepType.BARRIERLESS != step.get_type() - assert db.ElementaryStepType.REGULAR != step.get_type() def test_type_fails_collection(self): step = db.ElementaryStep(db.ID()) diff --git a/src/Database/Python/Tests/model_test.py b/src/Database/Python/Tests/model_test.py index acb10ad..06ad4cb 100644 --- a/src/Database/Python/Tests/model_test.py +++ b/src/Database/Python/Tests/model_test.py @@ -9,6 +9,7 @@ import unittest import os import pickle +from copy import deepcopy class ModelTest(unittest.TestCase): @@ -118,11 +119,14 @@ def test_equality_works(self): lhs = db.Model("dft", "any", "none", "none") rhs = db.Model("dft", "something", "", "none") assert lhs == rhs + assert lhs.equal_without_periodic_boundary_check(rhs) lhs.program = "sparrow" rhs.program = "sparrow" assert lhs == rhs + assert lhs.equal_without_periodic_boundary_check(rhs) rhs.program = "something_different" assert lhs != rhs + assert not lhs.equal_without_periodic_boundary_check(rhs) lhs.program = "any" rhs.program = "something" assert lhs == rhs @@ -130,6 +134,11 @@ def test_equality_works(self): assert lhs != rhs rhs.program = "" assert lhs != rhs + another = deepcopy(rhs) + assert another.equal_without_periodic_boundary_check(rhs) + another.periodic_boundaries = "5.0,5.0,5.0,90.0,90.0,90.0,xyz" + assert another != rhs + assert another.equal_without_periodic_boundary_check(rhs) def test_model_output_string(self): m = db.Model("any", "any", "any", "any") diff --git a/src/Database/Python/Tests/test_database_setup.py b/src/Database/Python/Tests/test_database_setup.py index 945d2f7..955a9b5 100644 --- a/src/Database/Python/Tests/test_database_setup.py +++ b/src/Database/Python/Tests/test_database_setup.py @@ -34,6 +34,18 @@ def _water() -> utils.AtomCollection: return utils.AtomCollection(elements, positions) +def _water_cbor() -> str: + return "pGFhg6VhYQBhYwBhb4GCAAFhcqRhbIKBAYECY2xua4GiYXCCAAFjc2Vxgw" + \ + "ABAmJscoKBAIEBYXOCgQGBAmFzAaVhYQBhYwFhb4GCAAFhcqRhbIKBAIEC" + \ + "Y2xua4GiYXCCAAFjc2VxgwEAAmJscoKBAIEBYXOCgQCBAmFzAaVhYQBhYw" + \ + "Jhb4GCAAFhcqRhbIKBAIEBY2xua4GiYXCCAAFjc2VxgwIAAWJscoGCAAFh" + \ + "c4GCAAFhcwFhYw9hZ6JhRYODAAEAgwACAIMBAgBhWoMBAQhhdoMCAAA=" + + +def _water_split_cbor() -> str: + return "o2FjD2FnomFFgYMAAQBhWoIBCGF2gwIAAA==;o2FjD2FnomFFgGFagQFhdoMCAAA=" + + def get_test_db_credentials(name: str = "chemoton_unittests") -> db.Credentials: """ Generate a set of credentials pointing to a database and server. @@ -261,6 +273,7 @@ def _insert_flask( complex = db.Structure() complex.link(structures) complex.create(_water(), 0, 1) + complex.set_graph("masm_cbor_graph", _water_split_cbor()) complex.set_label(db.Label.COMPLEX_OPTIMIZED) complex.set_aggregate(flask.get_id()) complex.set_model(db.Model("FAKE", "FAKE", "F-AKE")) @@ -355,6 +368,7 @@ def _fake_structure( structure.set_label(db.Label.USER_OPTIMIZED) else: structure.set_label(db.Label.MINIMUM_OPTIMIZED) + structure.set_graph("masm_cbor_graph", _water_cbor()) structure.set_aggregate(compound.get_id()) structure.set_model(model) add_random_energy(structure, energy_limits, properties, model) @@ -579,8 +593,10 @@ def insert_single_empty_structure_aggregate(manager: db.Manager, label: db.Label aggregate.link(aggregates) if build_flask: aggregate.create([structure.get_id()], []) # type: ignore + structure.set_graph("masm_cbor_graph", _water_split_cbor()) else: aggregate.create([structure.get_id()]) # type: ignore + structure.set_graph("masm_cbor_graph", _water_cbor()) aggregate.disable_exploration() structure.set_aggregate(aggregate.get_id()) structure.set_label(label) @@ -613,6 +629,7 @@ def insert_single_empty_structure_flask(manager: db.Manager, label: db.Label) -> flask.create([structure.get_id()], []) flask.disable_exploration() structure.set_aggregate(flask.get_id()) + structure.set_graph("masm_cbor_graph", _water_split_cbor()) structure.set_label(label) structure.set_model(db.Model("FAKE", "FAKE", "F-AKE")) diff --git a/src/Database/Python/Tests/test_energy_query_functions.py b/src/Database/Python/Tests/test_energy_query_functions.py index f82955c..572778a 100644 --- a/src/Database/Python/Tests/test_energy_query_functions.py +++ b/src/Database/Python/Tests/test_energy_query_functions.py @@ -10,7 +10,9 @@ import scine_database as db import test_database_setup as db_setup from scine_database.energy_query_functions import ( - get_min_free_energy_for_aggregate + get_min_free_energy_for_aggregate, + barrier_from_rate_constant, + rate_constant_from_barrier ) @@ -77,3 +79,10 @@ def test_get_min_free_energy_for_aggregate(self): - (energies[len(energies) - 2] + gibbs_correction_2[len(gibbs_correction_2) - 2])) < 1e-12 manager.wipe() + + def test_barrier_to_rate_transformation(self): + temperature = 298.15 + barrier = 42.0 # in kJ / mol + + rate = rate_constant_from_barrier(barrier, temperature) + assert abs(barrier_from_rate_constant(rate, temperature) / 1000 - barrier) < 1e-12 diff --git a/src/Database/Python/energy_query_functions.py b/src/Database/Python/energy_query_functions.py index c306dcd..8024cd9 100644 --- a/src/Database/Python/energy_query_functions.py +++ b/src/Database/Python/energy_query_functions.py @@ -6,13 +6,15 @@ """ # Standard library imports -from typing import Union, Tuple, List, Optional +from typing import Union, Tuple, List, Optional, TypeVar import math # Third party imports import numpy as np import scine_database as db import scine_utilities as utils +# Type hint for float or numpy array +float_or_ndarray = TypeVar('float_or_ndarray', float, np.ndarray) def get_elementary_step_with_min_ts_energy(reaction: db.Reaction, @@ -62,13 +64,15 @@ def get_elementary_step_with_min_ts_energy(reaction: db.Reaction, es = db.ElementaryStep(es_id, elementary_steps) # # # Type check elementary step and break if barrierless if not es.has_transition_state(): - first_structure_lhs = db.Structure(es.get_reactants()[0][0], structures) - if structure_model is not None and first_structure_lhs.get_model() != structure_model: + es_structures = [db.Structure(s_id, structures) for s_id in es.get_reactants()[0]] +\ + [db.Structure(s_id, structures) for s_id in es.get_reactants()[1]] + # # # Check all models of the structures + if structure_model is not None and\ + any(es_struct.get_model() != structure_model for es_struct in es_structures): continue - # # # Energy Check for minima - first_structure_lhs_energy = get_energy_for_structure(first_structure_lhs, energy_type, model, - structures, properties) - if first_structure_lhs_energy is None: + # # # Check all energies of the structures + if any(get_energy_for_structure(es_struct, energy_type, model, structures, properties) is None + for es_struct in es_structures): continue es_id_with_lowest_ts = es_id break @@ -211,16 +215,16 @@ def get_energy_for_structure(structure: db.Structure, prop_name: str, model: db. return prop.get_data() -def rate_constant_from_barrier(barrier: float, temperature: float) -> float: +def rate_constant_from_barrier(barrier: float_or_ndarray, temperature: float) -> float_or_ndarray: """ Calculate a rate constant from its energy [kJ / mol] and temperature according to transition state theory: rate-constant = k_B T / h exp[-barrier/(R T)] Parameters ---------- - barrier :: float + barrier : float_or_ndarray The reaction barrier in kJ/mol. - temperature :: flaot + temperature : float The temperature in K. Returns @@ -235,6 +239,29 @@ def rate_constant_from_barrier(barrier: float, temperature: float) -> float: return factor * np.exp(- beta_in_mol_per_j * barrier_j_per_mol) +def barrier_from_rate_constant(rate_constant: float_or_ndarray, temperature: float) -> float_or_ndarray: + """ + Calculate a barrier [J / mol] from a rate konstant [s^-1] and temperature [K] according to transition state theory: + barrier = log(rate-constant * h / (k_B * T)) * -1 * (R * T) + + Parameters + ---------- + rate_constant : float_or_ndarray + The rate constant in s^-1. + temperature : float + The temperature in K. + + Returns + ------- + The barrier in J/mol. + """ + kbt_in_j = utils.BOLTZMANN_CONSTANT * temperature # k_B T + factor = utils.PLANCK_CONSTANT / kbt_in_j # h / k_B T + rt_in_j_per_mol = utils.MOLAR_GAS_CONSTANT * temperature # R T + + return -1.0 * rt_in_j_per_mol * np.log(rate_constant * factor) + + def get_all_energies_for_aggregate(aggregate: Union[db.Compound, db.Flask], model: db.Model, energy_label: str, structures: db.Collection, properties: db.Collection) -> List[Union[float, None]]: all_energies = [] @@ -246,6 +273,28 @@ def get_all_energies_for_aggregate(aggregate: Union[db.Compound, db.Flask], mode def get_min_energy_for_aggregate(aggregate: Union[db.Compound, db.Flask], model: db.Model, energy_label: str, structures: db.Collection, properties: db.Collection) -> Optional[float]: + """ + Get the minimum energy for a given aggregate. + + Parameters + ---------- + aggregate : Union[db.Compound, db.Flask] + The aggregate for which to retrieve the minimum energy. + model : db.Model + The database model. + energy_label : str + The label of the energy property. + structures : db.Collection + The collection of structures. + properties : db.Collection + The collection of properties. + + Returns + ------- + Optional[float] + The minimum energy value for the aggregate, + or None if no energy values are available. + """ all_energies = get_all_energies_for_aggregate(aggregate, model, energy_label, structures, properties) if len(all_energies) < 1: return None @@ -261,6 +310,28 @@ def get_min_energy_for_aggregate(aggregate: Union[db.Compound, db.Flask], model: def get_min_free_energy_for_aggregate(aggregate: Union[db.Compound, db.Flask], electronic_model: db.Model, correction_model: db.Model, structures: db.Collection, properties: db.Collection) -> Optional[float]: + """ + Calculate the minimum free energy for an aggregate. + The free energy correction can have a different model than the electronic model. + + Parameters + ---------- + aggregate : Union[db.Compound, db.Flask] + The aggregate for which to retrieve the minimum free energy. + electronic_model : db.Model + The electronic model used for energy calculations. + correction_model : db.Model + The correction model used for energy calculations. + structures : db.Collection + The collection of structures. + properties : db.Collection + The collection of properties. + + Returns + ------- + Optional[float] + The minimum free energy of the aggregate, or None if the energy calculation is not possible. + """ equal_models = electronic_model == correction_model if equal_models: return get_min_energy_for_aggregate(aggregate, electronic_model, "gibbs_free_energy", structures, properties) diff --git a/src/Database/Python/insert_concentration.py b/src/Database/Python/insert_concentration.py index 724879a..fb5174c 100644 --- a/src/Database/Python/insert_concentration.py +++ b/src/Database/Python/insert_concentration.py @@ -33,7 +33,7 @@ def insert_concentration_for_structure( label: str = "start_concentration"): concentration_options = ["start_concentration", "max_concentration", "final_concentration", "concentration_flux", "manual_activation"] - if label not in concentration_options and "_concentration_flux" not in label: + if label not in concentration_options and "_concentration_flux" not in label and "sensitivity" not in label: warn(f"Your concentration label is not within the suggested labels {str(concentration_options)}." f"This may lead to problems recognizing the concentrations in the individual gears!") diff --git a/src/Database/Python/queries.py b/src/Database/Python/queries.py index 29868bf..1912333 100644 --- a/src/Database/Python/queries.py +++ b/src/Database/Python/queries.py @@ -269,15 +269,17 @@ def calculation_exists_in_structure(job_order: str, structure_id_list: List[db.I def calculation_exists_in_id_set(id_selection: Set[str], n_structures: int, calculations: db.Collection, specific_structures: Optional[List[db.ID]] = None, settings: Optional[Union[utils.ValueCollection, Dict[str, Any]]] = None, - auxiliaries: Optional[Dict[str, Any]] = None) -> bool: + auxiliaries: Optional[Dict[str, Any]] = None, + job_order: Optional[str] = None) -> bool: return query_calculation_in_id_set(id_selection, n_structures, calculations, specific_structures, - settings, auxiliaries) is not None + settings, auxiliaries, job_order) is not None def query_calculation_in_id_set(id_selection: Set[str], n_structures: int, calculations: db.Collection, specific_structures: Optional[List[db.ID]] = None, settings: Optional[Union[utils.ValueCollection, Dict[str, Any]]] = None, - auxiliaries: Optional[Dict[str, Any]] = None) -> Union[db.ID, None]: + auxiliaries: Optional[Dict[str, Any]] = None, + job_order: Optional[str] = None) -> Union[db.ID, None]: """ Check if a calculation exists that corresponds to the given structures, mode, settings, etc. @@ -300,6 +302,8 @@ def query_calculation_in_id_set(id_selection: Set[str], n_structures: int, calcu The settings of the calculation. auxiliaries : Optional[Dict[str, Any]] The auxiliaries of the calculation. + job_order : Optional[str] + The job order of the calculation. Returns ------- @@ -340,6 +344,9 @@ def query_calculation_in_id_set(id_selection: Set[str], n_structures: int, calcu if auxiliaries is not None: if auxiliaries != calculation.get_auxiliaries(): continue + if job_order is not None: + if job_order != calculation.get_job().order: + continue return calculation.id() return None @@ -377,7 +384,7 @@ def get_calculation_id_from_structure(job_order: str, structure_id_list: List[db if not calc_id_set: return None return query_calculation_in_id_set(calc_id_set, len(structure_id_list), calculations, structure_id_list, - settings, auxiliaries) + settings, auxiliaries, job_order) def get_calculation_id(job_order: str, structure_id_list: List[db.ID], model: db.Model, diff --git a/src/Database/Python/setup.py b/src/Database/Python/setup.py index b8f57f7..a3d37a5 100644 --- a/src/Database/Python/setup.py +++ b/src/Database/Python/setup.py @@ -9,7 +9,7 @@ import os # Read README.rst for the long description -with open("README.rst", "r") as fh: +with open("README.rst", "r", encoding="utf-8") as fh: long_description = fh.read() diff --git a/src/Database/Tests/CalculationTest.cpp b/src/Database/Tests/CalculationTest.cpp index d582b04..7ded532 100644 --- a/src/Database/Tests/CalculationTest.cpp +++ b/src/Database/Tests/CalculationTest.cpp @@ -314,13 +314,25 @@ TEST_F(CalculationTest, Settings) { auto settings = calc.getSettings(); ASSERT_EQ(settings.size(), 0); + const std::vector> listOfLists = {{0, 1, 2}, {3, 4, 5}, {-1, -2, 9}}; calc.setSetting("foo", Utils::UniversalSettings::GenericValue::fromString("bar")); calc.setSetting("spam", Utils::UniversalSettings::GenericValue::fromInt(4)); + calc.setSetting("spam_with_eggs", Utils::UniversalSettings::GenericValue::fromIntListList(listOfLists)); auto settings_db = calc.getSettings(); ASSERT_EQ(settings_db.getString("foo"), "bar"); ASSERT_EQ(settings_db.getInt("spam"), 4); ASSERT_EQ(calc.getSetting("foo").toString(), "bar"); ASSERT_EQ(calc.getSetting("spam").toInt(), 4); + const std::vector> listOfListsFromSettings = calc.getSetting("spam_with_eggs").toIntListList(); + ASSERT_EQ(listOfListsFromSettings.size(), listOfLists.size()); + for (unsigned int i = 0; i < listOfListsFromSettings.size(); ++i) { + const std::vector sublist = listOfListsFromSettings[i]; + const std::vector sublistRef = listOfLists[i]; + ASSERT_EQ(sublist.size(), sublistRef.size()); + for (unsigned int j = 0; j < sublist.size(); ++j) { + ASSERT_EQ(sublist[j], sublistRef[j]); + } + } calc.removeSetting("foo"); settings_db = calc.getSettings(); diff --git a/src/Database/Tests/ElementaryStepTest.cpp b/src/Database/Tests/ElementaryStepTest.cpp index 9e78c27..4ab59de 100644 --- a/src/Database/Tests/ElementaryStepTest.cpp +++ b/src/Database/Tests/ElementaryStepTest.cpp @@ -171,10 +171,6 @@ TEST_F(ElementaryStepTest, Type) { step.setType(ElementaryStepType::BARRIERLESS); ASSERT_EQ(ElementaryStepType::BARRIERLESS, step.getType()); ASSERT_NE(ElementaryStepType::REGULAR, step.getType()); - step.setType(ElementaryStepType::MODEL_TRANSFORMATION); - ASSERT_EQ(ElementaryStepType::MODEL_TRANSFORMATION, step.getType()); - ASSERT_NE(ElementaryStepType::REGULAR, step.getType()); - ASSERT_NE(ElementaryStepType::BARRIERLESS, step.getType()); } TEST_F(ElementaryStepTest, TypeFails1) { diff --git a/src/Database/Tests/ModelTest.cpp b/src/Database/Tests/ModelTest.cpp index 9477070..3e89da5 100644 --- a/src/Database/Tests/ModelTest.cpp +++ b/src/Database/Tests/ModelTest.cpp @@ -186,11 +186,14 @@ TEST_F(ModelTest, EqualityWorks) { Model lhs("dft", "any", "none", "none"); Model rhs("dft", "something", "", "none"); ASSERT_TRUE(lhs == rhs); + ASSERT_TRUE(lhs.equalWithoutPeriodicBoundaryCheck(rhs)); lhs.program = "sparrow"; rhs.program = "Sparrow"; ASSERT_TRUE(lhs == rhs); + ASSERT_TRUE(lhs.equalWithoutPeriodicBoundaryCheck(rhs)); rhs.program = "something_different"; ASSERT_FALSE(lhs == rhs); + ASSERT_FALSE(lhs.equalWithoutPeriodicBoundaryCheck(rhs)); lhs.program = "any"; rhs.program = "something"; ASSERT_TRUE(lhs == rhs); @@ -212,7 +215,9 @@ TEST_F(ModelTest, EqualityWorks) { lhs.periodicBoundaries = "any"; rhs.periodicBoundaries = "ANY"; ASSERT_TRUE(lhs == rhs); + ASSERT_TRUE(lhs.equalWithoutPeriodicBoundaryCheck(rhs)); rhs.periodicBoundaries = "none"; + ASSERT_TRUE(lhs.equalWithoutPeriodicBoundaryCheck(rhs)); ASSERT_FALSE(lhs == rhs); rhs.periodicBoundaries = "8.0,10.0,10.0,45.0,90.0,90.0,xyz"; ASSERT_TRUE(lhs == rhs); @@ -220,8 +225,10 @@ TEST_F(ModelTest, EqualityWorks) { ASSERT_TRUE(lhs == rhs); rhs.periodicBoundaries = "8.00,10.0,10.0,45.0,90.0,90.00,xyz"; ASSERT_TRUE(lhs == rhs); + ASSERT_TRUE(lhs.equalWithoutPeriodicBoundaryCheck(rhs)); rhs.periodicBoundaries = "8.00,10.1,10.0,45.0,90.0,90.00,xyz"; ASSERT_FALSE(lhs == rhs); + ASSERT_TRUE(lhs.equalWithoutPeriodicBoundaryCheck(rhs)); } TEST_F(ModelTest, EntryNoneAndAnyChecksWork) { diff --git a/updates/0_0_x_to_1_0_0.py b/updates/0_0_x_to_1_0_0.py index 3890351..7376d51 100644 --- a/updates/0_0_x_to_1_0_0.py +++ b/updates/0_0_x_to_1_0_0.py @@ -1,3 +1,9 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +__copyright__ = """ This code is licensed under the 3-clause BSD license. +Copyright ETH Zurich, Department of Chemistry and Applied Biosciences, Reiher Group. +See LICENSE.txt for details. +""" from pymongo import MongoClient import datetime import argparse @@ -17,13 +23,14 @@ db_name = args.db_name -client = MongoClient(ip, port) +client: MongoClient = MongoClient(ip, port) db = client[db_name] # Check version is_correct_old_version = False if "_db_meta_data" in db.list_collection_names(): meta_data = db["_db_meta_data"].find_one({}) + assert meta_data version = meta_data["version"] if version["major"] == 0 and version["minor"] == 0: is_correct_old_version = True @@ -42,6 +49,7 @@ if "_db_meta_data" in db.list_collection_names(): _db_meta_data = db["_db_meta_data"] meta_data = db["_db_meta_data"].find_one({}) + assert meta_data date = meta_data["_created"] db["_db_meta_data"].delete_many({}) else: diff --git a/updates/1_0_0_to_1_1_0.py b/updates/1_0_0_to_1_1_0.py index c3dc4f9..fe7f39f 100644 --- a/updates/1_0_0_to_1_1_0.py +++ b/updates/1_0_0_to_1_1_0.py @@ -1,4 +1,11 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +__copyright__ = """ This code is licensed under the 3-clause BSD license. +Copyright ETH Zurich, Department of Chemistry and Applied Biosciences, Reiher Group. +See LICENSE.txt for details. +""" from pymongo import MongoClient +from typing import Any, List, Dict from bson.objectid import ObjectId import datetime import argparse @@ -20,13 +27,14 @@ db_name = args.db_name extract_duplicates = args.duplicates -client = MongoClient(ip, port) +client: MongoClient = MongoClient(ip, port) db = client[db_name] # Check version is_correct_old_version = False if "_db_meta_data" in db.list_collection_names(): meta_data = db["_db_meta_data"].find_one({}) + assert meta_data version = meta_data["version"] if version["major"] == 1 and version["minor"] == 0: is_correct_old_version = True @@ -44,6 +52,7 @@ flasks = db["flasks"] flasks.create_index("flasks") # Add type to reactants of reaction +reaction: Dict[str, List[Dict[str, Any]]] for reaction in db["reactions"].find(): if len(reaction['lhs']) == 0 and len(reaction['rhs']) == 0: continue @@ -53,7 +62,7 @@ continue elif len(reaction['rhs']) != 0: if isinstance(reaction['rhs'][0], type(dict)): - if 'type' in reaction['rhs'][0]: + if 'type' in reaction['rhs'][0].keys(): continue lids = [lid for lid in reaction['lhs']] new_lhs = [] @@ -89,6 +98,7 @@ if "_db_meta_data" in db.list_collection_names(): _db_meta_data = db["_db_meta_data"] meta_data = db["_db_meta_data"].find_one({}) + assert meta_data date = meta_data["_created"] db["_db_meta_data"].delete_many({}) else: diff --git a/updates/1_1_0_to_1_2_0.py b/updates/1_1_0_to_1_2_0.py index 001457b..1b3e32b 100644 --- a/updates/1_1_0_to_1_2_0.py +++ b/updates/1_1_0_to_1_2_0.py @@ -1,5 +1,10 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +__copyright__ = """ This code is licensed under the 3-clause BSD license. +Copyright ETH Zurich, Department of Chemistry and Applied Biosciences, Reiher Group. +See LICENSE.txt for details. +""" from pymongo import MongoClient -from bson.objectid import ObjectId import datetime import argparse @@ -17,13 +22,14 @@ port = args.port db_name = args.db_name -client = MongoClient(ip, port) +client: MongoClient = MongoClient(ip, port) db = client[db_name] # Check version is_correct_old_version = False if "_db_meta_data" in db.list_collection_names(): meta_data = db["_db_meta_data"].find_one({}) + assert meta_data version = meta_data["version"] if version["major"] == 1 and version["minor"] == 1: is_correct_old_version = True @@ -49,6 +55,7 @@ if "_db_meta_data" in db.list_collection_names(): _db_meta_data = db["_db_meta_data"] meta_data = db["_db_meta_data"].find_one({}) + assert meta_data date = meta_data["_created"] db["_db_meta_data"].delete_many({}) else: diff --git a/updates/1_2_0_to_1_3_0.py b/updates/1_2_0_to_1_3_0.py new file mode 100644 index 0000000..b974f53 --- /dev/null +++ b/updates/1_2_0_to_1_3_0.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +__copyright__ = """ This code is licensed under the 3-clause BSD license. +Copyright ETH Zurich, Department of Chemistry and Applied Biosciences, Reiher Group. +See LICENSE.txt for details. +""" +from pymongo import MongoClient +import datetime +import argparse + + +parser = argparse.ArgumentParser(description='SCINE Database Update 1.2.X to 1.3.0.') +parser.add_argument('--ip', dest='ip', type=str, default='localhost', + help='The database server IP or hostname. [default = localhost]') +parser.add_argument('--port', dest='port', type=int, default=27017, + help='The database server port. [default = 27017]') +parser.add_argument('--name', dest='db_name', type=str, default='my_awesome_database', + help='The database name. [default = my_awesome_database]') +args = parser.parse_args() + +ip = args.ip +port = args.port +db_name = args.db_name + +client: MongoClient = MongoClient(ip, port) +db = client[db_name] + +# Check version +is_correct_old_version = False +if "_db_meta_data" in db.list_collection_names(): + meta_data = db["_db_meta_data"].find_one({}) + assert meta_data + version = meta_data["version"] + if version["major"] == 1 and version["minor"] == 2: + is_correct_old_version = True +else: + is_correct_old_version = False + +if not is_correct_old_version: + print("The database does not have the correct old version for this update.") + print("Exiting!") + exit(1) + +# Check for model_transformation steps +step = db["elementary_steps"].find_one({"type": "model_transformation"}) +if step is not None: + print("The database contains outdated elementary steps of type model_transformation.") + print("These steps must be removed manually before updating") + exit(1) + +# Update version +if "_db_meta_data" in db.list_collection_names(): + _db_meta_data = db["_db_meta_data"] + meta_data = db["_db_meta_data"].find_one({}) + assert meta_data + date = meta_data["_created"] + db["_db_meta_data"].delete_many({}) +else: + date = datetime.datetime.utcnow() + _db_meta_data = db["_db_meta_data"] +post = { + "version": { + "major": 1, + "minor": 3, + "patch": 0 + }, + "_created": date +} +_db_meta_data.insert_one(post) diff --git a/updates/requirements.txt b/updates/requirements.txt new file mode 100644 index 0000000..de4887b --- /dev/null +++ b/updates/requirements.txt @@ -0,0 +1 @@ +pymongo