feat(composition): added composition class
in order to get composition in a standardized way anywhere in the code we now have a composition class
This commit is contained in:
@@ -11,6 +11,7 @@ dependencies = [
|
||||
probe_dep,
|
||||
config_dep,
|
||||
quill_dep,
|
||||
species_weight_dep
|
||||
]
|
||||
|
||||
# Define the libcomposition library so it can be linked against by other parts of the build system
|
||||
|
||||
153
src/composition/private/composition.cpp
Normal file
153
src/composition/private/composition.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#include "composition.h"
|
||||
#include "quill/LogMacros.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "atomicSpecies.h"
|
||||
|
||||
using namespace composition;
|
||||
|
||||
Composition::Composition(const std::vector<std::string>& symbols) {
|
||||
for (const auto& symbol : symbols) {
|
||||
registerSymbol(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
void Composition::validateComposition(const std::vector<double>& mass_fractions) const {
|
||||
if (!isValidComposition(mass_fractions)) {
|
||||
LOG_ERROR(m_logger, "Invalid composition.");
|
||||
throw std::runtime_error("Invalid composition.");
|
||||
}
|
||||
}
|
||||
|
||||
bool Composition::isValidComposition(const std::vector<double>& mass_fractions) const {
|
||||
double sum = 0.0;
|
||||
for (const auto& mass_fraction : mass_fractions) {
|
||||
sum += mass_fraction;
|
||||
}
|
||||
if (sum < 0.99999999 || sum > 1.000000001) {
|
||||
LOG_ERROR(m_logger, "The sum of mass fractions must be equal to 1.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Composition::Composition(const std::vector<std::string>& symbols, const std::vector<double>& mass_fractions) {
|
||||
if (symbols.size() != mass_fractions.size()) {
|
||||
LOG_ERROR(m_logger, "The number of symbols and mass fractions must be equal.");
|
||||
throw std::runtime_error("The number of symbols and mass fractions must be equal.");
|
||||
}
|
||||
|
||||
validateComposition(mass_fractions);
|
||||
finalize();
|
||||
|
||||
for (const auto &symbol : symbols) {
|
||||
registerSymbol(symbol);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < symbols.size(); ++i) {
|
||||
setComposition(symbols[i], mass_fractions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void Composition::registerSymbol(const std::string& symbol) {
|
||||
if (!isValidSymbol(symbol)) {
|
||||
LOG_ERROR(m_logger, "Invalid symbol: {}", symbol);
|
||||
throw std::runtime_error("Invalid symbol.");
|
||||
}
|
||||
|
||||
if (m_registeredSymbols.find(symbol) != m_registeredSymbols.end()) {
|
||||
LOG_WARNING(m_logger, "Symbol {} is already registered.", symbol);
|
||||
return;
|
||||
}
|
||||
|
||||
m_registeredSymbols.insert(symbol);
|
||||
LOG_INFO(m_logger, "Registered symbol: {}", symbol);
|
||||
}
|
||||
|
||||
bool Composition::isValidSymbol(const std::string& symbol) const {
|
||||
return chemSpecies::species.count(symbol) > 0;
|
||||
}
|
||||
|
||||
double Composition::setComposition(const std::string& symbol, const double& mass_fraction) {
|
||||
m_finalized = false;
|
||||
if (m_registeredSymbols.find(symbol) == m_registeredSymbols.end()) {
|
||||
LOG_ERROR(m_logger, "Symbol {} is not registered.", symbol);
|
||||
throw std::runtime_error("Symbol is not registered.");
|
||||
}
|
||||
|
||||
if (mass_fraction < 0.0 || mass_fraction > 1.0) {
|
||||
LOG_ERROR(m_logger, "Mass fraction must be between 0 and 1.");
|
||||
throw std::runtime_error("Mass fraction must be between 0 and 1.");
|
||||
}
|
||||
|
||||
double old_mass_fraction = 0.0;
|
||||
if (m_compositions.find(symbol) != m_compositions.end()) {
|
||||
old_mass_fraction = m_compositions[symbol].mass_fraction;
|
||||
}
|
||||
m_compositions[symbol] = {symbol, mass_fraction};
|
||||
|
||||
|
||||
return old_mass_fraction;
|
||||
}
|
||||
|
||||
std::vector<double> Composition::setComposition(const std::vector<std::string>& symbols, const std::vector<double>& mass_fractions) {
|
||||
m_finalized = false;
|
||||
if (symbols.size() != mass_fractions.size()) {
|
||||
LOG_ERROR(m_logger, "The number of symbols and mass fractions must be equal.");
|
||||
throw std::runtime_error("The number of symbols and mass fractions must be equal.");
|
||||
}
|
||||
|
||||
std::vector<double> old_mass_fractions;
|
||||
old_mass_fractions.reserve(symbols.size());
|
||||
for (size_t i = 0; i < symbols.size(); ++i) {
|
||||
old_mass_fractions.push_back(setComposition(symbols[i], mass_fractions[i]));
|
||||
}
|
||||
return old_mass_fractions;
|
||||
}
|
||||
|
||||
std::set<std::string> Composition::getRegisteredSymbols() const {
|
||||
return m_registeredSymbols;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, CompositionEntry> Composition::getCompositions() const {
|
||||
if (!m_finalized) {
|
||||
LOG_ERROR(m_logger, "Composition has not been finalized.");
|
||||
throw std::runtime_error("Composition has not been finalized (Consider running .finalize()).");
|
||||
}
|
||||
return m_compositions;
|
||||
}
|
||||
|
||||
bool Composition::finalize() {
|
||||
m_finalized = true;
|
||||
std::vector<double> mass_fractions;
|
||||
mass_fractions.reserve(m_compositions.size());
|
||||
for (const auto& [_, entry] : m_compositions) {
|
||||
mass_fractions.push_back(entry.mass_fraction);
|
||||
}
|
||||
try {
|
||||
validateComposition(mass_fractions);
|
||||
} catch (const std::runtime_error& e) {
|
||||
double massSum = 0.0;
|
||||
for (const auto& [_, entry] : m_compositions) {
|
||||
massSum += entry.mass_fraction;
|
||||
}
|
||||
LOG_ERROR(m_logger, "Composition is invalid (Total mass {}).", massSum);
|
||||
m_finalized = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
CompositionEntry Composition::getComposition(const std::string& symbol) const {
|
||||
if (!m_finalized) {
|
||||
LOG_ERROR(m_logger, "Composition has not been finalized.");
|
||||
throw std::runtime_error("Composition has not been finalized (Consider running .finalize()).");
|
||||
}
|
||||
if (m_compositions.count(symbol) == 0) {
|
||||
LOG_ERROR(m_logger, "Symbol {} is not in the composition.", symbol);
|
||||
throw std::runtime_error("Symbol is not in the composition.");
|
||||
}
|
||||
return m_compositions.at(symbol);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
#ifndef COMPOSITION_H
|
||||
#define COMPOSITION_H
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "quill/LogMacros.h
|
||||
|
||||
#include "probe.h"
|
||||
#include "config.h"
|
||||
|
||||
struct CompositionEntry {
|
||||
std::string symbol;
|
||||
std::string mass_fraction;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const CompositionEntry& entry) {
|
||||
os << std::setw(5) << "<" << entry.symbol << " : " << entry.mass_fraction << ">";
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
class Composition {
|
||||
private:
|
||||
Config& m_config = Config::getInstance();
|
||||
Probe::LogManager& m_logManager = Probe::LogManager::getInstance();
|
||||
quill::Logger* m_logger = logManager.getLogger('log');
|
||||
|
||||
std::vector<std::string> m_registeredSymbols;
|
||||
std::unordered_map<std::string, CompositionEntry> m_compositions;
|
||||
|
||||
}
|
||||
|
||||
#endif // COMPOSITION_H
|
||||
214
src/composition/public/composition.h
Normal file
214
src/composition/public/composition.h
Normal file
@@ -0,0 +1,214 @@
|
||||
#ifndef COMPOSITION_H
|
||||
#define COMPOSITION_H
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
|
||||
#include "quill/LogMacros.h"
|
||||
|
||||
#include "probe.h"
|
||||
#include "config.h"
|
||||
|
||||
#include "atomicSpecies.h"
|
||||
|
||||
namespace composition{
|
||||
/**
|
||||
* @brief Represents an entry in the composition with a symbol and mass fraction.
|
||||
*/
|
||||
struct CompositionEntry {
|
||||
std::string symbol; ///< The chemical symbol of the element.
|
||||
double mass_fraction; ///< The mass fraction of the element.
|
||||
|
||||
/**
|
||||
* @brief Overloaded output stream operator for CompositionEntry.
|
||||
* @param os The output stream.
|
||||
* @param entry The CompositionEntry to output.
|
||||
* @return The output stream.
|
||||
*/
|
||||
friend std::ostream& operator<<(std::ostream& os, const CompositionEntry& entry) {
|
||||
os << "<" << entry.symbol << " : " << entry.mass_fraction << ">";
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Manages the composition of elements.
|
||||
* @details The composition is a collection of elements with their respective mass fractions.
|
||||
* The general purpose of this class is to provide a standardized interface for managing the composition of
|
||||
* any part of 4DSSE. There are a few rules when using this class.
|
||||
* - Only species in the atomicSpecies.h database can be used. There are 1000s (All species from AME2020) in there so it should not be a problem.
|
||||
* - Before a mass fraction can be set with a particular instance of Composition, the symbol must be registered. (i.e. register He-3 before setting its mass fraction)
|
||||
* - Before any composition information can be retrived (e.g. getComposition), the composition must be finalized (call to .finalize()). This checks if the total mass fraction sums to approximatly 1 (within 1 part in 10^8)
|
||||
* - Any changes made to the composition after finalization will "unfinalize" the composition. This means that the composition must be finalized again before any information can be retrived.
|
||||
* - The mass fraction of any individual species must be no more than 1 and no less than 0.
|
||||
* - The only exception to the finalize rule is if the compositon was constructed with symbols and mass fractions at instantiation time. In this case, the composition is automatically finalized.
|
||||
* however, this means that the composition passed to the constructor must be valid.
|
||||
*
|
||||
* @example Constructing a finalized composition with symbols and mass fractions:
|
||||
* @code
|
||||
* std::vector<std::string> symbols = {"H", "O"};
|
||||
* std::vector<double> mass_fractions = {0.1, 0.9};
|
||||
* Composition comp(symbols, mass_fractions);
|
||||
* @endcode
|
||||
* @example Constructing a composition with symbols and finalizing it later:
|
||||
* @code
|
||||
* std::vector<std::string> symbols = {"H", "O"};
|
||||
* Composition comp(symbols);
|
||||
* comp.setComposition("H", 0.1);
|
||||
* comp.setComposition("O", 0.9);
|
||||
* comp.finalize();
|
||||
* @endcode
|
||||
*/
|
||||
class Composition {
|
||||
private:
|
||||
Config& m_config = Config::getInstance();
|
||||
Probe::LogManager& m_logManager = Probe::LogManager::getInstance();
|
||||
quill::Logger* m_logger = m_logManager.getLogger("log");
|
||||
|
||||
bool m_finalized = false;
|
||||
|
||||
std::set<std::string> m_registeredSymbols;
|
||||
std::unordered_map<std::string, CompositionEntry> m_compositions;
|
||||
|
||||
/**
|
||||
* @brief Checks if the given symbol is valid.
|
||||
* @details A symbol is valid if it is in the atomic species database (species in atomicSpecies.h). These include all the isotopes from AME2020.
|
||||
* @param symbol The symbol to check.
|
||||
* @return True if the symbol is valid, false otherwise.
|
||||
*/
|
||||
bool isValidSymbol(const std::string& symbol) const;
|
||||
|
||||
/**
|
||||
* @brief Checks if the given mass fractions are valid.
|
||||
* @param mass_fractions The mass fractions to check.
|
||||
* @return True if the mass fractions are valid, false otherwise.
|
||||
*/
|
||||
bool isValidComposition(const std::vector<double>& mass_fractions) const;
|
||||
|
||||
/**
|
||||
* @brief Validates the given mass fractions.
|
||||
* @param mass_fractions The mass fractions to validate.
|
||||
* @throws std::invalid_argument if the mass fractions are invalid.
|
||||
*/
|
||||
void validateComposition(const std::vector<double>& mass_fractions) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor.
|
||||
*/
|
||||
Composition() = default;
|
||||
|
||||
/**
|
||||
* @brief Default destructor.
|
||||
*/
|
||||
~Composition() = default;
|
||||
|
||||
/**
|
||||
* @brief Finalizes the composition.
|
||||
* @return True if the composition is successfully finalized, false otherwise.
|
||||
*/
|
||||
bool finalize();
|
||||
|
||||
/**
|
||||
* @brief Constructs a Composition with the given symbols.
|
||||
* @param symbols The symbols to initialize the composition with.
|
||||
* @example
|
||||
* @code
|
||||
* std::vector<std::string> symbols = {"H", "O"};
|
||||
* Composition comp(symbols);
|
||||
* @endcode
|
||||
*/
|
||||
Composition(const std::vector<std::string>& symbols);
|
||||
|
||||
/**
|
||||
* @brief Constructs a Composition with the given symbols and mass fractions.
|
||||
* @param symbols The symbols to initialize the composition with.
|
||||
* @param mass_fractions The mass fractions corresponding to the symbols.
|
||||
* @example
|
||||
* @code
|
||||
* std::vector<std::string> symbols = {"H", "O"};
|
||||
* std::vector<double> mass_fractions = {0.1, 0.9};
|
||||
* Composition comp(symbols, mass_fractions);
|
||||
* @endcode
|
||||
*/
|
||||
Composition(const std::vector<std::string>& symbols, const std::vector<double>& mass_fractions);
|
||||
|
||||
/**
|
||||
* @brief Registers a new symbol.
|
||||
* @param symbol The symbol to register.
|
||||
* @example
|
||||
* @code
|
||||
* Composition comp;
|
||||
* comp.registerSymbol("H");
|
||||
* @endcode
|
||||
*/
|
||||
void registerSymbol(const std::string& symbol);
|
||||
|
||||
/**
|
||||
* @brief Gets the registered symbols.
|
||||
* @return A set of registered symbols.
|
||||
*/
|
||||
std::set<std::string> getRegisteredSymbols() const;
|
||||
|
||||
/**
|
||||
* @brief Sets the composition for a given symbol.
|
||||
* @param symbol The symbol to set the composition for.
|
||||
* @param mass_fraction The mass fraction to set.
|
||||
* @return The mass fraction that was set.
|
||||
* @example
|
||||
* @code
|
||||
* Composition comp;
|
||||
* comp.setComposition("H", 0.1);
|
||||
* @endcode
|
||||
*/
|
||||
double setComposition(const std::string& symbol, const double& mass_fraction);
|
||||
|
||||
/**
|
||||
* @brief Sets the composition for multiple symbols.
|
||||
* @param symbols The symbols to set the composition for.
|
||||
* @param mass_fractions The mass fractions corresponding to the symbols.
|
||||
* @return A vector of mass fractions that were set.
|
||||
* @example
|
||||
* @code
|
||||
* std::vector<std::string> symbols = {"H", "O"};
|
||||
* std::vector<double> mass_fractions = {0.1, 0.9};
|
||||
* Composition comp;
|
||||
* comp.setComposition(symbols, mass_fractions);
|
||||
* @endcode
|
||||
*/
|
||||
std::vector<double> setComposition(const std::vector<std::string>& symbols, const std::vector<double>& mass_fractions);
|
||||
|
||||
/**
|
||||
* @brief Gets the compositions.
|
||||
* @return An unordered map of compositions.
|
||||
*/
|
||||
std::unordered_map<std::string, CompositionEntry> getCompositions() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the composition for a given symbol.
|
||||
* @param symbol The symbol to get the composition for.
|
||||
* @return The CompositionEntry for the given symbol.
|
||||
*/
|
||||
CompositionEntry getComposition(const std::string& symbol) const;
|
||||
|
||||
/**
|
||||
* @brief Overloaded output stream operator for Composition.
|
||||
* @param os The output stream.
|
||||
* @param composition The Composition to output.
|
||||
* @return The output stream.
|
||||
*/
|
||||
friend std::ostream& operator<<(std::ostream& os, const Composition& composition) {
|
||||
os << "Composition: \n";
|
||||
for (const auto& [symbol, entry] : composition.m_compositions) {
|
||||
os << entry << "\n";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
#endif // COMPOSITION_H
|
||||
Reference in New Issue
Block a user