Skip to content

Commit

Permalink
More cleanups, including a manager class to filter methods
Browse files Browse the repository at this point in the history
Signed-off-by: Geoff Hutchison <[email protected]>
  • Loading branch information
ghutchis committed Oct 14, 2023
1 parent d73b186 commit dfad831
Show file tree
Hide file tree
Showing 11 changed files with 365 additions and 185 deletions.
2 changes: 2 additions & 0 deletions avogadro/calc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ avogadro_headers(Calc
chargemanager.h
defaultmodel.h
energycalculator.h
energymanager.h
lennardjones.h
)

Expand All @@ -13,6 +14,7 @@ target_sources(Calc PRIVATE
chargemanager.cpp
defaultmodel.cpp
energycalculator.cpp
energymanager.cpp
lennardjones.cpp
)

Expand Down
2 changes: 1 addition & 1 deletion avogadro/calc/chargemanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class AVOGADROCALC_EXPORT ChargeManager

/**
* @brief Register a new charge model with the manager.
* @param format An instance of the format to manage, the manager assumes
* @param model An instance of the model to manage, the manager assumes
* ownership of the object passed in.
* @return True on success, false on failure.
*/
Expand Down
29 changes: 29 additions & 0 deletions avogadro/calc/energycalculator.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "avogadrocalcexport.h"

#include <avogadro/core/molecule.h>
#include <avogadro/core/variantmap.h>
#include <avogadro/core/vector.h>

Expand Down Expand Up @@ -46,6 +47,34 @@ class AVOGADROCALC_EXPORT EnergyCalculator : public cppoptlib::Problem<Real>
*/
virtual bool setConfiguration(Core::VariantMap& config) { return true; }

/**
* @brief Indicate if your method only treats a subset of elements
* @return an element mask corresponding to the defined subset
*/
virtual Core::Molecule::ElementMask elements() const = 0;

/**
* @brief Indicate if your method can handle unit cells
* @return true if unit cells are supported
*/
virtual bool acceptsUnitCell() const { return false; }

/**
* @brief Indicate if your method can handle ions
* Many methods only treat neutral systems, either
* a neutral molecule or a neutral unit cell.
*
* @return true if ions are supported
*/
virtual bool acceptsIons() const { return false; }

/**
* @brief Indicate if your method can handle radicals
* Most methods only treat closed-shell molecules.
* @return true if radicals are supported
*/
virtual bool acceptsRadicals() const { return false; }

/**
* Calculate the gradients for this method, defaulting to numerical
* finite-difference methods
Expand Down
124 changes: 124 additions & 0 deletions avogadro/calc/energymanager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/******************************************************************************
This source file is part of the Avogadro project.
This source code is released under the 3-Clause BSD License, (see "LICENSE").
******************************************************************************/

#include "energymanager.h"
#include "energycalculator.h"
#include "lennardjones.h"

#include <algorithm>
#include <memory>

namespace Avogadro::Calc {

EnergyManager& EnergyManager::instance()
{
static EnergyManager instance;
return instance;
}

void EnergyManager::appendError(const std::string& errorMessage)
{
m_error += errorMessage + "\n";
}

bool EnergyManager::registerModel(EnergyCalculator* model)
{
return instance().addModel(model);
}

bool EnergyManager::unregisterModel(const std::string& identifier)
{
return instance().removeModel(identifier);
}

bool EnergyManager::addModel(EnergyCalculator* model)
{
if (model == nullptr) {
appendError("Supplied model was null.");
return false;
}

if (m_identifiers.find(model->identifier()) != m_identifiers.end()) {
appendError("Model " + model->identifier() + " already loaded.");
return false;
}

// If we got here then the format is unique enough to be added.
size_t index = m_models.size();
m_models.push_back(model);
m_identifiers[model->identifier()] = index;
m_identifierToName[model->identifier()] = model->name();

return true;
}

bool EnergyManager::removeModel(const std::string& identifier)
{
auto ids = m_identifiers[identifier];
m_identifiers.erase(identifier);
m_identifierToName.erase(identifier);

auto* model = m_models[ids];

if (model != nullptr) {
m_models[ids] = nullptr;
delete model;
}

return true;
}

std::string EnergyManager::nameForModel(const std::string& identifier) const
{
auto it = m_identifierToName.find(identifier);
if (it == m_identifierToName.end()) {
return identifier;
}
return it->second;
}

EnergyManager::EnergyManager()
{
// add any default models here (EEM maybe?)
}

EnergyManager::~EnergyManager()
{
// Delete the models that were loaded.
for (auto& m_model : m_models) {
delete m_model;
}
m_models.clear();
}

std::set<std::string> EnergyManager::identifiersForMolecule(
const Core::Molecule& molecule) const
{
std::set<std::string> identifiers;

// check our models for compatibility
for (auto m_model : m_models) {
// we can check easy things first
// - is the molecule an ion based on total charge
// - is the molecule a radical based on spin multiplicity
// - does the molecule have a unit cell
if (molecule.totalCharge() != 0 && !m_model->acceptsIons())
continue;
if (molecule.totalSpinMultiplicity() != 1 && !m_model->acceptsRadicals())
continue;
if (molecule.unitCell() != nullptr && !m_model->acceptsUnitCell())
continue;

// Finally, we check that every element in the molecule
// is handled by the model
auto mask = m_model->elements() & molecule.elements();
if (mask.count() == molecule.elements().count())
identifiers.insert(m_model->identifier()); // this one will work
}

return identifiers;
}

} // namespace Avogadro::Calc
143 changes: 143 additions & 0 deletions avogadro/calc/energymanager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/******************************************************************************
This source file is part of the Avogadro project.
This source code is released under the 3-Clause BSD License, (see "LICENSE").
******************************************************************************/

#ifndef AVOGADRO_CALC_ENERGYMANAGER_H
#define AVOGADRO_CALC_ENERGYMANAGER_H

#include "avogadrocalcexport.h"

#include <avogadro/core/array.h>
#include <avogadro/core/matrix.h>
#include <avogadro/core/vector.h>

#include <map>
#include <set>
#include <string>
#include <vector>

namespace Avogadro {
namespace Core {
class Molecule;
}
namespace Calc {

class EnergyCalculator;

/**
* @class EnergyManager chargemanager.h
* <avogadro/calc/energymanager.h>
* @brief Class to manage registration, searching and creation of force field
* (energy) calculators.
* @author Geoffrey R. Hutchison
*
* The energy manager is a singleton class that handles the runtime
* registration, search, creation and eventual destruction of calculators
* for geometry optimization and molecular dynamics.
* It can be used to gain a listing of available models, register new
* models, etc.
*
* All energy calculation can take place independent of this code, but for
* automated registration and look up, this is the preferred API.
*/
class AVOGADROCALC_EXPORT EnergyManager
{
public:
/**
* Get the singleton instance of the energy manager. This instance should
* not be deleted.
*/
static EnergyManager& instance();

/**
* @brief Register a new model with the manager.
* @param model An instance of the calculator to manage, the manager assumes
* ownership of the object passed in.
* @return True on success, false on failure.
*/
static bool registerModel(EnergyCalculator* model);

/**
* @brief Unregister a charge model from the manager.
* @param identifier The identifier for the model to remove.
* @return True on success, false on failure.
*/
static bool unregisterModel(const std::string& identifier);

/**
* Add the supplied @p model to the manager, registering its ID and other
* relevant data for later lookup. The manager assumes ownership of the
* supplied object.
* @return True on success, false on failure.
*/
bool addModel(EnergyCalculator* model);

/**
* Remove the model with the identifier @a identifier from the manager.
* @return True on success, false on failure.
*/
bool removeModel(const std::string& identifier);

/**
* New instance of the model for the specified @p identifier. Ownership
* is passed to the caller.
* @param identifier The unique identifier of the model.
* @return Instance of the model, nullptr if not found. Ownership passes to
* the caller.
*/
EnergyCalculator* newModelFromIdentifier(const std::string& identifier) const;

/**
* Get a list of all loaded identifiers
*/
std::set<std::string> identifiers() const;

/**
* @brief Get a list of models that work for this molecule.
*
* This is probably the method you want to get a list for a user
*/
std::set<std::string> identifiersForMolecule(
const Core::Molecule& molecule) const;

/**
* @brief Get the name of the model for the specified identifier.
*
* The name is a user-visible string, and may be translated.
* @param identifier The unique identifier of the model.
* @return The name of the model, or an empty string if not found.
*/
std::string nameForModel(const std::string& identifier) const;

/**
* Get any errors that have been logged when loading models.
*/
std::string error() const;

private:
typedef std::map<std::string, size_t> ModelIdMap;

EnergyManager();
~EnergyManager();

EnergyManager(const EnergyManager&); // Not implemented.
EnergyManager& operator=(const EnergyManager&); // Not implemented.

/**
* @brief Append warnings/errors to the error message string.
* @param errorMessage The error message to append.
*/
void appendError(const std::string& errorMessage);

std::vector<EnergyCalculator*> m_models;
mutable ModelIdMap m_identifiers;
mutable std::map<std::string, std::string> m_identifierToName;

std::string m_error;
};

} // namespace Calc
} // namespace Avogadro

#endif // AVOGADRO_CALC_ENERGYMANAGER_H
Loading

0 comments on commit dfad831

Please sign in to comment.