2025-03-24 10:41:13 -04:00
# include <gtest/gtest.h>
2025-03-25 13:01:22 -04:00
# include <stdexcept>
2025-03-24 10:41:13 -04:00
# include <string>
# include <algorithm>
2025-06-22 04:56:04 -04:00
# include "fourdst/composition/atomicSpecies.h"
# include "fourdst/composition/species.h"
# include "fourdst/composition/composition.h"
2025-07-21 07:48:00 -04:00
# include "fourdst/composition/exceptions/exceptions_composition.h"
2025-06-22 04:56:04 -04:00
# include "fourdst/config/config.h"
2025-03-24 10:41:13 -04:00
2025-03-24 12:59:17 -04:00
std : : string EXAMPLE_FILENAME = std : : string ( getenv ( " MESON_SOURCE_ROOT " ) ) + " /tests/config/example.yaml " ;
2025-03-24 10:41:13 -04:00
/**
2025-07-24 09:35:52 -04:00
* @ brief Test suite for the Composition class and related data structures .
* @ details This suite validates the core functionality of the composition library ,
* including the underlying atomic data , the creation and manipulation of
* compositions , and the correctness of derived physical quantities .
2025-03-24 10:41:13 -04:00
*/
class compositionTest : public : : testing : : Test { } ;
/**
2025-07-24 09:35:52 -04:00
* @ brief Tests the correctness of atomic mass data for select isotopes .
* @ details This test verifies that the static ` fourdst : : atomic : : species ` database is loaded
* and that the ` mass ( ) ` method for ` Species ` objects returns the expected values for a few
* key isotopes ( H - 1 , He - 3 , He - 4 ) .
* @ par What this test proves :
* - The species data header is correctly included and parsed at compile time .
* - The ` species . at ( ) ` map lookup and the ` mass ( ) ` accessor method work as expected for these specific cases .
* @ par What this test does not prove :
* - The correctness of the mass for every isotope in the database . It is a spot check , not an exhaustive validation of the underlying data file .
2025-03-24 10:41:13 -04:00
*/
2025-03-24 12:59:17 -04:00
TEST_F ( compositionTest , isotopeMasses ) {
2025-06-21 11:33:27 -04:00
EXPECT_NO_THROW ( fourdst : : atomic : : species . at ( " H-1 " ) ) ;
EXPECT_DOUBLE_EQ ( fourdst : : atomic : : species . at ( " H-1 " ) . mass ( ) , 1.007825031898 ) ;
EXPECT_DOUBLE_EQ ( fourdst : : atomic : : species . at ( " He-3 " ) . mass ( ) , 3.0160293219700001 ) ;
EXPECT_DOUBLE_EQ ( fourdst : : atomic : : species . at ( " He-4 " ) . mass ( ) , 4.0026032541300003 ) ;
2025-03-24 12:59:17 -04:00
}
2025-07-24 09:35:52 -04:00
/**
* @ brief Tests the correctness of half - life data for select isotopes .
* @ details This test checks the ` halfLife ( ) ` method for isotopes with different stability
* characteristics : a stable isotope ( H - 1 , infinite half - life ) , a radioactive isotope with a
* finite half - life ( F - 18 ) , and an unbound isotope ( B - 20 , zero half - life ) .
* @ par What this test proves :
* - The half - life data from the NUBASE data file is correctly parsed and stored .
* - The ` halfLife ( ) ` accessor correctly handles and returns values for stable ( infinity ) , unstable ( finite ) , and unbound ( zero ) cases .
* @ par What this test does not prove :
* - The correctness of the half - life for all isotopes in the database .
*/
2025-06-28 06:31:41 -04:00
TEST_F ( compositionTest , isotopeHalfLives ) {
EXPECT_DOUBLE_EQ ( fourdst : : atomic : : H_1 . halfLife ( ) , std : : numeric_limits < double > : : infinity ( ) ) ;
EXPECT_DOUBLE_EQ ( fourdst : : atomic : : F_18 . halfLife ( ) , 6584.04 ) ;
EXPECT_DOUBLE_EQ ( fourdst : : atomic : : B_20 . halfLife ( ) , 0.0 ) ;
}
2025-07-24 09:35:52 -04:00
/**
* @ brief Tests the numeric conversion of spin - parity strings .
* @ details This test validates the ` spin ( ) ` method , which relies on the ` convert_jpi_to_double `
* utility . It covers a wide range of cases including half - integer spins ( H - 1 ) , integer spins ( Li - 10 ) ,
* zero spin ( He - 4 ) , and cases where the spin is not known and should result in NaN ( Bh - 270 ) .
* @ par What this test proves :
* - The spin - parity string parsing logic correctly handles common formats ( e . g . , " 1/2+ " , " 5 " , " 0+ " ) .
* - The system correctly identifies and represents unknown or unmeasured spin values as ` NaN ` .
* @ par What this test does not prove :
* - The correct parsing of every esoteric or malformed spin - parity string that might exist in data files . It focuses on the most common and expected formats .
*/
2025-07-02 10:35:54 -04:00
TEST_F ( compositionTest , isotopeSpin ) {
using namespace fourdst : : atomic ;
EXPECT_DOUBLE_EQ ( H_1 . spin ( ) , 0.5 ) ;
EXPECT_DOUBLE_EQ ( He_4 . spin ( ) , 0.0 ) ;
EXPECT_DOUBLE_EQ ( Pm_164 . spin ( ) , 0.0 ) ;
EXPECT_DOUBLE_EQ ( Tb_164 . spin ( ) , 5.0 ) ;
EXPECT_DOUBLE_EQ ( Ta_163 . spin ( ) , 0.5 ) ;
EXPECT_DOUBLE_EQ ( Hf_165 . spin ( ) , 2.5 ) ;
EXPECT_DOUBLE_EQ ( Ta_165 . spin ( ) , 0.5 ) ;
EXPECT_DOUBLE_EQ ( Li_10 . spin ( ) , 1.0 ) ;
EXPECT_DOUBLE_EQ ( He_9 . spin ( ) , 0.5 ) ;
EXPECT_DOUBLE_EQ ( F_18 . spin ( ) , 0.0 ) ;
EXPECT_DOUBLE_EQ ( B_20 . spin ( ) , 1.0 ) ;
EXPECT_TRUE ( std : : isnan ( Bh_270 . spin ( ) ) ) ;
}
2025-07-24 09:35:52 -04:00
/**
* @ brief Tests the default constructor of the Composition class .
* @ details This is a basic sanity check to ensure that a ` Composition ` object can be
* instantiated without any arguments and does not throw an exception .
* @ par What this test proves :
* - The default constructor is accessible and does not fail on basic initialization .
* @ par What this test does not prove :
2025-10-08 15:17:33 -04:00
* - The state of the constructed object or the correctness of its methods .
2025-07-24 09:35:52 -04:00
*/
2025-03-24 10:41:13 -04:00
TEST_F ( compositionTest , constructor ) {
2025-06-21 11:33:27 -04:00
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
EXPECT_NO_THROW ( fourdst : : composition : : Composition comp ) ;
2025-03-24 12:59:17 -04:00
}
2025-07-24 09:35:52 -04:00
/**
* @ brief Tests the registration of valid and invalid symbols .
* @ details This test verifies that ` registerSymbol ` correctly adds valid isotope symbols
* to the composition and throws an ` InvalidSymbolError ` for symbols that do not exist
* in the atomic species database . It also checks that ` getRegisteredSymbols ` reflects the correct state .
* @ par What this test proves :
* - The validation logic within ` registerSymbol ` correctly distinguishes between known and unknown species .
* - The internal set of registered symbols is correctly maintained .
* @ par What this test does not prove :
* - The handling of mode conflicts ( e . g . , trying to register a symbol in number fraction mode when the composition is already in mass fraction mode ) .
*/
2025-03-24 12:59:17 -04:00
TEST_F ( compositionTest , registerSymbol ) {
2025-06-21 11:33:27 -04:00
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
fourdst : : composition : : Composition comp ;
2025-03-24 12:59:17 -04:00
EXPECT_NO_THROW ( comp . registerSymbol ( " H-1 " ) ) ;
EXPECT_NO_THROW ( comp . registerSymbol ( " He-4 " ) ) ;
2025-07-21 07:48:00 -04:00
EXPECT_THROW ( comp . registerSymbol ( " H-19 " ) , fourdst : : composition : : exceptions : : InvalidSymbolError ) ;
EXPECT_THROW ( comp . registerSymbol ( " He-21 " ) , fourdst : : composition : : exceptions : : InvalidSymbolError ) ;
2025-03-24 12:59:17 -04:00
std : : set < std : : string > registeredSymbols = comp . getRegisteredSymbols ( ) ;
2025-10-06 14:29:33 -04:00
EXPECT_TRUE ( registeredSymbols . contains ( " H-1 " ) ) ;
EXPECT_TRUE ( registeredSymbols . contains ( " He-4 " ) ) ;
EXPECT_TRUE ( ! registeredSymbols . contains ( " H-19 " ) ) ;
EXPECT_TRUE ( ! registeredSymbols . contains ( " He-21 " ) ) ;
2025-03-24 10:41:13 -04:00
}
2025-07-24 09:35:52 -04:00
/**
* @ brief Tests the core workflow of setting and getting mass fractions .
* @ details This test covers setting mass fractions for single and multiple symbols ,
* the requirement to ` finalize ( ) ` before getting data , and the behavior when finalization
* fails ( e . g . , due to non - normalized fractions ) .
* @ par What this test proves :
* - ` setMassFraction ` correctly updates the internal state .
* - ` finalize ` correctly validates the composition ( sum of fractions must be ~ 1.0 ) .
* - ` getComposition ` and other getter methods correctly throw ` CompositionNotFinalizedError ` if called before ` finalize ` .
* - An attempt to set a fraction for an unregistered symbol throws ` UnregisteredSymbolError ` .
* @ par What this test does not prove :
* - The correctness of number fraction mode , which is tested separately .
*/
2025-03-24 12:59:17 -04:00
TEST_F ( compositionTest , setGetComposition ) {
2025-06-21 11:33:27 -04:00
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
fourdst : : composition : : Composition comp ;
2025-03-24 12:59:17 -04:00
comp . registerSymbol ( " H-1 " ) ;
comp . registerSymbol ( " He-4 " ) ;
2025-03-25 12:49:37 -04:00
EXPECT_DOUBLE_EQ ( comp . setMassFraction ( " H-1 " , 0.5 ) , 0.0 ) ;
EXPECT_DOUBLE_EQ ( comp . setMassFraction ( " He-4 " , 0.5 ) , 0.0 ) ;
EXPECT_DOUBLE_EQ ( comp . setMassFraction ( " H-1 " , 0.6 ) , 0.5 ) ;
EXPECT_DOUBLE_EQ ( comp . setMassFraction ( " He-4 " , 0.4 ) , 0.5 ) ;
2025-03-24 12:59:17 -04:00
2025-10-12 10:12:49 -04:00
EXPECT_NO_THROW ( static_cast < void > ( comp . finalize ( ) ) ) ;
2025-03-25 12:49:37 -04:00
EXPECT_DOUBLE_EQ ( comp . getMassFraction ( " H-1 " ) , 0.6 ) ;
2025-03-24 12:59:17 -04:00
2025-07-21 07:48:00 -04:00
EXPECT_THROW ( comp . setMassFraction ( " He-3 " , 0.3 ) , fourdst : : composition : : exceptions : : UnregisteredSymbolError ) ;
2025-03-24 12:59:17 -04:00
2025-07-24 09:35:52 -04:00
const std : : vector < std : : string > symbols = { " H-1 " , " He-4 " } ;
EXPECT_NO_THROW ( comp . setMassFraction ( symbols , { 0.5 , 0.5 } ) ) ;
2025-07-21 07:48:00 -04:00
EXPECT_THROW ( auto r = comp . getComposition ( " H-1 " ) , fourdst : : composition : : exceptions : : CompositionNotFinalizedError ) ;
2025-03-24 12:59:17 -04:00
EXPECT_TRUE ( comp . finalize ( ) ) ;
2025-03-25 12:49:37 -04:00
EXPECT_DOUBLE_EQ ( comp . getComposition ( " H-1 " ) . first . mass_fraction ( ) , 0.5 ) ;
2025-03-24 12:59:17 -04:00
2025-07-24 09:35:52 -04:00
EXPECT_NO_THROW ( comp . setMassFraction ( symbols , { 0.6 , 0.6 } ) ) ;
2025-03-24 12:59:17 -04:00
EXPECT_FALSE ( comp . finalize ( ) ) ;
2025-07-21 07:48:00 -04:00
EXPECT_THROW ( auto r = comp . getComposition ( " H-1 " ) , fourdst : : composition : : exceptions : : CompositionNotFinalizedError ) ;
2025-03-25 12:49:37 -04:00
}
2025-07-24 09:35:52 -04:00
/**
* @ brief Tests the workflow of setting and getting number fractions .
* @ details This test mirrors ` setGetComposition ` but for number fraction mode . It verifies
* that symbols can be registered in number fraction mode and that ` setNumberFraction ` and
* ` getNumberFraction ` work as expected .
* @ par What this test proves :
* - The composition can be correctly initialized and operated in number fraction mode .
* - ` setNumberFraction ` and ` getNumberFraction ` function correctly .
* - An attempt to set a fraction for an unregistered symbol throws the correct exception .
* @ par What this test does not prove :
* - The correctness of conversions between mass and number fraction modes .
*/
2025-03-25 12:49:37 -04:00
TEST_F ( compositionTest , setGetNumberFraction ) {
2025-06-21 11:33:27 -04:00
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
fourdst : : composition : : Composition comp ;
2025-03-25 12:49:37 -04:00
comp . registerSymbol ( " H-1 " , false ) ;
comp . registerSymbol ( " He-4 " , false ) ;
EXPECT_DOUBLE_EQ ( comp . setNumberFraction ( " H-1 " , 0.5 ) , 0.0 ) ;
EXPECT_DOUBLE_EQ ( comp . setNumberFraction ( " He-4 " , 0.5 ) , 0.0 ) ;
EXPECT_DOUBLE_EQ ( comp . setNumberFraction ( " H-1 " , 0.6 ) , 0.5 ) ;
EXPECT_DOUBLE_EQ ( comp . setNumberFraction ( " He-4 " , 0.4 ) , 0.5 ) ;
2025-10-12 10:12:49 -04:00
EXPECT_NO_THROW ( static_cast < void > ( comp . finalize ( ) ) ) ;
2025-03-25 12:49:37 -04:00
EXPECT_DOUBLE_EQ ( comp . getNumberFraction ( " H-1 " ) , 0.6 ) ;
2025-07-21 07:48:00 -04:00
EXPECT_THROW ( comp . setNumberFraction ( " He-3 " , 0.3 ) , fourdst : : composition : : exceptions : : UnregisteredSymbolError ) ;
2025-03-25 12:49:37 -04:00
}
2025-07-24 09:35:52 -04:00
/**
* @ brief Tests the creation of a normalized subset of a composition .
* @ details This test creates a composition , finalizes it , and then extracts a subset
* containing only one of the original elements . It verifies that the ` subset ` method with
* the " norm " option creates a new , valid composition where the single element ' s mass
* fraction is normalized to 1.0 .
* @ par What this test proves :
* - The ` subset ` method can extract a subset of symbols .
* - The " norm " method correctly renormalizes the fractions in the new subset to sum to 1.0 .
* @ par What this test does not prove :
* - The behavior of the ` subset ` method with the " none " option .
*/
2025-03-25 12:49:37 -04:00
TEST_F ( compositionTest , subset ) {
2025-06-21 11:33:27 -04:00
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
fourdst : : composition : : Composition comp ;
2025-03-25 12:49:37 -04:00
comp . registerSymbol ( " H-1 " ) ;
comp . registerSymbol ( " He-4 " ) ;
comp . setMassFraction ( " H-1 " , 0.6 ) ;
comp . setMassFraction ( " He-4 " , 0.4 ) ;
2025-10-12 10:12:49 -04:00
EXPECT_NO_THROW ( static_cast < void > ( comp . finalize ( ) ) ) ;
2025-03-25 12:49:37 -04:00
std : : vector < std : : string > symbols = { " H-1 " } ;
2025-06-21 11:33:27 -04:00
fourdst : : composition : : Composition subsetComp = comp . subset ( symbols , " norm " ) ;
2025-03-25 12:49:37 -04:00
EXPECT_TRUE ( subsetComp . finalize ( ) ) ;
EXPECT_DOUBLE_EQ ( subsetComp . getMassFraction ( " H-1 " ) , 1.0 ) ;
}
2025-07-24 09:35:52 -04:00
/**
* @ brief Tests the auto - normalization feature of the ` finalize ` method .
* @ details This test sets mass fractions that do not sum to 1.0 and then calls
* ` finalize ( true ) ` . It verifies that the composition is successfully finalized and that
* the mass fractions are correctly scaled to sum to 1.0 .
* @ par What this test proves :
* - ` finalize ( true ) ` correctly calculates the sum of fractions and normalizes each entry .
* - The resulting composition is valid and its normalized values can be retrieved .
* @ par What this test does not prove :
* - The behavior of ` finalize ( false ) ` , which is tested separately .
*/
2025-03-25 12:49:37 -04:00
TEST_F ( compositionTest , finalizeWithNormalization ) {
2025-06-21 11:33:27 -04:00
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
fourdst : : composition : : Composition comp ;
2025-03-25 12:49:37 -04:00
comp . registerSymbol ( " H-1 " ) ;
comp . registerSymbol ( " He-4 " ) ;
comp . setMassFraction ( " H-1 " , 0.3 ) ;
comp . setMassFraction ( " He-4 " , 0.3 ) ;
EXPECT_TRUE ( comp . finalize ( true ) ) ;
EXPECT_DOUBLE_EQ ( comp . getMassFraction ( " H-1 " ) , 0.5 ) ;
EXPECT_DOUBLE_EQ ( comp . getMassFraction ( " He-4 " ) , 0.5 ) ;
}
2025-07-24 09:35:52 -04:00
/**
* @ brief Tests the default ( non - normalizing ) behavior of the ` finalize ` method .
* @ details This test sets mass fractions that already sum to 1.0 and calls ` finalize ( false ) ` .
* It verifies that the composition is successfully finalized and the fractions remain unchanged .
* @ par What this test proves :
* - ` finalize ( false ) ` or ` finalize ( ) ` correctly validates a pre - normalized composition without altering its values .
* @ par What this test does not prove :
* - That ` finalize ( false ) ` would fail for a non - normalized composition ( this is implicitly tested in ` setGetComposition ` ) .
*/
2025-03-25 12:49:37 -04:00
TEST_F ( compositionTest , finalizeWithoutNormalization ) {
2025-06-21 11:33:27 -04:00
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
fourdst : : composition : : Composition comp ;
2025-03-25 12:49:37 -04:00
comp . registerSymbol ( " H-1 " ) ;
comp . registerSymbol ( " He-4 " ) ;
comp . setMassFraction ( " H-1 " , 0.5 ) ;
comp . setMassFraction ( " He-4 " , 0.5 ) ;
EXPECT_TRUE ( comp . finalize ( false ) ) ;
EXPECT_DOUBLE_EQ ( comp . getMassFraction ( " H-1 " ) , 0.5 ) ;
EXPECT_DOUBLE_EQ ( comp . getMassFraction ( " He-4 " ) , 0.5 ) ;
}
2025-07-24 09:35:52 -04:00
/**
* @ brief Tests the retrieval of global composition properties .
* @ details After creating and finalizing a composition , this test retrieves the
* ` CompositionEntry ` and ` GlobalComposition ` data . It verifies that the mass fraction
* in the entry and the calculated global properties ( mean particle mass , specific number density )
* are correct for the given input composition .
* @ par What this test proves :
* - The ` finalize ` method correctly computes ` meanParticleMass ` and ` specificNumberDensity ` .
* - The ` getComposition ` method returns a pair containing the correct entry - level and global data .
* @ par What this test does not prove :
* - The correctness of these calculations for all possible compositions , particularly complex ones . It validates the mechanism for a simple binary mixture .
*/
2025-03-25 12:49:37 -04:00
TEST_F ( compositionTest , getComposition ) {
2025-06-21 11:33:27 -04:00
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
fourdst : : composition : : Composition comp ;
2025-03-25 12:49:37 -04:00
comp . registerSymbol ( " H-1 " ) ;
comp . registerSymbol ( " He-4 " ) ;
comp . setMassFraction ( " H-1 " , 0.6 ) ;
comp . setMassFraction ( " He-4 " , 0.4 ) ;
2025-10-12 10:12:49 -04:00
EXPECT_NO_THROW ( static_cast < void > ( comp . finalize ( ) ) ) ;
2025-03-25 12:49:37 -04:00
2025-06-18 12:13:24 -04:00
const auto compositionEntry = comp . getComposition ( " H-1 " ) ;
2025-03-25 12:49:37 -04:00
EXPECT_DOUBLE_EQ ( compositionEntry . first . mass_fraction ( ) , 0.6 ) ;
EXPECT_DOUBLE_EQ ( compositionEntry . second . meanParticleMass , 1.4382769310381101 ) ;
EXPECT_DOUBLE_EQ ( compositionEntry . second . specificNumberDensity , 1.0 / 1.4382769310381101 ) ;
2025-03-25 13:01:22 -04:00
}
2025-07-24 09:35:52 -04:00
/**
* @ brief Tests the ability to switch between mass and number fraction modes .
* @ details This test creates a composition in mass fraction mode , finalizes it , and then
* switches to number fraction mode using ` setCompositionMode ( false ) ` . It then modifies the
* composition using number fractions and verifies that it must be re - finalized before switching back .
* @ par What this test proves :
* - ` setCompositionMode ` can be called on a finalized composition .
* - After switching modes , the appropriate ` set . . . Fraction ` method can be used .
* - Switching modes requires the composition to be finalized , and modifying it after the switch un - finalizes it again .
* @ par What this test does not prove :
* - The numerical correctness of the fraction conversions that happen internally when the mode is switched .
*/
2025-03-25 13:01:22 -04:00
TEST_F ( compositionTest , setCompositionMode ) {
2025-06-21 11:33:27 -04:00
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
fourdst : : composition : : Composition comp ;
2025-03-25 13:01:22 -04:00
comp . registerSymbol ( " H-1 " ) ;
comp . registerSymbol ( " He-4 " ) ;
comp . setMassFraction ( " H-1 " , 0.6 ) ;
comp . setMassFraction ( " He-4 " , 0.4 ) ;
2025-10-12 10:12:49 -04:00
EXPECT_NO_THROW ( static_cast < void > ( comp . finalize ( ) ) ) ;
2025-03-25 13:01:22 -04:00
EXPECT_DOUBLE_EQ ( comp . getMassFraction ( " H-1 " ) , 0.6 ) ;
EXPECT_DOUBLE_EQ ( comp . getMassFraction ( " He-4 " ) , 0.4 ) ;
EXPECT_NO_THROW ( comp . setCompositionMode ( false ) ) ;
EXPECT_NO_THROW ( comp . setNumberFraction ( " H-1 " , 0.9 ) ) ;
EXPECT_NO_THROW ( comp . setNumberFraction ( " He-4 " , 0.1 ) ) ;
2025-07-21 07:48:00 -04:00
EXPECT_THROW ( comp . setCompositionMode ( true ) , fourdst : : composition : : exceptions : : CompositionNotFinalizedError ) ;
2025-10-12 10:12:49 -04:00
EXPECT_NO_THROW ( static_cast < void > ( comp . finalize ( ) ) ) ;
2025-03-25 13:01:22 -04:00
EXPECT_NO_THROW ( comp . setCompositionMode ( true ) ) ;
2025-03-26 08:07:11 -04:00
}
2025-07-24 09:35:52 -04:00
/**
* @ brief Tests the ` hasSymbol ` utility method .
* @ details This test verifies that ` hasSymbol ` correctly reports the presence of registered
* symbols and the absence of non - registered symbols .
* @ par What this test proves :
* - The ` hasSymbol ` method accurately checks for the existence of a key in the internal composition map .
* @ par What this test does not prove :
* - Anything about the state ( e . g . , mass fraction ) of the symbol , only its presence .
*/
2025-03-26 08:07:11 -04:00
TEST_F ( compositionTest , hasSymbol ) {
2025-06-21 11:33:27 -04:00
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
fourdst : : composition : : Composition comp ;
2025-03-26 08:07:11 -04:00
comp . registerSymbol ( " H-1 " ) ;
comp . registerSymbol ( " He-4 " ) ;
comp . setMassFraction ( " H-1 " , 0.6 ) ;
comp . setMassFraction ( " He-4 " , 0.4 ) ;
2025-10-12 10:12:49 -04:00
EXPECT_NO_THROW ( static_cast < void > ( comp . finalize ( ) ) ) ;
2025-03-26 08:07:11 -04:00
EXPECT_TRUE ( comp . hasSymbol ( " H-1 " ) ) ;
EXPECT_TRUE ( comp . hasSymbol ( " He-4 " ) ) ;
EXPECT_FALSE ( comp . hasSymbol ( " H-2 " ) ) ;
EXPECT_FALSE ( comp . hasSymbol ( " He-3 " ) ) ;
}
2025-07-24 09:35:52 -04:00
/**
* @ brief Tests the mixing of two compositions .
* @ details This test creates two distinct compositions , finalizes them , and then mixes them
* using both the ` + ` operator ( 50 / 50 mix ) and the ` mix ` method with a specific fraction ( 25 / 75 ) .
* It verifies that the resulting mass fractions in the new compositions are correct .
* @ par What this test proves :
* - The ` mix ` method and the ` + ` operator correctly perform linear interpolation of mass fractions .
* - The resulting mixed composition is valid and its properties are correctly calculated .
* @ par What this test does not prove :
* - The behavior when mixing compositions with non - overlapping sets of symbols .
*/
2025-03-26 08:07:11 -04:00
TEST_F ( compositionTest , mix ) {
2025-06-21 11:33:27 -04:00
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
fourdst : : composition : : Composition comp1 ;
2025-03-26 08:07:11 -04:00
comp1 . registerSymbol ( " H-1 " ) ;
comp1 . registerSymbol ( " He-4 " ) ;
comp1 . setMassFraction ( " H-1 " , 0.6 ) ;
comp1 . setMassFraction ( " He-4 " , 0.4 ) ;
2025-10-12 10:12:49 -04:00
EXPECT_NO_THROW ( static_cast < void > ( comp1 . finalize ( ) ) ) ;
2025-03-26 08:07:11 -04:00
2025-06-21 11:33:27 -04:00
fourdst : : composition : : Composition comp2 ;
2025-03-26 08:07:11 -04:00
comp2 . registerSymbol ( " H-1 " ) ;
comp2 . registerSymbol ( " He-4 " ) ;
comp2 . setMassFraction ( " H-1 " , 0.4 ) ;
comp2 . setMassFraction ( " He-4 " , 0.6 ) ;
2025-10-12 10:12:49 -04:00
EXPECT_NO_THROW ( static_cast < void > ( comp2 . finalize ( ) ) ) ;
2025-03-26 08:07:11 -04:00
2025-06-21 11:33:27 -04:00
fourdst : : composition : : Composition mixedComp = comp1 + comp2 ;
2025-03-26 08:07:11 -04:00
EXPECT_TRUE ( mixedComp . finalize ( ) ) ;
EXPECT_DOUBLE_EQ ( mixedComp . getMassFraction ( " H-1 " ) , 0.5 ) ;
EXPECT_DOUBLE_EQ ( mixedComp . getMassFraction ( " He-4 " ) , 0.5 ) ;
2025-06-21 11:33:27 -04:00
fourdst : : composition : : Composition mixedComp2 = comp1 . mix ( comp2 , 0.25 ) ;
2025-03-26 08:07:11 -04:00
EXPECT_TRUE ( mixedComp2 . finalize ( ) ) ;
EXPECT_DOUBLE_EQ ( mixedComp2 . getMassFraction ( " H-1 " ) , 0.45 ) ;
EXPECT_DOUBLE_EQ ( mixedComp2 . getMassFraction ( " He-4 " ) , 0.55 ) ;
2025-06-21 11:33:27 -04:00
}
2025-06-25 08:26:50 -04:00
2025-07-24 09:35:52 -04:00
/**
* @ brief Tests the calculation of molar abundance .
* @ details This test creates a simple composition and verifies that ` getMolarAbundance `
* returns the correct value , which is defined as ( mass fraction / atomic mass ) .
* @ par What this test proves :
* - The ` getMolarAbundance ` calculation is performed correctly .
* @ par What this test does not prove :
* - The correctness of the underlying mass data , which is tested separately .
*/
2025-06-25 08:26:50 -04:00
TEST_F ( compositionTest , molarAbundance ) {
fourdst : : composition : : Composition comp1 ;
comp1 . registerSymbol ( " H-1 " ) ;
comp1 . registerSymbol ( " He-4 " ) ;
comp1 . setMassFraction ( " H-1 " , 0.5 ) ;
comp1 . setMassFraction ( " He-4 " , 0.5 ) ;
2025-10-12 10:12:49 -04:00
const bool didFinalize = comp1 . finalize ( ) ;
2025-06-25 08:26:50 -04:00
2025-10-12 10:12:49 -04:00
EXPECT_TRUE ( didFinalize ) ;
2025-06-25 08:26:50 -04:00
EXPECT_DOUBLE_EQ ( comp1 . getMolarAbundance ( " H-1 " ) , 0.5 / fourdst : : atomic : : H_1 . mass ( ) ) ;
EXPECT_DOUBLE_EQ ( comp1 . getMolarAbundance ( " He-4 " ) , 0.5 / fourdst : : atomic : : He_4 . mass ( ) ) ;
}
2025-06-28 06:31:41 -04:00
2025-07-24 09:35:52 -04:00
/**
* @ brief Tests the registration and retrieval of species objects .
* @ details This test uses ` registerSpecies ` to add species directly ( instead of by symbol )
* and then uses ` getRegisteredSpecies ` to retrieve them . It verifies that the returned
* set contains the correct ` Species ` objects .
* @ par What this test proves :
* - The ` registerSpecies ` and ` getRegisteredSpecies ` methods work correctly with ` Species ` objects .
* - The ` std : : set ` of species correctly stores and orders the objects ( based on the ` < ` operator overload ) .
* @ par What this test does not prove :
* - The behavior of setting fractions via ` Species ` objects , which is covered in other tests .
*/
2025-07-14 11:29:04 -04:00
TEST_F ( compositionTest , getRegisteredSpecies ) {
fourdst : : composition : : Composition comp ;
comp . registerSpecies ( { fourdst : : atomic : : Be_7 , fourdst : : atomic : : H_1 , fourdst : : atomic : : He_4 } , true ) ;
auto registeredSpecies = comp . getRegisteredSpecies ( ) ;
EXPECT_TRUE ( registeredSpecies . contains ( fourdst : : atomic : : H_1 ) ) ;
EXPECT_TRUE ( registeredSpecies . contains ( fourdst : : atomic : : He_4 ) ) ;
EXPECT_FALSE ( registeredSpecies . contains ( fourdst : : atomic : : Li_6 ) ) ;
auto it1 = registeredSpecies . begin ( ) ;
EXPECT_EQ ( * it1 , fourdst : : atomic : : H_1 ) ;
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests the az_to_species utility for correct species lookup and error handling .
* @ details This test checks that az_to_species returns the correct Species for valid ( A , Z ) pairs ,
* and the correct error codes for invalid atomic number or element number .
* @ par What this test proves :
* - az_to_species correctly maps valid ( A , Z ) to Species .
* - az_to_species returns appropriate error codes for invalid input .
*/
2025-08-13 13:07:49 -04:00
TEST_F ( compositionTest , getSpeciesFromAZ ) {
EXPECT_EQ ( fourdst : : atomic : : O_12 , fourdst : : atomic : : az_to_species ( 12 , 8 ) ) ;
2025-10-08 15:17:33 -04:00
EXPECT_EQ ( fourdst : : atomic : : SpeciesErrorType : : SPECIES_SYMBOL_NOT_FOUND , fourdst : : atomic : : az_to_species ( 120 , 38 ) . error ( ) ) ;
EXPECT_EQ ( fourdst : : atomic : : SpeciesErrorType : : ELEMENT_SYMBOL_NOT_FOUND , fourdst : : atomic : : az_to_species ( 120 , 500 ) . error ( ) ) ;
2025-08-13 13:07:49 -04:00
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests constructors that take vectors and sets of symbols .
* @ details This test verifies that the Composition can be constructed from a vector or set of symbols ,
* and that the resulting object correctly registers those symbols .
* @ par What this test proves :
* - Constructors accepting std : : vector and std : : set of symbols work as expected .
* - Registered symbols are correctly tracked .
*/
2025-10-06 14:29:33 -04:00
TEST_F ( compositionTest , constructorWithSymbolsVectorAndSet ) {
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
std : : vector < std : : string > vs = { " H-1 " , " He-4 " } ;
std : : set < std : : string > ss = { " H-1 " , " He-4 " } ;
fourdst : : composition : : Composition compVec ( vs ) ;
EXPECT_TRUE ( compVec . hasSymbol ( " H-1 " ) ) ;
EXPECT_TRUE ( compVec . hasSymbol ( " He-4 " ) ) ;
fourdst : : composition : : Composition compSet ( ss ) ;
EXPECT_TRUE ( compSet . hasSymbol ( " H-1 " ) ) ;
EXPECT_TRUE ( compSet . hasSymbol ( " He-4 " ) ) ;
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests constructors that take symbols and fractions for both mass and number fraction modes .
* @ details This test verifies that the Composition can be constructed directly from vectors of symbols and fractions ,
* and that the resulting mass and number fractions are correct . It also checks conversions between modes .
* @ par What this test proves :
* - Constructors for both mass and number fraction modes work as expected .
* - Conversion between mass and number fraction modes is correct .
*/
2025-10-06 14:29:33 -04:00
TEST_F ( compositionTest , constructorWithSymbolsAndFractionsMassAndNumber ) {
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
using fourdst : : composition : : Composition ;
using fourdst : : atomic : : species ;
// Mass-fraction constructor
std : : vector < std : : string > symM = { " H-1 " , " He-4 " } ;
std : : vector < double > fracM = { 0.6 , 0.4 } ;
Composition compM ( symM , fracM , true ) ;
EXPECT_NEAR ( compM . getMassFraction ( " H-1 " ) , 0.6 , 1e-12 ) ;
EXPECT_NEAR ( compM . getMassFraction ( " He-4 " ) , 0.4 , 1e-12 ) ;
// Mean particle mass and specific number density are reciprocals
double sn = 0.6 / species . at ( " H-1 " ) . mass ( ) + 0.4 / species . at ( " He-4 " ) . mass ( ) ;
double mp = 1.0 / sn ;
EXPECT_NEAR ( compM . getMeanParticleMass ( ) , mp , 1e-12 ) ;
// Number-fraction constructor
std : : vector < std : : string > symN = { " H-1 " , " He-4 " } ;
std : : vector < double > fracN = { 0.9 , 0.1 } ;
Composition compN ( symN , fracN , false ) ;
EXPECT_NEAR ( compN . getNumberFraction ( " H-1 " ) , 0.9 , 1e-12 ) ;
EXPECT_NEAR ( compN . getNumberFraction ( " He-4 " ) , 0.1 , 1e-12 ) ;
double meanA = 0.9 * species . at ( " H-1 " ) . mass ( ) + 0.1 * species . at ( " He-4 " ) . mass ( ) ;
EXPECT_NEAR ( compN . getMeanParticleMass ( ) , meanA , 1e-12 ) ;
// Check converted mass fractions X_i = n_i * A_i / <A>
double xH = 0.9 * species . at ( " H-1 " ) . mass ( ) / meanA ;
double xHe = 0.1 * species . at ( " He-4 " ) . mass ( ) / meanA ;
compN . setCompositionMode ( true ) ;
EXPECT_NEAR ( compN . getMassFraction ( " H-1 " ) , xH , 1e-12 ) ;
EXPECT_NEAR ( compN . getMassFraction ( " He-4 " ) , xHe , 1e-12 ) ;
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests registering symbols via vector , single Species , and mode mismatch error .
* @ details This test checks that symbols can be registered via a vector , that registering by Species works ,
* and that attempting to register a symbol with a mismatched mode throws the correct exception .
* @ par What this test proves :
* - registerSymbol works with vectors .
* - registerSpecies works with single Species .
* - Mode mismatch throws CompositionModeError .
*/
2025-10-06 14:29:33 -04:00
TEST_F ( compositionTest , registerSymbolVectorAndSingleSpeciesAndModeMismatch ) {
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
using fourdst : : composition : : Composition ;
Composition comp ;
comp . registerSymbol ( std : : vector < std : : string > { " H-1 " , " He-4 " } ) ;
EXPECT_TRUE ( comp . hasSymbol ( " H-1 " ) ) ;
EXPECT_TRUE ( comp . hasSymbol ( " He-4 " ) ) ;
// Register by Species
Composition comp2 ;
comp2 . registerSpecies ( fourdst : : atomic : : H_1 ) ;
comp2 . registerSpecies ( fourdst : : atomic : : He_4 ) ;
EXPECT_TRUE ( comp2 . hasSymbol ( " H-1 " ) ) ;
EXPECT_TRUE ( comp2 . hasSymbol ( " He-4 " ) ) ;
// Mode mismatch should throw
Composition comp3 ;
comp3 . registerSymbol ( " H-1 " , true ) ; // mass mode
EXPECT_THROW ( comp3 . registerSymbol ( " He-4 " , false ) , fourdst : : composition : : exceptions : : CompositionModeError ) ;
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests setMassFraction overloads for Species and vectors .
* @ details This test verifies that setMassFraction works for both single Species and vectors of Species ,
* and that the returned old values are correct .
* @ par What this test proves :
* - setMassFraction overloads work as expected .
* - Old values are returned correctly .
*/
2025-10-06 14:29:33 -04:00
TEST_F ( compositionTest , setMassFractionBySpeciesAndVector ) {
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
using fourdst : : composition : : Composition ;
using fourdst : : atomic : : H_1 ;
using fourdst : : atomic : : He_4 ;
Composition comp ;
comp . registerSymbol ( " H-1 " ) ;
comp . registerSymbol ( " He-4 " ) ;
// Single species overload
double old = comp . setMassFraction ( H_1 , 0.7 ) ;
EXPECT_NEAR ( old , 0.0 , 1e-15 ) ;
old = comp . setMassFraction ( He_4 , 0.3 ) ;
EXPECT_NEAR ( old , 0.0 , 1e-15 ) ;
// Vector overload
std : : vector < fourdst : : atomic : : Species > sp = { H_1 , He_4 } ;
std : : vector < double > xs = { 0.6 , 0.4 } ;
auto olds = comp . setMassFraction ( sp , xs ) ;
ASSERT_EQ ( olds . size ( ) , 2u ) ;
EXPECT_NEAR ( olds [ 0 ] , 0.7 , 1e-12 ) ;
EXPECT_NEAR ( olds [ 1 ] , 0.3 , 1e-12 ) ;
EXPECT_TRUE ( comp . finalize ( ) ) ;
EXPECT_NEAR ( comp . getMassFraction ( " H-1 " ) , 0.6 , 1e-12 ) ;
EXPECT_NEAR ( comp . getMassFraction ( He_4 ) , 0.4 , 1e-12 ) ;
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests setNumberFraction overloads for symbols and Species .
* @ details This test verifies that setNumberFraction works for both single symbols / Species and vectors ,
* and that the returned old values are correct .
* @ par What this test proves :
* - setNumberFraction overloads work as expected .
* - Old values are returned correctly .
*/
2025-10-06 14:29:33 -04:00
TEST_F ( compositionTest , setNumberFractionOverloads ) {
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
using fourdst : : composition : : Composition ;
using fourdst : : atomic : : H_1 ;
using fourdst : : atomic : : He_4 ;
Composition comp ;
comp . registerSymbol ( " H-1 " , false ) ;
comp . registerSymbol ( " He-4 " , false ) ;
// Single symbol
double old = comp . setNumberFraction ( " H-1 " , 0.8 ) ;
EXPECT_NEAR ( old , 0.0 , 1e-15 ) ;
// Vector of symbols
auto oldv = comp . setNumberFraction ( std : : vector < std : : string > { " H-1 " , " He-4 " } , std : : vector < double > { 0.75 , 0.25 } ) ;
ASSERT_EQ ( oldv . size ( ) , 2u ) ;
EXPECT_NEAR ( oldv [ 0 ] , 0.8 , 1e-12 ) ;
EXPECT_NEAR ( oldv [ 1 ] , 0.0 , 1e-12 ) ;
// Species and vector<Species>
old = comp . setNumberFraction ( H_1 , 0.7 ) ;
EXPECT_NEAR ( old , 0.75 , 1e-12 ) ;
auto oldsv = comp . setNumberFraction ( std : : vector < fourdst : : atomic : : Species > { H_1 , He_4 } , std : : vector < double > { 0.6 , 0.4 } ) ;
ASSERT_EQ ( oldsv . size ( ) , 2u ) ;
EXPECT_NEAR ( oldsv [ 0 ] , 0.7 , 1e-12 ) ;
EXPECT_NEAR ( oldsv [ 1 ] , 0.25 , 1e-12 ) ;
EXPECT_TRUE ( comp . finalize ( ) ) ;
EXPECT_NEAR ( comp . getNumberFraction ( " H-1 " ) , 0.6 , 1e-12 ) ;
EXPECT_NEAR ( comp . getNumberFraction ( He_4 ) , 0.4 , 1e-12 ) ;
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests error cases for mixing compositions .
* @ details This test checks that mixing with a non - finalized composition or with invalid fractions throws the correct exceptions .
* @ par What this test proves :
* - Mixing with a non - finalized composition throws CompositionNotFinalizedError .
* - Mixing with invalid fractions throws InvalidCompositionError .
*/
2025-10-06 14:29:33 -04:00
TEST_F ( compositionTest , mixErrorCases ) {
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
using fourdst : : composition : : Composition ;
2025-10-12 10:12:49 -04:00
Composition a ; a . registerSymbol ( " H-1 " ) ; a . registerSymbol ( " He-4 " ) ; a . setMassFraction ( " H-1 " , 0.6 ) ; a . setMassFraction ( " He-4 " , 0.4 ) ;
bool didFinalizeA = a . finalize ( ) ;
EXPECT_TRUE ( didFinalizeA ) ;
2025-10-06 14:29:33 -04:00
Composition b ; b . registerSymbol ( " H-1 " ) ; b . registerSymbol ( " He-4 " ) ; b . setMassFraction ( " H-1 " , 0.5 ) ; b . setMassFraction ( " He-4 " , 0.5 ) ;
// Not finalized second comp
EXPECT_THROW ( static_cast < void > ( a . mix ( b , 0.5 ) ) , fourdst : : composition : : exceptions : : CompositionNotFinalizedError ) ;
2025-10-12 10:12:49 -04:00
bool didFinalizeB = b . finalize ( ) ;
EXPECT_TRUE ( didFinalizeB ) ;
2025-10-06 14:29:33 -04:00
// Invalid fraction
EXPECT_THROW ( static_cast < void > ( a . mix ( b , - 0.1 ) ) , fourdst : : composition : : exceptions : : InvalidCompositionError ) ;
EXPECT_THROW ( static_cast < void > ( a . mix ( b , 1.1 ) ) , fourdst : : composition : : exceptions : : InvalidCompositionError ) ;
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests getMassFraction and getNumberFraction maps and Species overloads .
* @ details This test verifies that the getter methods for mass and number fractions return correct maps ,
* and that overloads for Species work as expected .
* @ par What this test proves :
* - getMassFraction and getNumberFraction return correct maps .
* - Overloads for Species work as expected .
*/
2025-10-06 14:29:33 -04:00
TEST_F ( compositionTest , getMassAndNumberFractionMapsAndSpeciesOverloads ) {
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
using fourdst : : composition : : Composition ;
Composition comp ; comp . registerSymbol ( " H-1 " ) ; comp . registerSymbol ( " He-4 " ) ;
comp . setMassFraction ( " H-1 " , 0.6 ) ; comp . setMassFraction ( " He-4 " , 0.4 ) ;
ASSERT_TRUE ( comp . finalize ( ) ) ;
auto m = comp . getMassFraction ( ) ;
ASSERT_EQ ( m . size ( ) , 2u ) ;
EXPECT_NEAR ( m . at ( " H-1 " ) , 0.6 , 1e-12 ) ;
EXPECT_NEAR ( m . at ( " He-4 " ) , 0.4 , 1e-12 ) ;
EXPECT_NEAR ( comp . getMassFraction ( fourdst : : atomic : : H_1 ) , 0.6 , 1e-12 ) ;
EXPECT_NEAR ( comp . getMolarAbundance ( fourdst : : atomic : : H_1 ) , m . at ( " H-1 " ) / fourdst : : atomic : : H_1 . mass ( ) , 1e-12 ) ;
// Switch to number-fraction mode and verify number maps
comp . setCompositionMode ( false ) ;
// Must re-finalize after modifications (mode switch itself keeps values consistent but not finalized status changed? setCompositionMode requires to be finalized; here we just switched modes)
// Set specific number fractions and finalize
comp . setNumberFraction ( " H-1 " , 0.7 ) ;
comp . setNumberFraction ( " He-4 " , 0.3 ) ;
ASSERT_TRUE ( comp . finalize ( ) ) ;
auto n = comp . getNumberFraction ( ) ;
ASSERT_EQ ( n . size ( ) , 2u ) ;
EXPECT_NEAR ( n . at ( " H-1 " ) , 0.7 , 1e-12 ) ;
EXPECT_NEAR ( n . at ( " He-4 " ) , 0.3 , 1e-12 ) ;
EXPECT_NEAR ( comp . getNumberFraction ( fourdst : : atomic : : He_4 ) , 0.3 , 1e-12 ) ;
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests mean atomic number and electron abundance calculations .
* @ details This test verifies that getElectronAbundance and getMeanAtomicNumber return correct values
* based on the composition .
* @ par What this test proves :
* - getElectronAbundance and getMeanAtomicNumber are calculated correctly .
*/
2025-10-06 14:29:33 -04:00
TEST_F ( compositionTest , meanAtomicNumberAndElectronAbundance ) {
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
using fourdst : : atomic : : species ;
using fourdst : : composition : : Composition ;
Composition comp ; comp . registerSymbol ( " H-1 " ) ; comp . registerSymbol ( " He-4 " ) ;
comp . setMassFraction ( " H-1 " , 0.6 ) ; comp . setMassFraction ( " He-4 " , 0.4 ) ;
ASSERT_TRUE ( comp . finalize ( ) ) ;
// Compute expected Ye = sum(X_i * Z_i / A_i)
constexpr double xH = 0.6 , xHe = 0.4 ;
const double aH = species . at ( " H-1 " ) . a ( ) ;
const double aHe = species . at ( " He-4 " ) . a ( ) ;
const double zH = species . at ( " H-1 " ) . z ( ) ;
const double zHe = species . at ( " He-4 " ) . z ( ) ;
const double expectedYe = xH * zH / aH + xHe * zHe / aHe ;
EXPECT_NEAR ( comp . getElectronAbundance ( ) , expectedYe , 1e-12 ) ;
// <Z> = <A> * sum(X_i * Z_i / A_i)
const double expectedZ = comp . getMeanParticleMass ( ) * expectedYe ;
EXPECT_NEAR ( comp . getMeanAtomicNumber ( ) , expectedZ , 1e-12 ) ;
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests canonical composition and caching behavior .
* @ details This test verifies that getCanonicalComposition returns correct X , Y , Z values ,
* and that repeated calls use the cached result .
* @ par What this test proves :
* - getCanonicalComposition returns correct canonical values .
* - Caching works as expected .
*/
2025-10-06 14:29:33 -04:00
TEST_F ( compositionTest , canonicalCompositionAndCaching ) {
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
using fourdst : : composition : : Composition ;
Composition comp ; comp . registerSymbol ( " H-1 " ) ; comp . registerSymbol ( " He-4 " ) ;
comp . setMassFraction ( " H-1 " , 0.6 ) ; comp . setMassFraction ( " He-4 " , 0.4 ) ;
ASSERT_TRUE ( comp . finalize ( ) ) ;
auto canon1 = comp . getCanonicalComposition ( ) ;
EXPECT_NEAR ( canon1 . X , 0.6 , 1e-12 ) ;
EXPECT_NEAR ( canon1 . Y , 0.4 , 1e-12 ) ;
EXPECT_NEAR ( canon1 . Z , 0.0 , 1e-12 ) ;
// Call again to exercise caching code path
auto canon2 = comp . getCanonicalComposition ( ) ;
EXPECT_NEAR ( canon2 . X , 0.6 , 1e-12 ) ;
EXPECT_NEAR ( canon2 . Y , 0.4 , 1e-12 ) ;
EXPECT_NEAR ( canon2 . Z , 0.0 , 1e-12 ) ;
// Add a metal and re-check
Composition comp2 ; comp2 . registerSymbol ( " H-1 " ) ; comp2 . registerSymbol ( " He-4 " ) ; comp2 . registerSymbol ( " O-16 " ) ;
comp2 . setMassFraction ( " H-1 " , 0.6 ) ; comp2 . setMassFraction ( " He-4 " , 0.35 ) ; comp2 . setMassFraction ( " O-16 " , 0.05 ) ;
ASSERT_TRUE ( comp2 . finalize ( ) ) ;
auto canon3 = comp2 . getCanonicalComposition ( true ) ;
EXPECT_NEAR ( canon3 . X , 0.6 , 1e-12 ) ;
EXPECT_NEAR ( canon3 . Y , 0.35 , 1e-12 ) ;
EXPECT_NEAR ( canon3 . Z , 0.05 , 1e-12 ) ;
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests vector getters , indexing , and species - at - index functionality .
* @ details This test verifies that the vector getters for mass , number , and molar abundance fractions
* return correct values , and that species can be accessed by index .
* @ par What this test proves :
* - Vector getters return correct values .
* - Indexing and species - at - index work as expected .
*/
2025-10-06 14:29:33 -04:00
TEST_F ( compositionTest , vectorsAndIndexingAndSpeciesAtIndex ) {
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
using fourdst : : composition : : Composition ;
using fourdst : : atomic : : species ;
Composition comp ; comp . registerSymbol ( " H-1 " ) ; comp . registerSymbol ( " He-4 " ) ; comp . registerSymbol ( " O-16 " ) ;
comp . setMassFraction ( " H-1 " , 0.5 ) ; comp . setMassFraction ( " He-4 " , 0.3 ) ; comp . setMassFraction ( " O-16 " , 0.2 ) ;
ASSERT_TRUE ( comp . finalize ( ) ) ;
// Mass fraction vector sorted by mass: H-1, He-4, O-16
auto mv = comp . getMassFractionVector ( ) ;
ASSERT_EQ ( mv . size ( ) , 3u ) ;
EXPECT_NEAR ( mv [ 0 ] , 0.5 , 1e-12 ) ;
EXPECT_NEAR ( mv [ 1 ] , 0.3 , 1e-12 ) ;
EXPECT_NEAR ( mv [ 2 ] , 0.2 , 1e-12 ) ;
// Species indices ordering
size_t iH = comp . getSpeciesIndex ( " H-1 " ) ;
size_t iHe = comp . getSpeciesIndex ( " He-4 " ) ;
size_t iO = comp . getSpeciesIndex ( " O-16 " ) ;
EXPECT_LT ( iH , iHe ) ;
EXPECT_LT ( iHe , iO ) ;
EXPECT_EQ ( comp . getSpeciesIndex ( fourdst : : atomic : : H_1 ) , iH ) ;
EXPECT_EQ ( comp . getSpeciesIndex ( species . at ( " He-4 " ) ) , iHe ) ;
// Species at index
auto sAtHe = comp . getSpeciesAtIndex ( iHe ) ;
EXPECT_EQ ( std : : string ( sAtHe . name ( ) ) , std : : string ( " He-4 " ) ) ;
// Number fraction vector after switching modes
comp . setCompositionMode ( false ) ;
// Tweak number fractions and finalize
// Compute expected number fractions from original mass fractions first
double denom = 0.5 / species . at ( " H-1 " ) . mass ( ) + 0.3 / species . at ( " He-4 " ) . mass ( ) + 0.2 / species . at ( " O-16 " ) . mass ( ) ;
double nH_exp = ( 0.5 / species . at ( " H-1 " ) . mass ( ) ) / denom ;
double nHe_exp = ( 0.3 / species . at ( " He-4 " ) . mass ( ) ) / denom ;
double nO_exp = ( 0.2 / species . at ( " O-16 " ) . mass ( ) ) / denom ;
auto nv0 = comp . getNumberFractionVector ( ) ;
ASSERT_EQ ( nv0 . size ( ) , 3u ) ;
EXPECT_NEAR ( nv0 [ iH ] , nH_exp , 1e-12 ) ;
EXPECT_NEAR ( nv0 [ iHe ] , nHe_exp , 1e-12 ) ;
EXPECT_NEAR ( nv0 [ iO ] , nO_exp , 1e-12 ) ;
// Molar abundance vector X_i/A_i in mass mode; switch back to mass mode to verify
comp . setCompositionMode ( true ) ;
2025-10-12 10:12:49 -04:00
bool didFinalize = comp . finalize ( true ) ;
EXPECT_TRUE ( didFinalize ) ;
2025-10-06 14:29:33 -04:00
auto av = comp . getMolarAbundanceVector ( ) ;
ASSERT_EQ ( av . size ( ) , 3u ) ;
EXPECT_NEAR ( av [ iH ] , 0.5 / species . at ( " H-1 " ) . mass ( ) , 1e-12 ) ;
EXPECT_NEAR ( av [ iHe ] , 0.3 / species . at ( " He-4 " ) . mass ( ) , 1e-12 ) ;
EXPECT_NEAR ( av [ iO ] , 0.2 / species . at ( " O-16 " ) . mass ( ) , 1e-12 ) ;
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests contains method and pre - finalization guards .
* @ details This test verifies that contains throws before finalization and works correctly after .
* @ par What this test proves :
* - contains throws before finalize .
* - contains works as expected after finalize .
*/
2025-10-06 14:29:33 -04:00
TEST_F ( compositionTest , containsAndPreFinalizationGuards ) {
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
fourdst : : composition : : Composition comp ;
comp . registerSymbol ( " H-1 " ) ; comp . registerSymbol ( " He-4 " ) ;
comp . setMassFraction ( " H-1 " , 0.6 ) ; comp . setMassFraction ( " He-4 " , 0.4 ) ;
// contains should throw before finalize
EXPECT_THROW ( static_cast < void > ( comp . contains ( fourdst : : atomic : : H_1 ) ) , fourdst : : composition : : exceptions : : CompositionNotFinalizedError ) ;
ASSERT_TRUE ( comp . finalize ( ) ) ;
EXPECT_TRUE ( comp . contains ( fourdst : : atomic : : H_1 ) ) ;
EXPECT_FALSE ( comp . contains ( fourdst : : atomic : : Li_6 ) ) ;
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests subset method with " none " normalization and normalization flow .
* @ details This test verifies that subset with " none " does not normalize by default ,
* and that normalization can be forced with finalize ( true ) .
* @ par What this test proves :
* - subset with " none " does not normalize by default .
* - finalize ( true ) normalizes the subset .
*/
2025-10-06 14:29:33 -04:00
TEST_F ( compositionTest , subsetNoneMethodAndNormalizationFlow ) {
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
fourdst : : composition : : Composition comp ;
comp . registerSymbol ( " H-1 " ) ; comp . registerSymbol ( " He-4 " ) ; comp . registerSymbol ( " O-16 " ) ;
comp . setMassFraction ( " H-1 " , 0.5 ) ; comp . setMassFraction ( " He-4 " , 0.3 ) ; comp . setMassFraction ( " O-16 " , 0.2 ) ;
ASSERT_TRUE ( comp . finalize ( ) ) ;
fourdst : : composition : : Composition sub = comp . subset ( std : : vector < std : : string > { " H-1 " , " He-4 " } , " none " ) ;
// Not normalized: finalize without normalization should fail
EXPECT_FALSE ( sub . finalize ( false ) ) ;
// With normalization, it should succeed and scale to sum to 1
EXPECT_TRUE ( sub . finalize ( true ) ) ;
double sum = sub . getMassFraction ( " H-1 " ) + sub . getMassFraction ( " He-4 " ) ;
EXPECT_NEAR ( sum , 1.0 , 1e-12 ) ;
EXPECT_NEAR ( sub . getMassFraction ( " H-1 " ) , 0.5 / ( 0.5 + 0.3 ) , 1e-12 ) ;
EXPECT_NEAR ( sub . getMassFraction ( " He-4 " ) , 0.3 / ( 0.5 + 0.3 ) , 1e-12 ) ;
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests copy constructor and assignment operator for independence .
* @ details This test verifies that copies of a Composition object are independent of the original .
* @ par What this test proves :
* - Copy constructor and assignment operator create independent objects .
*/
2025-10-06 14:29:33 -04:00
TEST_F ( compositionTest , copyConstructorAndAssignmentIndependence ) {
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
using fourdst : : composition : : Composition ;
Composition comp ; comp . registerSymbol ( " H-1 " ) ; comp . registerSymbol ( " He-4 " ) ;
comp . setMassFraction ( " H-1 " , 0.6 ) ; comp . setMassFraction ( " He-4 " , 0.4 ) ;
ASSERT_TRUE ( comp . finalize ( ) ) ;
Composition copy ( comp ) ; // copy ctor
EXPECT_NEAR ( copy . getMassFraction ( " H-1 " ) , 0.6 , 1e-12 ) ;
EXPECT_NEAR ( copy . getMassFraction ( " He-4 " ) , 0.4 , 1e-12 ) ;
Composition assigned ; assigned = comp ; // assignment
EXPECT_NEAR ( assigned . getMassFraction ( " H-1 " ) , 0.6 , 1e-12 ) ;
EXPECT_NEAR ( assigned . getMassFraction ( " He-4 " ) , 0.4 , 1e-12 ) ;
// Modify original and ensure copies do not change
comp . setMassFraction ( " H-1 " , 0.7 ) ; comp . setMassFraction ( " He-4 " , 0.3 ) ; ASSERT_TRUE ( comp . finalize ( ) ) ;
EXPECT_NEAR ( copy . getMassFraction ( " H-1 " ) , 0.6 , 1e-12 ) ;
EXPECT_NEAR ( assigned . getMassFraction ( " He-4 " ) , 0.4 , 1e-12 ) ;
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests getComposition by Species and retrieval of all entries .
* @ details This test verifies that getComposition works for both single Species and all entries ,
* and that the returned values are correct .
* @ par What this test proves :
* - getComposition works for both single Species and all entries .
*/
2025-10-06 14:29:33 -04:00
TEST_F ( compositionTest , getCompositionBySpeciesAndAllEntries ) {
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
using fourdst : : composition : : Composition ;
Composition comp ; comp . registerSymbol ( " H-1 " ) ; comp . registerSymbol ( " He-4 " ) ;
comp . setMassFraction ( " H-1 " , 0.6 ) ; comp . setMassFraction ( " He-4 " , 0.4 ) ;
ASSERT_TRUE ( comp . finalize ( ) ) ;
auto pairBySpec = comp . getComposition ( fourdst : : atomic : : H_1 ) ;
EXPECT_NEAR ( pairBySpec . first . mass_fraction ( ) , 0.6 , 1e-12 ) ;
EXPECT_NEAR ( pairBySpec . second . meanParticleMass , comp . getMeanParticleMass ( ) , 1e-15 ) ;
auto all = comp . getComposition ( ) ;
ASSERT_EQ ( all . first . size ( ) , 2u ) ;
EXPECT_NEAR ( all . first . at ( " H-1 " ) . mass_fraction ( ) , 0.6 , 1e-12 ) ;
EXPECT_NEAR ( all . first . at ( " He-4 " ) . mass_fraction ( ) , 0.4 , 1e-12 ) ;
EXPECT_NEAR ( all . second . meanParticleMass , comp . getMeanParticleMass ( ) , 1e-15 ) ;
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests iteration over composition and out - of - range index access .
* @ details This test verifies that begin / end iteration covers all entries ,
* and that accessing an out - of - range index throws an exception .
* @ par What this test proves :
* - Iteration covers all entries .
* - Out - of - range index throws std : : out_of_range .
*/
2025-10-06 14:29:33 -04:00
TEST_F ( compositionTest , iterationBeginEndAndIndexOutOfRange ) {
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
fourdst : : composition : : Composition comp ;
comp . registerSymbol ( " H-1 " ) ; comp . registerSymbol ( " He-4 " ) ; comp . registerSymbol ( " O-16 " ) ;
comp . setMassFraction ( " H-1 " , 0.5 ) ; comp . setMassFraction ( " He-4 " , 0.3 ) ; comp . setMassFraction ( " O-16 " , 0.2 ) ;
ASSERT_TRUE ( comp . finalize ( ) ) ;
// Iterate and count entries
size_t count = 0 ;
for ( auto it = comp . begin ( ) ; it ! = comp . end ( ) ; + + it ) {
count + + ;
}
EXPECT_EQ ( count , 3u ) ;
// Out-of-range access
EXPECT_THROW ( static_cast < void > ( comp . getSpeciesAtIndex ( 100 ) ) , std : : out_of_range ) ;
}
2025-10-12 10:21:28 -04:00
/**
* @ brief Tests inheritance from CompositionAbstract and overriding a getter .
* @ details This test defines a derived class that overrides getMolarAbundance ,
* and verifies that the override works and other getters are inherited .
* @ par What this test proves :
* - Derived classes can override getters .
* - All CompositionAbstract children have the standard getter interface .
*/
2025-10-12 10:12:49 -04:00
TEST_F ( compositionTest , abstractBase ) {
class UnrestrictedComposition : public fourdst : : composition : : Composition {
private :
fourdst : : atomic : : Species m_species ;
const Composition & m_composition ;
public :
UnrestrictedComposition ( const Composition & base , const fourdst : : atomic : : Species & species ) :
Composition ( base ) ,
m_species ( species ) ,
m_composition ( base )
{ }
double getMolarAbundance ( const fourdst : : atomic : : Species & species ) const override {
if ( species = = m_species ) {
return 1.0 ;
}
return m_composition . getMolarAbundance ( species ) ;
}
} ;
fourdst : : config : : Config : : getInstance ( ) . loadConfig ( EXAMPLE_FILENAME ) ;
fourdst : : composition : : Composition comp ;
comp . registerSymbol ( " H-1 " ) ; comp . registerSymbol ( " He-4 " ) ; comp . registerSymbol ( " O-16 " ) ;
comp . setMassFraction ( " H-1 " , 0.5 ) ; comp . setMassFraction ( " He-4 " , 0.3 ) ; comp . setMassFraction ( " O-16 " , 0.2 ) ;
ASSERT_TRUE ( comp . finalize ( ) ) ;
const UnrestrictedComposition uComp ( comp , fourdst : : atomic : : H_1 ) ;
ASSERT_DOUBLE_EQ ( uComp . getMolarAbundance ( fourdst : : atomic : : H_1 ) , 1.0 ) ;
ASSERT_DOUBLE_EQ ( uComp . getMassFraction ( " He-4 " ) , comp . getMassFraction ( " He-4 " ) ) ;
}