2025-04-21 08:56:45 -04:00
/* ***********************************************************************
//
// Copyright (C) 2025 -- The 4D-STAR Collaboration
// File Author: Emily Boudreaux
2025-10-06 14:29:33 -04:00
// Last Modified: October 6, 2025
2025-04-21 08:56:45 -04:00
//
// 4DSSE is free software; you can use it and/or modify
// it under the terms and restrictions the GNU General Library Public
// License version 3 (GPLv3) as published by the Free Software Foundation.
//
// 4DSSE is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public License
// along with this software; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// *********************************************************************** */
2025-03-24 12:58:30 -04:00
# include "quill/LogMacros.h"
# include <stdexcept>
2025-03-25 12:49:37 -04:00
# include <unordered_map>
# include <vector>
2025-06-16 15:00:33 -04:00
# include <ranges>
2025-09-16 11:23:01 -04:00
# include <algorithm>
2025-11-07 15:49:25 -05:00
# include <set>
# include <string>
2025-09-16 11:23:01 -04:00
2025-03-25 12:49:37 -04:00
# include <utility>
2025-03-24 12:58:30 -04:00
2025-11-07 15:49:25 -05:00
# include "fourdst/atomic/atomicSpecies.h"
# include "fourdst/atomic/species.h"
2025-06-22 04:56:04 -04:00
# include "fourdst/composition/composition.h"
2025-10-06 14:29:33 -04:00
2025-07-21 07:48:00 -04:00
# include "fourdst/composition/exceptions/exceptions_composition.h"
2025-03-24 12:58:30 -04:00
2025-09-16 11:23:01 -04:00
namespace {
template < typename A , typename B >
2025-10-06 14:29:33 -04:00
std : : vector < A > sortVectorBy (
std : : vector < A > toSort ,
const std : : vector < B > & by
) {
2025-09-16 11:23:01 -04:00
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 ;
}
2025-10-06 14:29:33 -04:00
2025-11-07 15:49:25 -05:00
std : : optional < fourdst : : atomic : : Species > getSpecies ( const std : : string & symbol ) {
2025-06-25 08:26:50 -04:00
if ( ! fourdst : : atomic : : species . contains ( symbol ) ) {
2025-11-07 15:49:25 -05:00
return std : : nullopt ;
2025-06-16 15:00:33 -04:00
}
2025-11-07 15:49:25 -05:00
return fourdst : : atomic : : species . at ( symbol ) ;
2025-03-24 12:58:30 -04:00
}
2025-11-07 15:49:25 -05:00
void throw_unknown_symbol ( quill : : Logger * logger , const std : : string & symbol ) {
LOG_ERROR ( logger , " Symbol {} is not a valid species symbol (not in the species database) " , symbol ) ;
throw fourdst : : composition : : exceptions : : UnknownSymbolError ( " Symbol " + symbol + " is not a valid species symbol (not in the species database) " ) ;
2025-03-25 12:49:37 -04:00
}
2025-11-07 15:49:25 -05:00
void throw_unregistered_symbol ( quill : : Logger * logger , const std : : string & symbol ) {
LOG_ERROR ( logger , " Symbol {} is not registered in the composition. " , symbol ) ;
throw fourdst : : composition : : exceptions : : UnregisteredSymbolError ( " Symbol " + symbol + " is not registered in the composition. " ) ;
2025-03-24 12:58:30 -04:00
}
2025-11-07 15:49:25 -05:00
}
2025-03-25 12:49:37 -04:00
2025-11-07 15:49:25 -05:00
namespace fourdst : : composition {
Composition : : Composition (
const std : : vector < std : : string > & symbols
) {
for ( const auto & symbol : symbols ) {
registerSymbol ( symbol ) ;
2025-06-16 15:00:33 -04:00
}
2025-03-25 12:49:37 -04:00
}
2025-11-07 15:49:25 -05:00
Composition : : Composition (
const std : : set < std : : string > & symbols
2025-10-06 14:29:33 -04:00
) {
2025-11-07 15:49:25 -05:00
for ( const auto & symbol : symbols ) {
registerSymbol ( symbol ) ;
2025-10-06 14:29:33 -04:00
}
2025-03-25 12:49:37 -04:00
}
2025-11-07 15:49:25 -05:00
Composition : : Composition (
const std : : vector < atomic : : Species > & species
2025-10-06 14:29:33 -04:00
) {
2025-11-07 15:49:25 -05:00
for ( const auto & s : species ) {
registerSpecies ( s ) ;
2025-06-16 15:00:33 -04:00
}
2025-03-26 08:07:11 -04:00
}
2025-11-07 15:49:25 -05:00
Composition : : Composition (
const std : : set < atomic : : Species > & species
2025-10-06 14:29:33 -04:00
) {
2025-11-07 15:49:25 -05:00
for ( const auto & s : species ) {
registerSpecies ( s ) ;
2025-06-16 15:00:33 -04:00
}
2025-03-24 12:58:30 -04:00
}
2025-11-07 15:49:25 -05:00
Composition : : Composition (
const std : : vector < std : : string > & symbols ,
const std : : vector < double > & molarAbundances
2025-10-06 14:29:33 -04:00
) {
2025-11-07 15:49:25 -05:00
if ( symbols . size ( ) ! = molarAbundances . size ( ) ) {
LOG_CRITICAL ( getLogger ( ) , " The number of symbols and molarAbundances must be equal (got {} symbols and {} molarAbundances). " , symbols . size ( ) , molarAbundances . size ( ) ) ;
throw exceptions : : InvalidCompositionError ( " The number of symbols and fractions must be equal. Got " + std : : to_string ( symbols . size ( ) ) + " symbols and " + std : : to_string ( molarAbundances . size ( ) ) + " fractions. " ) ;
2025-06-16 15:00:33 -04:00
}
2025-03-24 12:58:30 -04:00
2025-11-07 15:49:25 -05:00
for ( const auto & [ symbol , y ] : std : : views : : zip ( symbols , molarAbundances ) ) {
2025-06-16 15:00:33 -04:00
registerSymbol ( symbol ) ;
2025-11-07 15:49:25 -05:00
setMolarAbundance ( symbol , y ) ;
2025-03-25 12:49:37 -04:00
}
2025-03-24 12:58:30 -04:00
}
2025-10-06 14:29:33 -04:00
Composition : : Composition (
2025-11-07 15:49:25 -05:00
const std : : vector < atomic : : Species > & species ,
const std : : vector < double > & molarAbundances
2025-10-06 14:29:33 -04:00
) {
2025-11-07 15:49:25 -05:00
if ( species . size ( ) ! = molarAbundances . size ( ) ) {
LOG_CRITICAL ( getLogger ( ) , " The number of species and molarAbundances must be equal (got {} species and {} molarAbundances). " , species . size ( ) , molarAbundances . size ( ) ) ;
throw exceptions : : InvalidCompositionError ( " The number of species and fractions must be equal. Got " + std : : to_string ( species . size ( ) ) + " species and " + std : : to_string ( molarAbundances . size ( ) ) + " fractions. " ) ;
2025-06-16 15:00:33 -04:00
}
2025-03-24 12:58:30 -04:00
2025-11-07 15:49:25 -05:00
for ( const auto & [ s , y ] : std : : views : : zip ( species , molarAbundances ) ) {
registerSpecies ( s ) ;
setMolarAbundance ( s , y ) ;
2025-03-25 12:49:37 -04:00
}
2025-11-07 15:49:25 -05:00
}
2025-03-25 12:49:37 -04:00
2025-11-07 15:49:25 -05:00
Composition : : Composition (
const std : : set < std : : string > & symbols ,
const std : : vector < double > & molarAbundances
) {
if ( symbols . size ( ) ! = molarAbundances . size ( ) ) {
LOG_CRITICAL ( getLogger ( ) , " The number of symbols and molarAbundances must be equal (got {} symbols and {} molarAbundances). " , symbols . size ( ) , molarAbundances . size ( ) ) ;
throw exceptions : : InvalidCompositionError ( " The number of symbols and fractions must be equal. Got " + std : : to_string ( symbols . size ( ) ) + " symbols and " + std : : to_string ( molarAbundances . size ( ) ) + " fractions. " ) ;
2025-06-16 15:00:33 -04:00
}
2025-11-07 15:49:25 -05:00
for ( const auto & [ symbol , y ] : std : : views : : zip ( sortVectorBy < std : : string > ( std : : vector < std : : string > ( symbols . begin ( ) , symbols . end ( ) ) , molarAbundances ) , molarAbundances ) ) {
registerSymbol ( symbol ) ;
setMolarAbundance ( symbol , y ) ;
2025-10-12 10:12:49 -04:00
}
2025-06-16 15:00:33 -04:00
}
2025-11-07 15:49:25 -05:00
Composition : : Composition (
const Composition & composition
) {
m_registeredSpecies = composition . m_registeredSpecies ;
m_molarAbundances = composition . m_molarAbundances ;
2025-06-16 15:00:33 -04:00
}
2025-11-12 15:21:33 -05:00
Composition : : Composition ( const CompositionAbstract & composition ) {
for ( const auto & species : composition . getRegisteredSpecies ( ) ) {
registerSpecies ( species ) ;
setMolarAbundance ( species , composition . getMolarAbundance ( species ) ) ;
}
}
2025-11-07 15:49:25 -05:00
Composition & Composition : : operator = (
const Composition & other
) {
2025-06-16 15:00:33 -04:00
if ( this ! = & other ) {
2025-11-07 15:49:25 -05:00
m_registeredSpecies = other . m_registeredSpecies ;
m_molarAbundances = other . m_molarAbundances ;
2025-06-16 15:00:33 -04:00
}
return * this ;
2025-03-25 12:49:37 -04:00
}
2025-10-06 14:29:33 -04:00
void Composition : : registerSymbol (
2025-11-07 15:49:25 -05:00
const std : : string & symbol
2025-10-06 14:29:33 -04:00
) {
2025-11-07 15:49:25 -05:00
const auto result = getSpecies ( symbol ) ;
if ( ! result ) {
throw_unknown_symbol ( getLogger ( ) , symbol ) ;
2025-06-16 15:00:33 -04:00
}
2025-03-25 12:49:37 -04:00
2025-11-07 15:49:25 -05:00
registerSpecies ( result . value ( ) ) ;
2025-03-25 12:49:37 -04:00
}
2025-10-06 14:29:33 -04:00
void Composition : : registerSymbol (
2025-11-07 15:49:25 -05:00
const std : : vector < std : : string > & symbols
2025-10-06 14:29:33 -04:00
) {
2025-06-16 15:00:33 -04:00
for ( const auto & symbol : symbols ) {
2025-11-07 15:49:25 -05:00
registerSymbol ( symbol ) ;
2025-06-16 15:00:33 -04:00
}
2025-03-25 12:49:37 -04:00
}
2025-06-16 15:00:33 -04:00
2025-10-06 14:29:33 -04:00
void Composition : : registerSpecies (
2025-11-07 15:49:25 -05:00
const atomic : : Species & species
2025-11-08 06:42:41 -05:00
) noexcept {
2025-11-07 15:49:25 -05:00
m_registeredSpecies . insert ( species ) ;
if ( ! m_molarAbundances . contains ( species ) ) {
m_molarAbundances . emplace ( species , 0.0 ) ;
}
2025-07-14 11:29:04 -04:00
}
2025-10-06 14:29:33 -04:00
void Composition : : registerSpecies (
2025-11-07 15:49:25 -05:00
const std : : vector < atomic : : Species > & species
2025-11-08 06:42:41 -05:00
) noexcept {
2025-07-14 11:29:04 -04:00
for ( const auto & s : species ) {
2025-11-07 15:49:25 -05:00
registerSpecies ( s ) ;
2025-07-14 11:29:04 -04:00
}
}
2025-11-08 06:42:41 -05:00
std : : set < std : : string > Composition : : getRegisteredSymbols ( ) const noexcept {
2025-11-07 15:49:25 -05:00
std : : set < std : : string > symbols ;
for ( const auto & species : m_registeredSpecies ) {
symbols . insert ( std : : string ( species . name ( ) ) ) ;
2025-10-06 14:29:33 -04:00
}
2025-11-07 15:49:25 -05:00
return symbols ;
2025-03-25 12:49:37 -04:00
}
2025-11-08 06:42:41 -05:00
const std : : set < atomic : : Species > & Composition : : getRegisteredSpecies ( ) const noexcept {
2025-11-07 15:49:25 -05:00
return m_registeredSpecies ;
2025-03-25 12:49:37 -04:00
}
2025-03-24 12:58:30 -04:00
2025-03-26 08:07:11 -04:00
2025-06-16 15:00:33 -04:00
double Composition : : getMassFraction ( const std : : string & symbol ) const {
2025-11-07 15:49:25 -05:00
const auto species = getSpecies ( symbol ) ;
if ( ! species ) {
throw_unknown_symbol ( getLogger ( ) , symbol ) ;
2025-06-16 15:00:33 -04:00
}
2025-11-07 15:49:25 -05:00
return getMassFraction ( species . value ( ) ) ;
2025-03-25 12:49:37 -04:00
}
2025-10-06 14:29:33 -04:00
double Composition : : getMassFraction (
const atomic : : Species & species
) const {
2025-11-08 06:42:41 -05:00
if ( ! m_molarAbundances . contains ( species ) ) {
throw_unregistered_symbol ( getLogger ( ) , std : : string ( species . name ( ) ) ) ;
}
2025-11-07 15:49:25 -05:00
std : : map < atomic : : Species , double > raw_mass ;
double totalMass = 0 ;
for ( const auto & [ sp , y ] : m_molarAbundances ) {
const double contrib = y * sp . mass ( ) ;
totalMass + = contrib ;
raw_mass . emplace ( sp , contrib ) ;
}
return raw_mass . at ( species ) / totalMass ;
2025-07-14 11:29:04 -04:00
}
2025-11-08 06:42:41 -05:00
std : : unordered_map < atomic : : Species , double > Composition : : getMassFraction ( ) const noexcept {
2025-11-07 15:49:25 -05:00
std : : unordered_map < atomic : : Species , double > mass_fractions ;
for ( const auto & species : m_molarAbundances | std : : views : : keys ) {
mass_fractions . emplace ( species , getMassFraction ( species ) ) ;
2025-06-16 15:00:33 -04:00
}
return mass_fractions ;
2025-03-25 12:49:37 -04:00
}
2025-10-06 14:29:33 -04:00
double Composition : : getNumberFraction (
const std : : string & symbol
) const {
2025-11-08 06:42:41 -05:00
const auto species = getSpecies ( symbol ) ;
2025-11-07 15:49:25 -05:00
if ( ! species ) {
throw_unknown_symbol ( getLogger ( ) , symbol ) ;
2025-06-16 15:00:33 -04:00
}
2025-11-07 15:49:25 -05:00
return getNumberFraction ( species . value ( ) ) ;
2025-03-25 12:49:37 -04:00
}
2025-10-06 14:29:33 -04:00
double Composition : : getNumberFraction (
const atomic : : Species & species
) const {
2025-11-08 06:42:41 -05:00
if ( ! m_molarAbundances . contains ( species ) ) {
throw_unregistered_symbol ( getLogger ( ) , std : : string ( species . name ( ) ) ) ;
}
2025-11-07 15:49:25 -05:00
double total_moles_per_gram = 0.0 ;
for ( const auto & y : m_molarAbundances | std : : views : : values ) {
total_moles_per_gram + = y ;
}
return m_molarAbundances . at ( species ) / total_moles_per_gram ;
2025-07-14 11:29:04 -04:00
}
2025-11-08 06:42:41 -05:00
std : : unordered_map < atomic : : Species , double > Composition : : getNumberFraction ( ) const noexcept {
2025-11-07 15:49:25 -05:00
std : : unordered_map < atomic : : Species , double > number_fractions ;
for ( const auto & species : m_molarAbundances | std : : views : : keys ) {
number_fractions . emplace ( species , getNumberFraction ( species ) ) ;
2025-06-16 15:00:33 -04:00
}
return number_fractions ;
2025-03-25 12:49:37 -04:00
}
2025-10-06 14:29:33 -04:00
double Composition : : getMolarAbundance (
const std : : string & symbol
) const {
2025-11-08 06:42:41 -05:00
const auto species = getSpecies ( symbol ) ;
2025-11-07 15:49:25 -05:00
if ( ! species ) {
throw_unknown_symbol ( getLogger ( ) , symbol ) ;
2025-06-25 08:26:50 -04:00
}
2025-11-07 15:49:25 -05:00
return getMolarAbundance ( species . value ( ) ) ;
2025-06-25 08:26:50 -04:00
}
2025-10-06 14:29:33 -04:00
double Composition : : getMolarAbundance (
const atomic : : Species & species
) const {
2025-11-07 15:49:25 -05:00
if ( ! m_molarAbundances . contains ( species ) ) {
2025-11-08 06:42:41 -05:00
throw_unregistered_symbol ( getLogger ( ) , std : : string ( species . name ( ) ) ) ;
2025-06-16 15:00:33 -04:00
}
2025-11-07 15:49:25 -05:00
return m_molarAbundances . at ( species ) ;
2025-03-25 12:49:37 -04:00
}
2025-11-08 06:42:41 -05:00
double Composition : : getMeanParticleMass ( ) const noexcept {
2025-11-07 15:49:25 -05:00
std : : vector < double > X = getMassFractionVector ( ) ;
double sum = 0.0 ;
for ( const auto & [ species , x ] : std : : views : : zip ( m_registeredSpecies , X ) ) {
sum + = x / species . mass ( ) ;
2025-06-16 15:00:33 -04:00
}
2025-03-25 12:49:37 -04:00
2025-11-07 15:49:25 -05:00
return 1.0 / sum ;
2025-03-25 12:49:37 -04:00
}
2025-06-16 15:00:33 -04:00
2025-11-08 06:42:41 -05:00
double Composition : : getElectronAbundance ( ) const noexcept {
2025-10-06 14:29:33 -04:00
double Ye = 0.0 ;
2025-11-07 15:49:25 -05:00
for ( const auto & [ species , y ] : m_molarAbundances ) {
Ye + = species . z ( ) * y ;
2025-10-06 14:29:33 -04:00
}
return Ye ;
}
2025-06-16 15:00:33 -04:00
2025-03-26 08:07:11 -04:00
2025-10-06 14:29:33 -04:00
CanonicalComposition Composition : : getCanonicalComposition (
) const {
2025-11-07 15:49:25 -05:00
using namespace fourdst : : atomic ;
2025-10-06 14:29:33 -04:00
if ( m_cache . canonicalComp . has_value ( ) ) {
return m_cache . canonicalComp . value ( ) ; // Short circuit if we have cached the canonical composition
}
2025-06-17 08:12:41 -04:00
CanonicalComposition canonicalComposition ;
2025-11-07 15:49:25 -05:00
const std : : set < Species > canonicalH = { H_1 , H_2 , H_3 , H_4 , H_5 , H_6 , H_7 } ;
const std : : set < Species > canonicalHe = { He_3 , He_4 , He_5 , He_6 , He_7 , He_8 , He_9 , He_10 } ;
2025-06-17 08:12:41 -04:00
for ( const auto & symbol : canonicalH ) {
2025-11-07 15:49:25 -05:00
if ( contains ( symbol ) ) {
2025-06-17 08:12:41 -04:00
canonicalComposition . X + = getMassFraction ( symbol ) ;
}
}
for ( const auto & symbol : canonicalHe ) {
2025-11-07 15:49:25 -05:00
if ( contains ( symbol ) ) {
2025-06-17 08:12:41 -04:00
canonicalComposition . Y + = getMassFraction ( symbol ) ;
}
}
2025-11-07 15:49:25 -05:00
for ( const auto & species : m_molarAbundances | std : : views : : keys ) {
const bool isHIsotope = canonicalH . contains ( species ) ;
const bool isHeIsotope = canonicalHe . contains ( species ) ;
2025-06-17 08:12:41 -04:00
2025-11-07 15:49:25 -05:00
if ( isHIsotope | | isHeIsotope ) {
2025-06-17 08:12:41 -04:00
continue ; // Skip canonical H and He symbols
}
2025-11-07 15:49:25 -05:00
canonicalComposition . Z + = getMassFraction ( species ) ;
2025-06-17 08:12:41 -04:00
}
2025-10-06 14:29:33 -04:00
// ReSharper disable once CppTooWideScopeInitStatement
2025-06-17 08:12:41 -04:00
const double Z = 1.0 - ( canonicalComposition . X + canonicalComposition . Y ) ;
2025-11-08 06:42:41 -05:00
if ( std : : abs ( Z - canonicalComposition . Z ) > 1e-16 ) {
LOG_ERROR ( getLogger ( ) , " Validation composition Z (X-Y = {}) is different than canonical composition Z ({}) (∑a_i where a_i != H/He). " , Z , canonicalComposition . Z ) ;
throw exceptions : : InvalidCompositionError ( " Validation composition Z (X-Y = " + std : : to_string ( Z ) + " ) is different than canonical composition Z ( " + std : : to_string ( canonicalComposition . Z ) + " ) (∑a_i where a_i != H/He). " ) ;
2025-06-17 08:12:41 -04:00
}
2025-10-06 14:29:33 -04:00
m_cache . canonicalComp = canonicalComposition ;
2025-06-17 08:12:41 -04:00
return canonicalComposition ;
}
2025-11-08 06:42:41 -05:00
std : : vector < double > Composition : : getMassFractionVector ( ) const noexcept {
2025-10-06 14:29:33 -04:00
if ( m_cache . massFractions . has_value ( ) ) {
return m_cache . massFractions . value ( ) ; // Short circuit if we have cached the mass fractions
}
2025-09-16 11:23:01 -04:00
std : : vector < double > massFractionVector ;
std : : vector < double > speciesMass ;
2025-11-07 15:49:25 -05:00
massFractionVector . reserve ( m_molarAbundances . size ( ) ) ;
speciesMass . reserve ( m_molarAbundances . size ( ) ) ;
2025-09-16 11:23:01 -04:00
2025-11-07 15:49:25 -05:00
for ( const auto & species : m_molarAbundances | std : : views : : keys ) {
massFractionVector . push_back ( getMassFraction ( species ) ) ;
speciesMass . push_back ( species . mass ( ) ) ;
2025-09-16 11:23:01 -04:00
}
2025-10-06 14:29:33 -04:00
std : : vector < double > massFractions = sortVectorBy ( massFractionVector , speciesMass ) ;
m_cache . massFractions = massFractions ; // Cache the result
return massFractions ;
2025-09-16 11:23:01 -04:00
}
2025-11-08 06:42:41 -05:00
std : : vector < double > Composition : : getNumberFractionVector ( ) const noexcept {
2025-10-06 14:29:33 -04:00
if ( m_cache . numberFractions . has_value ( ) ) {
return m_cache . numberFractions . value ( ) ; // Short circuit if we have cached the number fractions
}
2025-09-16 11:23:01 -04:00
std : : vector < double > numberFractionVector ;
std : : vector < double > speciesMass ;
2025-11-07 15:49:25 -05:00
numberFractionVector . reserve ( m_molarAbundances . size ( ) ) ;
speciesMass . reserve ( m_molarAbundances . size ( ) ) ;
2025-09-16 11:23:01 -04:00
2025-11-07 15:49:25 -05:00
for ( const auto & species : m_molarAbundances | std : : views : : keys ) {
numberFractionVector . push_back ( getNumberFraction ( species ) ) ;
speciesMass . push_back ( species . mass ( ) ) ;
2025-09-16 11:23:01 -04:00
}
2025-10-06 14:29:33 -04:00
std : : vector < double > numberFractions = sortVectorBy ( numberFractionVector , speciesMass ) ;
m_cache . numberFractions = numberFractions ; // Cache the result
return numberFractions ;
2025-09-16 11:23:01 -04:00
}
2025-11-08 06:42:41 -05:00
std : : vector < double > Composition : : getMolarAbundanceVector ( ) const noexcept {
2025-10-06 14:29:33 -04:00
if ( m_cache . molarAbundances . has_value ( ) ) {
return m_cache . molarAbundances . value ( ) ; // Short circuit if we have cached the molar abundances
}
2025-09-16 11:23:01 -04:00
std : : vector < double > molarAbundanceVector ;
std : : vector < double > speciesMass ;
2025-11-07 15:49:25 -05:00
molarAbundanceVector . reserve ( m_molarAbundances . size ( ) ) ;
speciesMass . reserve ( m_molarAbundances . size ( ) ) ;
2025-09-16 11:23:01 -04:00
2025-11-07 15:49:25 -05:00
for ( const auto & [ species , y ] : m_molarAbundances ) {
molarAbundanceVector . push_back ( y ) ;
speciesMass . push_back ( species . mass ( ) ) ;
2025-09-16 11:23:01 -04:00
}
2025-10-06 14:29:33 -04:00
std : : vector < double > molarAbundances = sortVectorBy ( molarAbundanceVector , speciesMass ) ;
m_cache . molarAbundances = molarAbundances ; // Cache the result
return molarAbundances ;
2025-09-16 11:23:01 -04:00
}
2025-10-06 14:29:33 -04:00
size_t Composition : : getSpeciesIndex (
const std : : string & symbol
) const {
2025-11-07 15:49:25 -05:00
const auto species = getSpecies ( symbol ) ;
if ( ! species ) {
throw_unknown_symbol ( getLogger ( ) , symbol ) ;
2025-09-16 11:23:01 -04:00
}
2025-11-07 15:49:25 -05:00
return getSpeciesIndex ( species . value ( ) ) ;
2025-09-16 11:23:01 -04:00
}
2025-10-06 14:29:33 -04:00
size_t Composition : : getSpeciesIndex (
const atomic : : Species & species
) const {
2025-11-07 15:49:25 -05:00
if ( ! m_registeredSpecies . contains ( species ) ) {
2025-10-22 10:32:50 -04:00
LOG_ERROR ( getLogger ( ) , " Species {} is not in the composition. " , species . name ( ) ) ;
2025-09-16 11:23:01 -04:00
throw exceptions : : UnregisteredSymbolError ( " Species " + std : : string ( species . name ( ) ) + " is not in the composition. " ) ;
}
2025-10-06 14:29:33 -04:00
if ( m_cache . sortedSpecies . has_value ( ) ) {
return std : : distance (
m_cache . sortedSpecies - > begin ( ) ,
std : : ranges : : find (
m_cache . sortedSpecies . value ( ) . begin ( ) ,
m_cache . sortedSpecies . value ( ) . end ( ) ,
species
)
) ;
}
2025-09-16 11:23:01 -04:00
std : : vector < atomic : : Species > speciesVector ;
std : : vector < double > speciesMass ;
2025-11-07 15:49:25 -05:00
speciesVector . reserve ( m_molarAbundances . size ( ) ) ;
speciesMass . reserve ( m_molarAbundances . size ( ) ) ;
2025-09-16 11:23:01 -04:00
2025-11-07 15:49:25 -05:00
for ( const auto & s : m_registeredSpecies ) {
speciesVector . emplace_back ( s ) ;
speciesMass . push_back ( s . mass ( ) ) ;
2025-09-16 11:23:01 -04:00
}
std : : vector < atomic : : Species > sortedSpecies = sortVectorBy ( speciesVector , speciesMass ) ;
2025-10-06 14:29:33 -04:00
m_cache . sortedSpecies = sortedSpecies ;
2025-09-16 11:23:01 -04:00
return std : : distance ( sortedSpecies . begin ( ) , std : : ranges : : find ( sortedSpecies , species ) ) ;
}
2025-10-06 14:29:33 -04:00
atomic : : Species Composition : : getSpeciesAtIndex (
2025-11-07 15:49:25 -05:00
const size_t index
2025-10-06 14:29:33 -04:00
) const {
if ( m_cache . sortedSpecies . has_value ( ) ) {
return m_cache . sortedSpecies . value ( ) . at ( index ) ;
2025-09-16 11:23:01 -04:00
}
std : : vector < atomic : : Species > speciesVector ;
std : : vector < double > speciesMass ;
2025-11-07 15:49:25 -05:00
speciesVector . reserve ( m_molarAbundances . size ( ) ) ;
speciesMass . reserve ( m_molarAbundances . size ( ) ) ;
2025-09-16 11:23:01 -04:00
2025-11-07 15:49:25 -05:00
for ( const auto & species : m_registeredSpecies ) {
speciesVector . emplace_back ( species ) ;
speciesMass . push_back ( species . mass ( ) ) ;
2025-09-16 11:23:01 -04:00
}
std : : vector < atomic : : Species > sortedSymbols = sortVectorBy ( speciesVector , speciesMass ) ;
2025-11-08 06:42:41 -05:00
if ( index > = sortedSymbols . size ( ) ) {
LOG_ERROR ( getLogger ( ) , " Index {} is out of range for composition of size {}. " , index , sortedSymbols . size ( ) ) ;
throw std : : out_of_range ( " Index " + std : : to_string ( index ) + " is out of range for composition of size " + std : : to_string ( sortedSymbols . size ( ) ) + " . " ) ;
}
2025-09-16 11:23:01 -04:00
return sortedSymbols . at ( index ) ;
}
2025-11-09 09:51:29 -05:00
std : : unique_ptr < CompositionAbstract > Composition : : clone ( ) const {
return std : : make_unique < Composition > ( * this ) ;
}
2025-11-07 15:49:25 -05:00
bool Composition : : contains (
const atomic : : Species & species
2025-11-08 06:42:41 -05:00
) const noexcept {
2025-11-07 15:49:25 -05:00
return m_registeredSpecies . contains ( species ) ;
2025-10-12 10:12:49 -04:00
}
2025-10-06 14:29:33 -04:00
bool Composition : : contains (
2025-11-07 15:49:25 -05:00
const std : : string & symbol
2025-10-06 14:29:33 -04:00
) const {
2025-11-07 15:49:25 -05:00
const auto species = getSpecies ( symbol ) ;
if ( ! species ) {
throw_unknown_symbol ( getLogger ( ) , symbol ) ;
2025-06-18 15:25:09 -04:00
}
2025-11-07 15:49:25 -05:00
return contains ( species . value ( ) ) ;
}
2025-11-08 06:42:41 -05:00
size_t Composition : : size ( ) const noexcept {
2025-11-07 15:49:25 -05:00
return m_registeredSpecies . size ( ) ;
}
void Composition : : setMolarAbundance (
const std : : string & symbol ,
const double & molar_abundance
) {
const auto species = getSpecies ( symbol ) ;
if ( ! species ) {
throw_unknown_symbol ( getLogger ( ) , symbol ) ;
}
2025-11-08 06:42:41 -05:00
setMolarAbundance ( species . value ( ) , molar_abundance ) ;
2025-06-18 15:25:09 -04:00
}
2025-11-07 15:49:25 -05:00
void Composition : : setMolarAbundance (
const atomic : : Species & species ,
const double & molar_abundance
) {
if ( ! m_registeredSpecies . contains ( species ) ) {
throw_unregistered_symbol ( getLogger ( ) , std : : string ( species . name ( ) ) ) ;
}
2025-11-08 06:42:41 -05:00
if ( molar_abundance < 0.0 ) {
LOG_ERROR ( getLogger ( ) , " Molar abundance must be non-negative for symbol {}. Currently it is {}. " , species . name ( ) , molar_abundance ) ;
throw exceptions : : InvalidCompositionError ( " Molar abundance must be non-negative, got " + std : : to_string ( molar_abundance ) + " for symbol " + std : : string ( species . name ( ) ) + " . " ) ;
}
2025-11-07 15:49:25 -05:00
m_molarAbundances . at ( species ) = molar_abundance ;
}
2025-03-26 08:07:11 -04:00
2025-11-07 15:49:25 -05:00
void Composition : : setMolarAbundance (
const std : : vector < std : : string > & symbols ,
const std : : vector < double > & molar_abundances
) {
for ( const auto & [ symbol , y ] : std : : views : : zip ( symbols , molar_abundances ) ) {
setMolarAbundance ( symbol , y ) ;
}
2025-06-16 15:00:33 -04:00
}
2025-03-26 08:07:11 -04:00
2025-11-07 15:49:25 -05:00
void Composition : : setMolarAbundance (
const std : : vector < atomic : : Species > & species ,
const std : : vector < double > & molar_abundances
2025-10-06 14:29:33 -04:00
) {
2025-11-07 15:49:25 -05:00
for ( const auto & [ s , y ] : std : : views : : zip ( species , molar_abundances ) ) {
setMolarAbundance ( s , y ) ;
}
2025-06-16 15:00:33 -04:00
}
2025-06-11 14:49:11 -04:00
2025-11-07 15:49:25 -05:00
void Composition : : setMolarAbundance (
const std : : set < std : : string > & symbols ,
const std : : vector < double > & molar_abundances
2025-10-06 14:29:33 -04:00
) {
2025-11-07 15:49:25 -05:00
for ( const auto & [ symbol , y ] : std : : views : : zip ( symbols , molar_abundances ) ) {
setMolarAbundance ( symbol , y ) ;
}
}
void Composition : : setMolarAbundance (
const std : : set < atomic : : Species > & species ,
const std : : vector < double > & molar_abundances
) {
for ( const auto & [ s , y ] : std : : views : : zip ( species , molar_abundances ) ) {
setMolarAbundance ( s , y ) ;
}
2025-06-16 15:00:33 -04:00
}
2025-06-11 14:49:11 -04:00
2025-11-07 15:49:25 -05:00
/// OVERLOADS
2025-10-06 14:29:33 -04:00
std : : ostream & operator < < (
std : : ostream & os ,
const Composition & composition
) {
2025-11-07 15:49:25 -05:00
os < < " Composition(Mass Fractions => [ " ;
2025-07-24 09:35:52 -04:00
size_t count = 0 ;
2025-11-07 15:49:25 -05:00
for ( const auto & species : composition . m_registeredSpecies ) {
os < < species < < " : " < < composition . getMassFraction ( species ) ;
if ( count < composition . size ( ) - 1 ) {
2025-06-20 13:51:27 -04:00
os < < " , " ;
}
2025-10-06 14:29:33 -04:00
count + + ;
2025-06-16 15:00:33 -04:00
}
2025-11-07 15:49:25 -05:00
os < < " ]) " ;
2025-06-16 15:00:33 -04:00
return os ;
2025-03-26 08:07:11 -04:00
}
2025-06-11 14:49:11 -04:00
2025-06-21 11:33:27 -04:00
} // namespace fourdst::composition