fourdst::libcomposition v2.2.1
Robust atomic species information library
Loading...
Searching...
No Matches
composition.cpp
Go to the documentation of this file.
1/* ***********************************************************************
2//
3// Copyright (C) 2025 -- The 4D-STAR Collaboration
4// File Author: Emily Boudreaux
5// Last Modified: October 6, 2025
6//
7// 4DSSE is free software; you can use it and/or modify
8// it under the terms and restrictions the GNU General Library Public
9// License version 3 (GPLv3) as published by the Free Software Foundation.
10//
11// 4DSSE is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14// See the GNU Library General Public License for more details.
15//
16// You should have received a copy of the GNU Library General Public License
17// along with this software; if not, write to the Free Software
18// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19//
20// *********************************************************************** */
21#include "quill/LogMacros.h"
22
23#include <stdexcept>
24#include <unordered_map>
25#include <vector>
26#include <ranges>
27#include <algorithm>
28#include <set>
29#include <string>
30
31
32#include <utility>
33
37
39
40namespace {
41 template<typename A, typename B>
42 std::vector<A> sortVectorBy(
43 std::vector<A> toSort,
44 const std::vector<B>& by
45 ) {
46 std::vector<std::size_t> indices(by.size());
47 for (size_t i = 0; i < indices.size(); i++) {
48 indices[i] = i;
49 }
50
51 std::ranges::sort(indices, [&](size_t a, size_t b) {
52 return by[a] < by[b];
53 });
54
55 std::vector<A> sorted;
56 sorted.reserve(indices.size());
57
58 for (const auto idx: indices) {
59 sorted.push_back(toSort[idx]);
60 }
61
62 return sorted;
63 }
64
65 std::optional<fourdst::atomic::Species> getSpecies(const std::string& symbol) {
66 if (!fourdst::atomic::species.contains(symbol)) {
67 return std::nullopt;
68 }
69 return fourdst::atomic::species.at(symbol);
70 }
71
72 void throw_unknown_symbol(quill::Logger* logger, const std::string& symbol) {
73 LOG_ERROR(logger, "Symbol {} is not a valid species symbol (not in the species database)", symbol);
74 throw fourdst::composition::exceptions::UnknownSymbolError("Symbol " + symbol + " is not a valid species symbol (not in the species database)");
75 }
76
77 void throw_unregistered_symbol(quill::Logger* logger, const std::string& symbol) {
78 LOG_ERROR(logger, "Symbol {} is not registered in the composition.", symbol);
79 throw fourdst::composition::exceptions::UnregisteredSymbolError("Symbol " + symbol + " is not registered in the composition.");
80 }
81}
82
83namespace fourdst::composition {
85 const std::vector<std::string>& symbols
86 ) {
87 for (const auto& symbol : symbols) {
88 registerSymbol(symbol);
89 }
90 }
91
93 const std::set<std::string>& symbols
94 ) {
95 for (const auto& symbol : symbols) {
96 registerSymbol(symbol);
97 }
98 }
99
101 const std::vector<atomic::Species> &species
102 ) {
103 for (const auto& s : species) {
105 }
106 }
107
109 const std::set<atomic::Species> &species
110 ) {
111 for (const auto& s : species) {
113 }
114 }
115
116 Composition::Composition(const std::unordered_set<std::string> &symbols) {
117 for (const auto& symbol : symbols) {
118 registerSymbol(symbol);
119 }
120 }
121
122 Composition::Composition(const std::unordered_set<atomic::Species> &species) {
123 for (const auto& s : species) {
125 }
126 }
127
129 const std::vector<std::string>& symbols,
130 const std::vector<double>& molarAbundances
131 ) {
132 if (symbols.size() != molarAbundances.size()) {
133 LOG_CRITICAL(getLogger(), "The number of symbols and molarAbundances must be equal (got {} symbols and {} molarAbundances).", symbols.size(), molarAbundances.size());
134 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.");
135 }
136
137 for (const auto &[symbol, y] : std::views::zip(symbols, molarAbundances)) {
138 registerSymbol(symbol);
139 setMolarAbundance(symbol, y);
140 }
141 }
142
144 const std::vector<atomic::Species> &species,
145 const std::vector<double> &molarAbundances
146 ) {
147 if (species.size() != molarAbundances.size()) {
148 LOG_CRITICAL(getLogger(), "The number of species and molarAbundances must be equal (got {} species and {} molarAbundances).", species.size(), molarAbundances.size());
149 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.");
150 }
151
152 for (const auto& [s, y] : std::views::zip(species, molarAbundances)) {
154 setMolarAbundance(s, y);
155 }
156 }
157
159 const std::set<std::string> &symbols,
160 const std::vector<double> &molarAbundances
161 ) {
162 if (symbols.size() != molarAbundances.size()) {
163 LOG_CRITICAL(getLogger(), "The number of symbols and molarAbundances must be equal (got {} symbols and {} molarAbundances).", symbols.size(), molarAbundances.size());
164 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.");
165 }
166
167 for (const auto& [symbol, y] : std::views::zip(sortVectorBy<std::string>(std::vector<std::string>(symbols.begin(), symbols.end()), molarAbundances), molarAbundances)) {
168 registerSymbol(symbol);
169 setMolarAbundance(symbol, y);
170 }
171 }
172
173 Composition::Composition(const std::unordered_map<std::string, double> &symbolMolarAbundances) {
174 for (const auto& [symbol, y] : symbolMolarAbundances) {
175 registerSymbol(symbol);
176 setMolarAbundance(symbol, y);
177 }
178 }
179
180 Composition::Composition(const std::map<std::string, double> &symbolMolarAbundances) {
181 for (const auto& [symbol, y] : symbolMolarAbundances) {
182 registerSymbol(symbol);
183 setMolarAbundance(symbol, y);
184 }
185 }
186
187 Composition::Composition(const std::unordered_map<atomic::Species, double> &speciesMolarAbundances) {
188 for (const auto& [species, y] : speciesMolarAbundances) {
189 registerSpecies(species);
190 setMolarAbundance(species, y);
191 }
192 }
193
194 Composition::Composition(const std::map<atomic::Species, double> &speciesMolarAbundances) {
195 for (const auto& [species, y] : speciesMolarAbundances) {
196 registerSpecies(species);
197 setMolarAbundance(species, y);
198 }
199 }
200
202 const Composition &composition
203 ) {
206 }
207
209 for (const auto& species : composition.getRegisteredSpecies()) {
210 registerSpecies(species);
211 setMolarAbundance(species, composition.getMolarAbundance(species));
212 }
213 }
214
216 const Composition &other
217 ) {
218 if (this != &other) {
221 }
222 return *this;
223 }
224
226 const std::string& symbol
227 ) {
228 const auto result = getSpecies(symbol);
229 if (!result) {
230 throw_unknown_symbol(getLogger(), symbol);
231 }
232
233 registerSpecies(result.value());
234 }
235
237 const std::vector<std::string>& symbols
238 ) {
239 for (const auto& symbol : symbols) {
240 registerSymbol(symbol);
241 }
242 }
243
245 const atomic::Species &species
246 ) noexcept {
247 m_registeredSpecies.insert(species);
248 if (!m_molarAbundances.contains(species)) {
249 m_molarAbundances.emplace(species, 0.0);
250 }
251 }
252
254 const std::vector<atomic::Species> &species
255 ) noexcept {
256 for (const auto& s : species) {
257 registerSpecies(s);
258 }
259 }
260
261 std::set<std::string> Composition::getRegisteredSymbols() const noexcept {
262 std::set<std::string> symbols;
263 for (const auto& species : m_registeredSpecies) {
264 symbols.insert(std::string(species.name()));
265 }
266 return symbols;
267 }
268
269 const std::set<atomic::Species> &Composition::getRegisteredSpecies() const noexcept {
270 return m_registeredSpecies;
271 }
272
273
274 double Composition::getMassFraction(const std::string& symbol) const {
275 const auto species = getSpecies(symbol);
276 if (!species) {
277 throw_unknown_symbol(getLogger(), symbol);
278 }
279 return getMassFraction(species.value());
280 }
281
283 const atomic::Species &species
284 ) const {
285 if (!m_molarAbundances.contains(species)) {
286 throw_unregistered_symbol(getLogger(), std::string(species.name()));
287 }
288 std::map<atomic::Species, double> raw_mass;
289 double totalMass = 0;
290 for (const auto& [sp, y] : m_molarAbundances) {
291 const double contrib = y * sp.mass();
292 totalMass += contrib;
293 raw_mass.emplace(sp, contrib);
294 }
295 return raw_mass.at(species) / totalMass;
296 }
297
298 std::unordered_map<atomic::Species, double> Composition::getMassFraction() const noexcept {
299 std::unordered_map<atomic::Species, double> mass_fractions;
300 for (const auto &species: m_molarAbundances | std::views::keys) {
301 mass_fractions.emplace(species, getMassFraction(species));
302 }
303 return mass_fractions;
304 }
305
306
308 const std::string& symbol
309 ) const {
310 const auto species = getSpecies(symbol);
311 if (!species) {
312 throw_unknown_symbol(getLogger(), symbol);
313 }
314 return getNumberFraction(species.value());
315 }
316
318 const atomic::Species &species
319 ) const {
320 if (!m_molarAbundances.contains(species)) {
321 throw_unregistered_symbol(getLogger(), std::string(species.name()));
322 }
323 double total_moles_per_gram = 0.0;
324 for (const auto &y: m_molarAbundances | std::views::values) {
325 total_moles_per_gram += y;
326 }
327 return m_molarAbundances.at(species) / total_moles_per_gram;
328 }
329
330 std::unordered_map<atomic::Species, double> Composition::getNumberFraction() const noexcept {
331 std::unordered_map<atomic::Species, double> number_fractions;
332 for (const auto &species: m_molarAbundances | std::views::keys) {
333 number_fractions.emplace(species, getNumberFraction(species));
334 }
335 return number_fractions;
336 }
337
339 const std::string &symbol
340 ) const {
341 const auto species = getSpecies(symbol);
342 if (!species) {
343 throw_unknown_symbol(getLogger(), symbol);
344 }
345 return getMolarAbundance(species.value());
346
347 }
348
350 const atomic::Species &species
351 ) const {
352 if (!m_molarAbundances.contains(species)) {
353 throw_unregistered_symbol(getLogger(), std::string(species.name()));
354 }
355 return m_molarAbundances.at(species);
356 }
357
358 double Composition::getMeanParticleMass() const noexcept {
359 std::vector<double> X = getMassFractionVector();
360 double sum = 0.0;
361 for (const auto& [species, x] : std::views::zip(m_registeredSpecies, X)) {
362 sum += x/species.mass();
363 }
364
365 return 1.0 / sum;
366 }
367
368 double Composition::getElectronAbundance() const noexcept {
369 double Ye = 0.0;
370 for (const auto& [species, y] : m_molarAbundances) {
371 Ye += species.z() * y;
372 }
373 return Ye;
374 }
375
376
378 ) const {
379 using namespace fourdst::atomic;
380
381 if (m_cache.canonicalComp.has_value()) {
382 return m_cache.canonicalComp.value(); // Short circuit if we have cached the canonical composition
383 }
384 CanonicalComposition canonicalComposition;
385 const std::set<Species> canonicalH = {H_1, H_2, H_3, H_4, H_5, H_6, H_7};
386 const std::set<Species> canonicalHe = {He_3, He_4, He_5, He_6, He_7, He_8, He_9, He_10};
387
388 for (const auto& symbol : canonicalH) {
389 if (contains(symbol)) {
390 canonicalComposition.X += getMassFraction(symbol);
391 }
392 }
393 for (const auto& symbol : canonicalHe) {
394 if (contains(symbol)) {
395 canonicalComposition.Y += getMassFraction(symbol);
396 }
397 }
398
399 for (const auto& species : m_molarAbundances | std::views::keys) {
400 const bool isHIsotope = canonicalH.contains(species);
401 const bool isHeIsotope = canonicalHe.contains(species);
402
403 if (isHIsotope || isHeIsotope) {
404 continue; // Skip canonical H and He symbols
405 }
406
407 canonicalComposition.Z += getMassFraction(species);
408 }
409
410 // ReSharper disable once CppTooWideScopeInitStatement
411 const double Z = 1.0 - (canonicalComposition.X + canonicalComposition.Y);
412 if (std::abs(Z - canonicalComposition.Z) > 1e-16) {
413 LOG_ERROR(getLogger(), "Validation composition Z (X-Y = {}) is different than canonical composition Z ({}) (∑a_i where a_i != H/He).", Z, canonicalComposition.Z);
414 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).");
415 }
416 m_cache.canonicalComp = canonicalComposition;
417 return canonicalComposition;
418 }
419
420 std::vector<double> Composition::getMassFractionVector() const noexcept {
421 if (m_cache.massFractions.has_value()) {
422 return m_cache.massFractions.value(); // Short circuit if we have cached the mass fractions
423 }
424
425 std::vector<double> massFractionVector;
426 std::vector<double> speciesMass;
427
428 massFractionVector.reserve(m_molarAbundances.size());
429 speciesMass.reserve(m_molarAbundances.size());
430
431 for (const auto &species: m_molarAbundances | std::views::keys) {
432 massFractionVector.push_back(getMassFraction(species));
433 speciesMass.push_back(species.mass());
434 }
435
436 std::vector<double> massFractions = sortVectorBy(massFractionVector, speciesMass);
437 m_cache.massFractions = massFractions; // Cache the result
438 return massFractions;
439
440 }
441
442 std::vector<double> Composition::getNumberFractionVector() const noexcept {
443 if (m_cache.numberFractions.has_value()) {
444 return m_cache.numberFractions.value(); // Short circuit if we have cached the number fractions
445 }
446
447 std::vector<double> numberFractionVector;
448 std::vector<double> speciesMass;
449
450 numberFractionVector.reserve(m_molarAbundances.size());
451 speciesMass.reserve(m_molarAbundances.size());
452
453 for (const auto &species: m_molarAbundances | std::views::keys) {
454 numberFractionVector.push_back(getNumberFraction(species));
455 speciesMass.push_back(species.mass());
456 }
457
458 std::vector<double> numberFractions = sortVectorBy(numberFractionVector, speciesMass);
459 m_cache.numberFractions = numberFractions; // Cache the result
460 return numberFractions;
461 }
462
463 std::vector<double> Composition::getMolarAbundanceVector() const noexcept {
464 if (m_cache.molarAbundances.has_value()) {
465 return m_cache.molarAbundances.value(); // Short circuit if we have cached the molar abundances
466 }
467
468 std::vector<double> molarAbundanceVector;
469 std::vector<double> speciesMass;
470
471 molarAbundanceVector.reserve(m_molarAbundances.size());
472 speciesMass.reserve(m_molarAbundances.size());
473
474 for (const auto &[species, y]: m_molarAbundances) {
475 molarAbundanceVector.push_back(y);
476 speciesMass.push_back(species.mass());
477 }
478
479 std::vector<double> molarAbundances = sortVectorBy(molarAbundanceVector, speciesMass);
480 m_cache.molarAbundances = molarAbundances; // Cache the result
481 return molarAbundances;
482
483 }
484
486 const std::string &symbol
487 ) const {
488 const auto species = getSpecies(symbol);
489 if (!species) {
490 throw_unknown_symbol(getLogger(), symbol);
491 }
492
493 return getSpeciesIndex(species.value());
494 }
495
497 const atomic::Species &species
498 ) const {
499 if (!m_registeredSpecies.contains(species)) {
500 LOG_ERROR(getLogger(), "Species {} is not in the composition.", species.name());
501 throw exceptions::UnregisteredSymbolError("Species " + std::string(species.name()) + " is not in the composition.");
502 }
503 if (m_cache.sortedSpecies.has_value()) {
504 return std::distance(
505 m_cache.sortedSpecies->begin(),
506 std::ranges::find(
507 m_cache.sortedSpecies.value().begin(),
508 m_cache.sortedSpecies.value().end(),
509 species
510 )
511 );
512 }
513
514 std::vector<atomic::Species> speciesVector;
515 std::vector<double> speciesMass;
516
517 speciesVector.reserve(m_molarAbundances.size());
518 speciesMass.reserve(m_molarAbundances.size());
519
520 for (const auto &s: m_registeredSpecies) {
521 speciesVector.emplace_back(s);
522 speciesMass.push_back(s.mass());
523 }
524
525 std::vector<atomic::Species> sortedSpecies = sortVectorBy(speciesVector, speciesMass);
526 m_cache.sortedSpecies = sortedSpecies;
527 return std::distance(sortedSpecies.begin(), std::ranges::find(sortedSpecies, species));
528 }
529
531 const size_t index
532 ) const {
533 if (m_cache.sortedSpecies.has_value()) {
534 return m_cache.sortedSpecies.value().at(index);
535 }
536
537 std::vector<atomic::Species> speciesVector;
538 std::vector<double> speciesMass;
539
540 speciesVector.reserve(m_molarAbundances.size());
541 speciesMass.reserve(m_molarAbundances.size());
542
543 for (const auto &species: m_registeredSpecies) {
544 speciesVector.emplace_back(species);
545 speciesMass.push_back(species.mass());
546 }
547
548 std::vector<atomic::Species> sortedSymbols = sortVectorBy(speciesVector, speciesMass);
549 if (index >= sortedSymbols.size()) {
550 LOG_ERROR(getLogger(), "Index {} is out of range for composition of size {}.", index, sortedSymbols.size());
551 throw std::out_of_range("Index " + std::to_string(index) + " is out of range for composition of size " + std::to_string(sortedSymbols.size()) + ".");
552 }
553 return sortedSymbols.at(index);
554 }
555
556 std::unique_ptr<CompositionAbstract> Composition::clone() const {
557 return std::make_unique<Composition>(*this);
558 }
559
561 const atomic::Species &species
562 ) const noexcept {
563 return m_registeredSpecies.contains(species);
564 }
565
567 const std::string &symbol
568 ) const {
569 const auto species = getSpecies(symbol);
570 if (!species) {
571 throw_unknown_symbol(getLogger(), symbol);
572 }
573 return contains(species.value());
574 }
575
576 size_t Composition::size() const noexcept {
577 return m_registeredSpecies.size();
578 }
579
581 const std::string &symbol,
582 const double &molar_abundance
583 ) {
584 const auto species = getSpecies(symbol);
585 if (!species) {
586 throw_unknown_symbol(getLogger(), symbol);
587 }
588
589 setMolarAbundance(species.value(), molar_abundance);
590 }
591
593 const atomic::Species &species,
594 const double &molar_abundance
595 ) {
596 if (!m_registeredSpecies.contains(species)) {
597 throw_unregistered_symbol(getLogger(), std::string(species.name()));
598 }
599 if (molar_abundance < 0.0) {
600 LOG_ERROR(getLogger(), "Molar abundance must be non-negative for symbol {}. Currently it is {}.", species.name(), molar_abundance);
601 throw exceptions::InvalidCompositionError("Molar abundance must be non-negative, got " + std::to_string(molar_abundance) + " for symbol " + std::string(species.name()) + ".");
602 }
603 m_molarAbundances.at(species) = molar_abundance;
604 }
605
607 const std::vector<std::string> &symbols,
608 const std::vector<double> &molar_abundances
609 ) {
610 for (const auto& [symbol, y] : std::views::zip(symbols, molar_abundances)) {
611 setMolarAbundance(symbol, y);
612 }
613 }
614
616 const std::vector<atomic::Species> &species,
617 const std::vector<double> &molar_abundances
618 ) {
619 for (const auto& [s, y] : std::views::zip(species, molar_abundances)) {
620 setMolarAbundance(s, y);
621 }
622 }
623
625 const std::set<std::string> &symbols,
626 const std::vector<double> &molar_abundances
627 ) {
628 for (const auto& [symbol, y] : std::views::zip(symbols, molar_abundances)) {
629 setMolarAbundance(symbol, y);
630 }
631 }
632
634 const std::set<atomic::Species> &species,
635 const std::vector<double> &molar_abundances
636 ) {
637 for (const auto& [s, y] : std::views::zip(species, molar_abundances)) {
638 setMolarAbundance(s, y);
639 }
640 }
641
643
644 std::ostream& operator<<(
645 std::ostream& os,
646 const Composition& composition
647 ) {
648 os << "Composition(Mass Fractions => [";
649 size_t count = 0;
650 for (const auto &species : composition.m_registeredSpecies) {
651 os << species << ": " << composition.getMassFraction(species);
652 if (count < composition.size() - 1) {
653 os << ", ";
654 }
655 count++;
656 }
657 os << "])";
658 return os;
659 }
660
661} // namespace fourdst::composition
Abstract base class for chemical composition representations.
virtual double getMolarAbundance(const std::string &symbol) const =0
Get the molar abundance for a given symbol.
virtual const std::set< fourdst::atomic::Species > & getRegisteredSpecies() const noexcept=0
Get all registered atomic species in the composition.
Manages a collection of chemical species and their abundances.
Definition composition.h:98
CompositionCache m_cache
Cache for computed properties to avoid redundant calculations.
size_t getSpeciesIndex(const std::string &symbol) const override
get the index in the sorted vector representation for a given symbol
bool contains(const atomic::Species &species) const noexcept override
Checks if a given species is present in the composition.
std::unordered_map< atomic::Species, double > getNumberFraction() const noexcept override
Gets the number fractions of all species in the composition.
Composition()=default
Default constructor.
void setMolarAbundance(const std::string &symbol, const double &molar_abundance)
Sets the molar abundance for a given symbol.
const std::set< atomic::Species > & getRegisteredSpecies() const noexcept override
Get a set of all species that are registered in the composition.
void registerSpecies(const atomic::Species &species) noexcept
Registers a new species by extracting its symbol.
void registerSymbol(const std::string &symbol)
Registers a new symbol for inclusion in the composition.
std::set< std::string > getRegisteredSymbols() const noexcept override
Gets the registered symbols.
std::set< atomic::Species > m_registeredSpecies
Set of registered species in the composition.
static quill::Logger * getLogger()
Gets the logger instance for the Composition class. This is static to ensure that all composition obj...
Composition & operator=(Composition const &other)
Assignment operator.
std::unique_ptr< CompositionAbstract > clone() const override
double getElectronAbundance() const noexcept override
Compute the electron abundance of the composition.
size_t size() const noexcept override
Gets the number of registered species in the composition.
std::unordered_map< atomic::Species, double > getMassFraction() const noexcept override
Gets the mass fractions of all species in the composition.
std::map< atomic::Species, double > m_molarAbundances
Map of species to their molar abundances.
CanonicalComposition getCanonicalComposition() const
Compute the canonical composition (X, Y, Z) of the composition.
std::vector< double > getMolarAbundanceVector() const noexcept override
Get a uniform vector representation of the molar abundances stored in the composition object sorted s...
double getMolarAbundance(const std::string &symbol) const override
Gets the molar abundances of all species in the composition.
std::vector< double > getNumberFractionVector() const noexcept override
Get a uniform vector representation of the number fractions stored in the composition object sorted s...
atomic::Species getSpeciesAtIndex(size_t index) const override
Get the species at a given index in the sorted vector representation.
std::vector< double > getMassFractionVector() const noexcept override
Get a uniform vector representation of the mass fraction stored in the composition object sorted such...
double getMeanParticleMass() const noexcept override
Compute the mean particle mass of the composition.
Exception thrown when a composition is in an invalid or inconsistent state.
Exception thrown when an unknown symbol is encountered.
Exception thrown when a symbol is used that has not been registered.
Contains canonical information about atomic species and elements used by 4D-STAR.
static const std::unordered_map< std::string, const Species & > species
Map of species names to their corresponding Species objects.
Definition species.h:3579
Utilities and types for representing and manipulating chemical compositions.
std::ostream & operator<<(std::ostream &os, const Composition &composition)
OVERLOADS.
Represents an atomic species (isotope) with its fundamental physical properties.
std::string_view name() const
Gets the name of the species.
Represents the canonical (X, Y, Z) composition of stellar material.
Definition composition.h:45
double Y
Mass fraction of Helium.
Definition composition.h:47
double X
Mass fraction of Hydrogen.
Definition composition.h:46
double Z
Mass fraction of Metals.
Definition composition.h:48
std::optional< std::vector< atomic::Species > > sortedSpecies
Cached vector of sorted species (by mass).
std::optional< std::vector< double > > numberFractions
Cached vector of number fractions.
std::optional< CanonicalComposition > canonicalComp
Cached canonical composition data.
std::optional< std::vector< double > > molarAbundances
Cached vector of molar abundances.
std::optional< std::vector< double > > massFractions
Cached vector of mass fractions.