feat(composition): added uniform tools to get vector representation of mass fraction, number fraction, and molar abundance

this is useful for external tools that need to ensure uniformity in which species are at what index in a vector representation
This commit is contained in:
2025-09-16 11:23:01 -04:00
parent b94d9b456f
commit 539c3b567e
2 changed files with 229 additions and 2 deletions

View File

@@ -734,6 +734,68 @@ namespace fourdst::composition {
*/ */
[[nodiscard]] CanonicalComposition getCanonicalComposition(bool harsh=false) const; [[nodiscard]] CanonicalComposition getCanonicalComposition(bool harsh=false) const;
/**
* @brief Get a uniform vector representation of the mass fraction stored in the composition object sorted such that the lightest species is at index 0 and the heaviest is at the last index.
* @details This is primarily useful for external libraries which need to ensure that vector representation uniformity is maintained
* @return the vector of mass fractions sorted by species mass (lightest to heaviest).
* @pre The composition must be finalized.
* @throws exceptions::CompositionNotFinalizedError if the composition is not finalized.
*/
[[nodiscard]] std::vector<double> getMassFractionVector() const;
/**
* @brief Get a uniform vector representation of the number fractions stored in the composition object sorted such that the lightest species is at index 0 and the heaviest is at the last index.
* @details This is primarily useful for external libraries which need to ensure that vector representation uniformity is maintained
* @return the vector of number fractions sorted by species mass (lightest to heaviest).
* @pre The composition must be finalized.
* @throws exceptions::CompositionNotFinalizedError if the composition is not finalized.
*/
[[nodiscard]] std::vector<double> getNumberFractionVector() const;
/**
* @brief Get a uniform vector representation of the molar abundances stored in the composition object sorted such that the lightest species is at index 0 and the heaviest is at the last index.
* @details This is primarily useful for external libraries which need to ensure that vector representation uniformity is maintained
* @return the vector of molar abundances sorted by species mass (lightest to heaviest).
* @pre The composition must be finalized.
* @throws exceptions::CompositionNotFinalizedError if the composition is not finalized.
*/
[[nodiscard]] std::vector<double> getMolarAbundanceVector() const;
/**
* @brief get the index in the sorted vector representation for a given symbol
* @details This is primarily useful for external libraries which need to ensure that vector representation uniformity is maintained
* @pre The composition must be finalized.
* @pre symbol must be registered in the composition
* @param symbol the symbol to look up the index for. Note that this is the index species data will be at if you were to call getMolarAbundanceVector(), getMassFractionVector(), or getNumberFractionVector()
* @throws exceptions::CompositionNotFinalizedError if the composition is not finalized.
* @throws exceptions::UnregisteredSymbolError if the symbol is not registered in the composition
* @return The index of the symbol in the sorted vector representation.
*/
[[nodiscard]] size_t getSpeciesIndex(const std::string& symbol) const;
/**
* @brief get the index in the sorted vector representation for a given symbol
* @details This is primarily useful for external libraries which need to ensure that vector representation uniformity is maintained
* @pre The composition must be finalized.
* @pre symbol must be registered in the composition
* @param species the species to look up the index for. Note that this is the index species data will be at if you were to call getMolarAbundanceVector(), getMassFractionVector(), or getNumberFractionVector()
* @throws exceptions::CompositionNotFinalizedError if the composition is not finalized.
* @throws exceptions::UnregisteredSymbolError if the symbol is not registered in the composition
* @return The index of the symbol in the sorted vector representation.
*/
[[nodiscard]] size_t getSpeciesIndex(const atomic::Species& species) const;
/**
* @brief Get the species at a given index in the sorted vector representation.
* @details This is primarily useful for external libraries which need to ensure that vector representation uniformity is maintained
* @pre The composition must be finalized.
* @param index The index in the sorted vector representation for which to return the species. Must be in [0, N-1] where N is the number of registered species.
* @throws exceptions::CompositionNotFinalizedError if the composition is not finalized.
* @throws std::out_of_range if the index is out of range.
* @return The species at the given index in the sorted vector representation.
*/
[[nodiscard]] atomic::Species getSpeciesAtIndex(size_t index) const;
/** /**
* @brief Overloaded output stream operator for Composition. * @brief Overloaded output stream operator for Composition.
* @param os The output stream. * @param os The output stream.
@@ -764,7 +826,7 @@ namespace fourdst::composition {
* @brief Returns a const iterator to the beginning of the composition map. * @brief Returns a const iterator to the beginning of the composition map.
* @return A const iterator to the beginning. * @return A const iterator to the beginning.
*/ */
auto begin() const { [[nodiscard]] auto begin() const {
return m_compositions.cbegin(); return m_compositions.cbegin();
} }
@@ -780,7 +842,7 @@ namespace fourdst::composition {
* @brief Returns a const iterator to the end of the composition map. * @brief Returns a const iterator to the end of the composition map.
* @return A const iterator to the end. * @return A const iterator to the end.
*/ */
auto end() const { [[nodiscard]] auto end() const {
return m_compositions.cend(); return m_compositions.cend();
} }

View File

@@ -25,6 +25,8 @@
#include <vector> #include <vector>
#include <array> #include <array>
#include <ranges> #include <ranges>
#include <algorithm>
#include <utility> #include <utility>
@@ -33,6 +35,29 @@
#include "fourdst/composition/composition.h" #include "fourdst/composition/composition.h"
#include "fourdst/composition/exceptions/exceptions_composition.h" #include "fourdst/composition/exceptions/exceptions_composition.h"
namespace {
template<typename A, typename B>
std::vector<A> sortVectorBy(std::vector<A> toSort, const std::vector<B>& by) {
std::vector<std::size_t> indices(by.size());
for (size_t i = 0; i < indices.size(); i++) {
indices[i] = i;
}
std::ranges::sort(indices, [&](size_t a, size_t b) {
return by[a] < by[b];
});
std::vector<A> sorted;
sorted.reserve(indices.size());
for (const auto idx: indices) {
sorted.push_back(toSort[idx]);
}
return sorted;
}
}
namespace fourdst::composition { namespace fourdst::composition {
CompositionEntry::CompositionEntry() : CompositionEntry::CompositionEntry() :
@@ -734,6 +759,146 @@ namespace fourdst::composition {
return canonicalComposition; return canonicalComposition;
} }
std::vector<double> Composition::getMassFractionVector() const {
if (!m_finalized) {
LOG_ERROR(m_logger, "Composition has not been finalized. Hint: Consider running .finalize().");
throw exceptions::CompositionNotFinalizedError("Composition has not been finalized. Hint: Consider running .finalize().");
}
std::vector<double> massFractionVector;
std::vector<double> speciesMass;
massFractionVector.reserve(m_compositions.size());
speciesMass.reserve(m_compositions.size());
for (const auto &entry: m_compositions | std::views::values) {
massFractionVector.push_back(entry.mass_fraction());
speciesMass.push_back(entry.isotope().mass());
}
return sortVectorBy(massFractionVector, speciesMass);
}
std::vector<double> Composition::getNumberFractionVector() const {
if (!m_finalized) {
LOG_ERROR(m_logger, "Composition has not been finalized. Hint: Consider running .finalize().");
throw exceptions::CompositionNotFinalizedError("Composition has not been finalized. Hint: Consider running .finalize().");
}
std::vector<double> numberFractionVector;
std::vector<double> speciesMass;
numberFractionVector.reserve(m_compositions.size());
speciesMass.reserve(m_compositions.size());
for (const auto &entry: m_compositions | std::views::values) {
numberFractionVector.push_back(entry.number_fraction());
speciesMass.push_back(entry.isotope().mass());
}
return sortVectorBy(numberFractionVector, speciesMass);
}
std::vector<double> Composition::getMolarAbundanceVector() const {
if (!m_finalized) {
LOG_ERROR(m_logger, "Composition has not been finalized. Hint: Consider running .finalize().");
throw exceptions::CompositionNotFinalizedError("Composition has not been finalized. Hint: Consider running .finalize().");
}
std::vector<double> molarAbundanceVector;
std::vector<double> speciesMass;
molarAbundanceVector.reserve(m_compositions.size());
speciesMass.reserve(m_compositions.size());
for (const auto &entry: m_compositions | std::views::values) {
molarAbundanceVector.push_back(getMolarAbundance(entry.isotope()));
speciesMass.push_back(entry.isotope().mass());
}
return sortVectorBy(molarAbundanceVector, speciesMass);
}
size_t Composition::getSpeciesIndex(const std::string &symbol) const {
if (!m_finalized) {
LOG_ERROR(m_logger, "Composition has not been finalized. Hint: Consider running .finalize().");
throw exceptions::CompositionNotFinalizedError("Composition has not been finalized. Hint: Consider running .finalize().");
}
if (!m_compositions.contains(symbol)) {
LOG_ERROR(m_logger, "Symbol {} is not in the composition.", symbol);
throw exceptions::UnregisteredSymbolError("Symbol " + symbol + " is not in the composition.");
}
std::vector<std::string> symbols;
std::vector<double> speciesMass;
symbols.reserve(m_compositions.size());
speciesMass.reserve(m_compositions.size());
for (const auto &entry: m_compositions | std::views::values) {
symbols.emplace_back(entry.isotope().name());
speciesMass.push_back(entry.isotope().mass());
}
std::vector<std::string> sortedSymbols = sortVectorBy(symbols, speciesMass);
return std::distance(sortedSymbols.begin(), std::ranges::find(sortedSymbols, symbol));
}
size_t Composition::getSpeciesIndex(const atomic::Species &species) const {
if (!m_finalized) {
LOG_ERROR(m_logger, "Composition has not been finalized. Hint: Consider running .finalize().");
throw exceptions::CompositionNotFinalizedError("Composition has not been finalized. Hint: Consider running .finalize().");
}
if (!m_compositions.contains(static_cast<std::string>(species.name()))) {
LOG_ERROR(m_logger, "Species {} is not in the composition.", species.name());
throw exceptions::UnregisteredSymbolError("Species " + std::string(species.name()) + " is not in the composition.");
}
std::vector<atomic::Species> speciesVector;
std::vector<double> speciesMass;
speciesVector.reserve(m_compositions.size());
speciesMass.reserve(m_compositions.size());
for (const auto &entry: m_compositions | std::views::values) {
speciesVector.emplace_back(entry.isotope());
speciesMass.push_back(entry.isotope().mass());
}
std::vector<atomic::Species> sortedSpecies = sortVectorBy(speciesVector, speciesMass);
return std::distance(sortedSpecies.begin(), std::ranges::find(sortedSpecies, species));
}
atomic::Species Composition::getSpeciesAtIndex(size_t index) const {
if (!m_finalized) {
LOG_ERROR(m_logger, "Composition has not been finalized. Hint: Consider running .finalize().");
throw exceptions::CompositionNotFinalizedError("Composition has not been finalized. Hint: Consider running .finalize().");
}
if (index >= m_compositions.size()) {
LOG_ERROR(m_logger, "Index {} is out of bounds for composition of size {}.", index, m_compositions.size());
throw std::out_of_range("Index " + std::to_string(index) + " is out of bounds for composition of size " + std::to_string(m_compositions.size()) + ".");
}
if (index < 0) {
LOG_ERROR(m_logger, "Index {} is negative. Cannot get species at negative index.", index);
throw std::out_of_range("Index " + std::to_string(index) + " is negative. Cannot get species at negative index.");
}
std::vector<atomic::Species> speciesVector;
std::vector<double> speciesMass;
speciesVector.reserve(m_compositions.size());
speciesMass.reserve(m_compositions.size());
for (const auto &entry: m_compositions | std::views::values) {
speciesVector.emplace_back(entry.isotope());
speciesMass.push_back(entry.isotope().mass());
}
std::vector<atomic::Species> sortedSymbols = sortVectorBy(speciesVector, speciesMass);
return sortedSymbols.at(index);
}
bool Composition::hasSymbol(const std::string& symbol) const { bool Composition::hasSymbol(const std::string& symbol) const {
return m_compositions.contains(symbol); return m_compositions.contains(symbol);
} }