SERiF 0.0.1a
3+1D Stellar Structure and Evolution
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: March 26, 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 "composition.h"
22#include "const.h"
23#include "quill/LogMacros.h"
24
25#include <stdexcept>
26#include <unordered_map>
27#include <vector>
28#include <array>
29#include <ranges>
30
31#include <utility>
32
33#include "atomicSpecies.h"
34#include "species.h"
35
37
38 CompositionEntry::CompositionEntry() : m_symbol("H-1"), m_isotope(serif::atomic::species.at("H-1")),
39 m_initialized(false) {
40 }
41
42 CompositionEntry::CompositionEntry(const std::string& symbol, const bool massFracMode) : m_symbol(symbol), m_isotope(serif::atomic::species.at(symbol)), m_massFracMode(massFracMode) {
44 }
45
54
55 void CompositionEntry::setSpecies(const std::string& symbol) {
56 if (m_initialized) {
57 throw std::runtime_error("Composition entry is already initialized.");
58 }
59 if (serif::atomic::species.count(symbol) == 0) {
60 throw std::runtime_error("Invalid symbol.");
61 }
63 m_isotope = serif::atomic::species.at(symbol);
64 m_initialized = true;
65 }
66
67 std::string CompositionEntry::symbol() const {
68 return m_symbol;
69 }
70
72 if (!m_massFracMode) {
73 throw std::runtime_error("Composition entry is in number fraction mode.");
74 }
75 return m_massFraction;
76 }
77
78 double CompositionEntry::mass_fraction(double meanMolarMass) const {
79 if (m_massFracMode) {
80 return m_massFraction;
81 }
82 return m_relAbundance / meanMolarMass;
83 }
84
85
87 if (m_massFracMode) {
88 throw std::runtime_error("Composition entry is in mass fraction mode.");
89 }
90 return m_numberFraction;
91 }
92
93 double CompositionEntry::number_fraction(double totalMoles) const {
94 if (m_massFracMode) {
95 return m_relAbundance / totalMoles;
96 }
97 return m_numberFraction;
98 }
99
101 return m_relAbundance;
102 }
103
104 serif::atomic::Species CompositionEntry::isotope() const {
105 return m_isotope;
106 }
107
109 if (!m_massFracMode) {
110 throw std::runtime_error("Composition entry is in number fraction mode.");
111 }
114 }
115
117 if (m_massFracMode) {
118 throw std::runtime_error("Composition entry is in mass fraction mode.");
119 }
122 }
123
124 bool CompositionEntry::setMassFracMode(double meanParticleMass) {
125 if (m_massFracMode) {
126 return false;
127 }
128 m_massFracMode = true;
129 m_massFraction = m_relAbundance / meanParticleMass;
130 return true;
131 }
132
133 bool CompositionEntry::setNumberFracMode(double specificNumberDensity) {
134 if (!m_massFracMode) {
135 return false;
136 }
137 m_massFracMode = false;
138 m_numberFraction = m_relAbundance / specificNumberDensity;
139 return true;
140 }
141
143 return m_massFracMode;
144 }
145
146 Composition::Composition(const std::vector<std::string>& symbols) {
147 for (const auto& symbol : symbols) {
148 registerSymbol(symbol);
149 }
150 }
151
152 Composition::Composition(const std::set<std::string>& symbols) {
153 for (const auto& symbol : symbols) {
154 registerSymbol(symbol);
155 }
156 }
157
158 Composition::Composition(const std::vector<std::string>& symbols, const std::vector<double>& fractions, bool massFracMode) : m_massFracMode(massFracMode) {
159 if (symbols.size() != fractions.size()) {
160 LOG_ERROR(m_logger, "The number of symbols and fractions must be equal.");
161 throw std::runtime_error("The number of symbols and fractions must be equal.");
162 }
163
164 validateComposition(fractions);
165
166 for (const auto &symbol : symbols) {
167 registerSymbol(symbol);
168 }
169
170 for (size_t i = 0; i < symbols.size(); ++i) {
171 if (m_massFracMode) {
172 setMassFraction(symbols[i], fractions[i]);
173 } else {
174 setNumberFraction(symbols[i], fractions[i]);
175 }
176 }
177 finalize();
178 }
179
181 m_finalized = composition.m_finalized;
182 m_specificNumberDensity = composition.m_specificNumberDensity;
183 m_meanParticleMass = composition.m_meanParticleMass;
184 m_massFracMode = composition.m_massFracMode;
185 m_registeredSymbols = composition.m_registeredSymbols;
186 m_compositions = composition.m_compositions;
187 }
188
190 if (this != &other) {
191 m_finalized = other.m_finalized;
197 // note: m_config remains bound to the same singleton, so we skip it
198 }
199 return *this;
200
201 }
202
203 void Composition::registerSymbol(const std::string& symbol, bool massFracMode) {
204 if (!isValidSymbol(symbol)) {
205 LOG_ERROR(m_logger, "Invalid symbol: {}", symbol);
206 throw std::runtime_error("Invalid symbol.");
207 }
208
209 // If no symbols have been registered allow mode to be set
210 if (m_registeredSymbols.size() == 0) {
211 m_massFracMode = massFracMode;
212 } else {
213 if (m_massFracMode != massFracMode) {
214 LOG_ERROR(m_logger, "Composition is in mass fraction mode. Cannot register symbol in number fraction mode.");
215 throw std::runtime_error("Composition is in mass fraction mode. Cannot register symbol in number fraction mode.");
216 }
217 }
218
219 if (m_registeredSymbols.find(symbol) != m_registeredSymbols.end()) {
220 LOG_WARNING(m_logger, "Symbol {} is already registered.", symbol);
221 return;
222 }
223
224 m_registeredSymbols.insert(symbol);
225 CompositionEntry entry(symbol, m_massFracMode);
226 m_compositions[symbol] = entry;
227 LOG_INFO(m_logger, "Registered symbol: {}", symbol);
228 }
229
230 void Composition::registerSymbol(const std::vector<std::string>& symbols, bool massFracMode) {
231 for (const auto& symbol : symbols) {
232 registerSymbol(symbol, massFracMode);
233 }
234 }
235
236 std::set<std::string> Composition::getRegisteredSymbols() const {
237 return m_registeredSymbols;
238 }
239
240 void Composition::validateComposition(const std::vector<double>& fractions) const {
241 if (!isValidComposition(fractions)) {
242 LOG_ERROR(m_logger, "Invalid composition.");
243 throw std::runtime_error("Invalid composition.");
244 }
245 }
246
247 bool Composition::isValidComposition(const std::vector<double>& fractions) const {
248 double sum = 0.0;
249 for (const auto& fraction : fractions) {
250 sum += fraction;
251 }
252 if (sum < 0.999999 || sum > 1.000001) {
253 LOG_ERROR(m_logger, "The sum of fractions must be equal to 1.");
254 return false;
255 }
256
257 return true;
258 }
259
260 bool Composition::isValidSymbol(const std::string& symbol) {
261 return serif::atomic::species.contains(symbol);
262 }
263
264 double Composition::setMassFraction(const std::string& symbol, const double& mass_fraction) {
265 if (!m_registeredSymbols.contains(symbol)) {
266 LOG_ERROR(m_logger, "Symbol {} is not registered.", symbol);
267 throw std::runtime_error("Symbol is not registered.");
268 }
269
270 if (!m_massFracMode) {
271 LOG_ERROR(m_logger, "Composition is in number fraction mode.");
272 throw std::runtime_error("Composition is in number fraction mode.");
273 }
274
275 if (mass_fraction < 0.0 || mass_fraction > 1.0) {
276 LOG_ERROR(m_logger, "Mass fraction must be between 0 and 1 for symbol {}. Currently it is {}.", symbol, mass_fraction);
277 throw std::runtime_error("Mass fraction must be between 0 and 1.");
278 }
279
280 m_finalized = false;
281 const double old_mass_fraction = m_compositions.at(symbol).mass_fraction();
282 m_compositions.at(symbol).setMassFraction(mass_fraction);
283
284 return old_mass_fraction;
285 }
286
287 std::vector<double> Composition::setMassFraction(const std::vector<std::string>& symbols, const std::vector<double>& mass_fractions) {
288 if (symbols.size() != mass_fractions.size()) {
289 LOG_ERROR(m_logger, "The number of symbols and mass fractions must be equal.");
290 throw std::runtime_error("The number of symbols and mass fractions must be equal.");
291 }
292
293 std::vector<double> old_mass_fractions;
294 old_mass_fractions.reserve(symbols.size());
295 for (size_t i = 0; i < symbols.size(); ++i) {
296 old_mass_fractions.push_back(setMassFraction(symbols[i], mass_fractions[i]));
297 }
298 return old_mass_fractions;
299 }
300
301 double Composition::setNumberFraction(const std::string& symbol, const double& number_fraction) {
302 if (m_registeredSymbols.find(symbol) == m_registeredSymbols.end()) {
303 LOG_ERROR(m_logger, "Symbol {} is not registered.", symbol);
304 throw std::runtime_error("Symbol is not registered.");
305 }
306
307 if (m_massFracMode) {
308 LOG_ERROR(m_logger, "Composition is in mass fraction mode.");
309 throw std::runtime_error("Composition is in mass fraction mode.");
310 }
311
312 if (number_fraction < 0.0 || number_fraction > 1.0) {
313 LOG_ERROR(m_logger, "Number fraction must be between 0 and 1 for symbol {}. Currently it is {}.", symbol, number_fraction);
314 throw std::runtime_error("Number fraction must be between 0 and 1.");
315 }
316
317 m_finalized = false;
318 double old_number_fraction = m_compositions.at(symbol).number_fraction();
319 m_compositions.at(symbol).setNumberFraction(number_fraction);
320
321 return old_number_fraction;
322 }
323
324 std::vector<double> Composition::setNumberFraction(const std::vector<std::string>& symbols, const std::vector<double>& number_fractions) {
325 if (symbols.size() != number_fractions.size()) {
326 LOG_ERROR(m_logger, "The number of symbols and number fractions must be equal.");
327 throw std::runtime_error("The number of symbols and number fractions must be equal.");
328 }
329
330 std::vector<double> old_number_fractions;
331 old_number_fractions.reserve(symbols.size());
332 for (size_t i = 0; i < symbols.size(); ++i) {
333 old_number_fractions.push_back(setNumberFraction(symbols[i], number_fractions[i]));
334 }
335 return old_number_fractions;
336 }
337
338 bool Composition::finalize(const bool norm) {
339 bool finalized = false;
340 if (m_massFracMode) {
341 finalized = finalizeMassFracMode(norm);
342 } else {
343 finalized = finalizeNumberFracMode(norm);
344 }
345 if (finalized) {
346 m_finalized = true;
347 }
348 return finalized;
349 }
350
352 std::vector<double> mass_fractions;
353 mass_fractions.reserve(m_compositions.size());
354 for (const auto& [_, entry] : m_compositions) {
355 mass_fractions.push_back(entry.mass_fraction());
356 }
357 if (norm) {
358 double sum = 0.0;
359 for (const auto& mass_fraction : mass_fractions) {
360 sum += mass_fraction;
361 }
362 for (int i = 0; i < static_cast<int>(mass_fractions.size()); ++i) {
363 mass_fractions[i] /= sum;
364 }
365 for (auto& [symbol, entry] : m_compositions) {
366 setMassFraction(symbol, entry.mass_fraction() / sum);
367 }
368 }
369 try {
370 validateComposition(mass_fractions);
371 } catch (const std::runtime_error& e) {
372 double massSum = 0.0;
373 for (const auto& [_, entry] : m_compositions) {
374 massSum += entry.mass_fraction();
375 }
376 LOG_ERROR(m_logger, "Composition is invalid (Total mass {}).", massSum);
377 m_finalized = false;
378 return false;
379 }
380 for (const auto& [_, entry] : m_compositions) {
381 m_specificNumberDensity += entry.rel_abundance();
382 }
384 return true;
385 }
386
388 std::vector<double> number_fractions;
389 number_fractions.reserve(m_compositions.size());
390 for (const auto& [_, entry] : m_compositions) {
391 number_fractions.push_back(entry.number_fraction());
392 }
393 if (norm) {
394 double sum = 0.0;
395 for (const auto& number_fraction : number_fractions) {
396 sum += number_fraction;
397 }
398 for (auto& [symbol, entry] : m_compositions) {
399 setNumberFraction(symbol, entry.number_fraction() / sum);
400 }
401 }
402 try {
403 validateComposition(number_fractions);
404 } catch (const std::runtime_error& e) {
405 double numberSum = 0.0;
406 for (const auto& [_, entry] : m_compositions) {
407 numberSum += entry.number_fraction();
408 }
409 LOG_ERROR(m_logger, "Composition is invalid (Total number {}).", numberSum);
410 m_finalized = false;
411 return false;
412 }
413 for (const auto& [_, entry] : m_compositions) {
414 m_meanParticleMass += entry.rel_abundance();
415 }
417 return true;
418 }
419
420 Composition Composition::mix(const Composition& other, double fraction) const {
421 if (!m_finalized || !other.m_finalized) {
422 LOG_ERROR(m_logger, "Compositions have not both been finalized.");
423 throw std::runtime_error("Compositions have not been finalized (Consider running .finalize()).");
424 }
425
426 if (fraction < 0.0 || fraction > 1.0) {
427 LOG_ERROR(m_logger, "Fraction must be between 0 and 1.");
428 throw std::runtime_error("Fraction must be between 0 and 1.");
429 }
430
431 std::set<std::string> mixedSymbols = other.getRegisteredSymbols();
432 // Get the union of the two sets
433 mixedSymbols.insert(m_registeredSymbols.begin(), m_registeredSymbols.end());
434
435 Composition mixedComposition(mixedSymbols);
436 for (const auto& symbol : mixedSymbols) {
437 double otherMassFrac = 0.0;
438
439 const double thisMassFrac = hasSymbol(symbol) ? getMassFraction(symbol) : 0.0;
440 otherMassFrac = other.hasSymbol(symbol) ? other.getMassFraction(symbol) : 0.0;
441
442 double massFraction = fraction * thisMassFrac + otherMassFrac * (1-fraction);
443 mixedComposition.setMassFraction(symbol, massFraction);
444 }
445 mixedComposition.finalize();
446 return mixedComposition;
447 }
448
449 double Composition::getMassFraction(const std::string& symbol) const {
450 if (!m_finalized) {
451 LOG_ERROR(m_logger, "Composition has not been finalized.");
452 throw std::runtime_error("Composition has not been finalized (Consider running .finalize()).");
453 }
454 if (!m_compositions.contains(symbol)) {
455 LOG_ERROR(m_logger, "Symbol {} is not in the composition.", symbol);
456 throw std::runtime_error("Symbol is not in the composition.");
457 }
458 if (m_massFracMode) {
459 return m_compositions.at(symbol).mass_fraction();
460 } else {
461 return m_compositions.at(symbol).mass_fraction(m_meanParticleMass);
462 }
463 }
464
465 std::unordered_map<std::string, double> Composition::getMassFraction() const {
466 std::unordered_map<std::string, double> mass_fractions;
467 for (const auto &symbol: m_compositions | std::views::keys) {
468 mass_fractions[symbol] = getMassFraction(symbol);
469 }
470 return mass_fractions;
471 }
472
473
474 double Composition::getNumberFraction(const std::string& symbol) const {
475 if (!m_finalized) {
476 LOG_ERROR(m_logger, "Composition has not been finalized.");
477 throw std::runtime_error("Composition has not been finalized (Consider running .finalize()).");
478 }
479 if (!m_compositions.contains(symbol)) {
480 LOG_ERROR(m_logger, "Symbol {} is not in the composition.", symbol);
481 throw std::runtime_error("Symbol is not in the composition.");
482 }
483 if (!m_massFracMode) {
484 return m_compositions.at(symbol).number_fraction();
485 } else {
486 return m_compositions.at(symbol).number_fraction(m_specificNumberDensity);
487 }
488 }
489
490 std::unordered_map<std::string, double> Composition::getNumberFraction() const {
491 std::unordered_map<std::string, double> number_fractions;
492 for (const auto &symbol: m_compositions | std::views::keys) {
493 number_fractions[symbol] = getNumberFraction(symbol);
494 }
495 return number_fractions;
496 }
497
498 std::pair<CompositionEntry, GlobalComposition> Composition::getComposition(const std::string& symbol) const {
499 if (!m_finalized) {
500 LOG_ERROR(m_logger, "Composition has not been finalized.");
501 throw std::runtime_error("Composition has not been finalized (Consider running .finalize()).");
502 }
503 if (!m_compositions.contains(symbol)) {
504 LOG_ERROR(m_logger, "Symbol {} is not in the composition.", symbol);
505 throw std::runtime_error("Symbol is not in the composition.");
506 }
508 }
509
510 std::pair<std::unordered_map<std::string, CompositionEntry>, GlobalComposition> Composition::getComposition() const {
511 if (!m_finalized) {
512 LOG_ERROR(m_logger, "Composition has not been finalized.");
513 throw std::runtime_error("Composition has not been finalized (Consider running .finalize()).");
514 }
516 }
517
519 if (!m_finalized) {
520 LOG_ERROR(m_logger, "Composition has not been finalized.");
521 throw std::runtime_error("Composition has not been finalized (Consider running .finalize()).");
522 }
523 return m_meanParticleMass;
524 }
525
527 if (!m_finalized) {
528 LOG_ERROR(m_logger, "Composition must be finalized before getting the mean atomic mass number.");
529 throw std::runtime_error("Composition not finalized. Cannot retrieve mean atomic mass number.");
530 }
531
532 double zSum = 0.0;
533
534 // Loop through all registered species in the composition.
535 for (const auto &val: m_compositions | std::views::values) {
536 zSum += (val.mass_fraction() * val.m_isotope.z())/val.m_isotope.a();
537 }
538
539 const double mean_A = m_meanParticleMass * zSum;
540 return mean_A;
541 }
542
543 Composition Composition::subset(const std::vector<std::string>& symbols, std::string method) const {
544 const std::array<std::string, 2> methods = {"norm", "none"};
545
546 if (std::ranges::find(methods, method) == methods.end()) {
547 const std::string errorMessage = "Invalid method: " + method + ". Valid methods are 'norm' and 'none'.";
548 LOG_ERROR(m_logger, "Invalid method: {}. Valid methods are norm and none.", method);
549 throw std::runtime_error(errorMessage);
550 }
551
552 Composition subsetComposition;
553 for (const auto& symbol : symbols) {
554 if (!m_compositions.contains(symbol)) {
555 LOG_ERROR(m_logger, "Symbol {} is not in the composition.", symbol);
556 throw std::runtime_error("Symbol is not in the composition.");
557 } else {
558 subsetComposition.registerSymbol(symbol);
559 }
560 subsetComposition.setMassFraction(symbol, m_compositions.at(symbol).mass_fraction());
561 }
562 if (method == "norm") {
563 const bool isNorm = subsetComposition.finalize(true);
564 if (!isNorm) {
565 LOG_ERROR(m_logger, "Subset composition is invalid.");
566 throw std::runtime_error("Subset composition is invalid.");
567 }
568 }
569 return subsetComposition;
570 }
571
572 void Composition::setCompositionMode(const bool massFracMode) {
573 if (!m_finalized) {
574 LOG_ERROR(m_logger, "Composition has not been finalized. Mode cannot be set unless composition is finalized.");
575 throw std::runtime_error("Composition has not been finalized (Consider running .finalize()). The mode cannot be set unless the composition is finalized.");
576 }
577
578 bool okay = true;
579 for (auto &entry: m_compositions | std::views::values) {
580 if (massFracMode) {
581 okay = entry.setMassFracMode(m_meanParticleMass);
582 } else {
583 okay = entry.setNumberFracMode(m_specificNumberDensity);
584 }
585 if (!okay) {
586 LOG_ERROR(m_logger, "Composition mode could not be set.");
587 throw std::runtime_error("Composition mode could not be set due to an unknown error.");
588 }
589 }
590 m_massFracMode = massFracMode;
591 }
592
594 if (!m_finalized) {
595 LOG_ERROR(m_logger, "Composition has not been finalized.");
596 throw std::runtime_error("Composition has not been finalized (Consider running .finalize()).");
597 }
598 CanonicalComposition canonicalComposition;
599 const std::array<std::string, 7> canonicalH = {
600 "H-1", "H-2", "H-3", "H-4", "H-5", "H-6", "H-7"
601 };
602 const std::array<std::string, 8> canonicalHe = {
603 "He-3", "He-4", "He-5", "He-6", "He-7", "He-8", "He-9", "He-10"
604 };
605 for (const auto& symbol : canonicalH) {
606 if (hasSymbol(symbol)) {
607 canonicalComposition.X += getMassFraction(symbol);
608 }
609 }
610 for (const auto& symbol : canonicalHe) {
611 if (hasSymbol(symbol)) {
612 canonicalComposition.Y += getMassFraction(symbol);
613 }
614 }
615
616 for (const auto& symbol : getRegisteredSymbols()) {
617 const bool isHSymbol = std::ranges::find(canonicalH, symbol) != std::end(canonicalH);
618 const bool isHeSymbol = std::ranges::find(canonicalHe, symbol) != std::end(canonicalHe);
619
620 if (isHSymbol || isHeSymbol) {
621 continue; // Skip canonical H and He symbols
622 }
623
624 canonicalComposition.Z += getMassFraction(symbol);
625 }
626
627 const double Z = 1.0 - (canonicalComposition.X + canonicalComposition.Y);
628 if (std::abs(Z - canonicalComposition.Z) > 1e-6) {
629 if (!harsh) {
630 LOG_WARNING(m_logger, "Validation composition Z (X-Y = {}) is different than canonical composition Z ({}) (∑a_i where a_i != H/He).", Z, canonicalComposition.Z);
631 }
632 else {
633 LOG_ERROR(m_logger, "Validation composition Z (X-Y = {}) is different than canonical composition Z ({}) (∑a_i where a_i != H/He).", Z, canonicalComposition.Z);
634 throw std::runtime_error("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).");
635 }
636 }
637 return canonicalComposition;
638 }
639
640 bool Composition::hasSymbol(const std::string& symbol) const {
641 return m_compositions.count(symbol) > 0;
642 }
643
644 bool Composition::contains(const serif::atomic::Species &isotope) const {
645 // Check if the isotope's symbol is in the composition
646 if (!m_finalized) {
647 LOG_ERROR(m_logger, "Composition has not been finalized.");
648 throw std::runtime_error("Composition has not been finalized (Consider running .finalize()).");
649 }
650 const auto symbol = static_cast<std::string>(isotope.name());
651 if (m_compositions.contains(symbol)) {
652 return true;
653 }
654 return false;
655 }
656
658
660 return mix(other, 0.5);
661 }
662
663 std::ostream& operator<<(std::ostream& os, const GlobalComposition& comp) {
664 os << "Global Composition: \n";
665 os << "\tSpecific Number Density: " << comp.specificNumberDensity << "\n";
666 os << "\tMean Particle Mass: " << comp.meanParticleMass << "\n";
667 return os;
668 }
669
670 std::ostream& operator<<(std::ostream& os, const CompositionEntry& entry) {
671 os << "<" << entry.m_symbol << " : m_frac = " << entry.mass_fraction() << ">";
672 return os;
673 }
674
675 std::ostream& operator<<(std::ostream& os, const Composition& composition) {
676 os << "Composition(finalized: " << (composition.m_finalized ? "true" : "false") << ", " ;
677 int count = 0;
678 for (const auto& [symbol, entry] : composition.m_compositions) {
679 os << entry;
680 if (count < composition.m_compositions.size() - 1) {
681 os << ", ";
682 }
683 }
684 os << ")";
685 return os;
686 }
687
688} // namespace serif::composition
bool hasSymbol(const std::string &symbol) const
Check if a symbol is registered.
std::unordered_map< std::string, double > getMassFraction() const
Gets the mass fractions of all compositions.
bool isValidComposition(const std::vector< double > &fractions) const
Checks if the given mass fractions are valid.
double getMeanAtomicNumber() const
Compute the mean atomic mass number of the composition.
bool contains(const serif::atomic::Species &isotope) const
double m_meanParticleMass
The mean particle mass of the composition (\sum_{i} \frac{n_i}{m_i}. where n_i is the number fraction...
bool finalizeMassFracMode(bool norm)
Finalizes the composition in mass fraction mode.
std::set< std::string > getRegisteredSymbols() const
Gets the registered symbols.
Composition()=default
Default constructor.
Composition mix(const Composition &other, double fraction) const
Mix two compositions together with a given fraction.
double m_specificNumberDensity
The specific number density of the composition (\sum_{i} X_i m_i. Where X_i is the number fraction of...
void registerSymbol(const std::string &symbol, bool massFracMode=true)
Registers a new symbol.
std::set< std::string > m_registeredSymbols
The registered symbols.
Composition subset(const std::vector< std::string > &symbols, std::string method="norm") const
Gets a subset of the composition.
std::unordered_map< std::string, double > getNumberFraction() const
Gets the number fractions of all compositions.
bool finalize(bool norm=false)
Finalizes the composition.
double setMassFraction(const std::string &symbol, const double &mass_fraction)
Sets the mass fraction for a given symbol.
bool m_finalized
True if the composition is finalized.
bool finalizeNumberFracMode(bool norm)
Finalizes the composition in number fraction mode.
std::pair< std::unordered_map< std::string, CompositionEntry >, GlobalComposition > getComposition() const
Gets all composition entries and the global composition.
Composition & operator=(Composition const &other)
Composition operator+(const Composition &other) const
Overloads the + operator to mix two compositions together with a fraction of 0.5.
bool m_massFracMode
True if mass fraction mode, false if number fraction mode.
CanonicalComposition getCanonicalComposition(bool harsh=false) const
Gets the current canonical composition (X, Y, Z).
double getMeanParticleMass() const
Compute the mean particle mass of the composition.
std::unordered_map< std::string, CompositionEntry > m_compositions
The compositions.
double setNumberFraction(const std::string &symbol, const double &number_fraction)
Sets the number fraction for a given symbol.
void validateComposition(const std::vector< double > &fractions) const
Validates the given mass fractions.
void setCompositionMode(bool massFracMode)
Sets the composition mode.
static bool isValidSymbol(const std::string &symbol)
Checks if the given symbol is valid.
std::ostream & operator<<(std::ostream &os, const GlobalComposition &comp)
double X
Mass fraction of Hydrogen.
Definition composition.h:37
double Z
Mass fraction of Metals.
Definition composition.h:39
double Y
Mass fraction of Helium.
Definition composition.h:38
Represents an entry in the composition with a symbol and mass fraction.
Definition composition.h:64
std::string symbol() const
Gets the chemical symbol of the species.
serif::atomic::Species isotope() const
Gets the isotope of the species.
double rel_abundance() const
Gets the relative abundance of the species.
bool setNumberFracMode(double totalMoles)
Sets the mode to number fraction mode.
void setNumberFraction(double number_fraction)
Sets the number fraction of the species.
bool m_massFracMode
The mode of the composition entry. True if mass fraction, false if number fraction.
Definition composition.h:67
void setMassFraction(double mass_fraction)
Sets the mass fraction of the species.
serif::atomic::Species m_isotope
The isotope of the species.
Definition composition.h:66
double m_numberFraction
The number fraction of the species.
Definition composition.h:70
void setSpecies(const std::string &symbol)
Sets the species for the composition entry.
double number_fraction() const
Gets the number fraction of the species.
double mass_fraction() const
Gets the mass fraction of the species.
double m_relAbundance
The relative abundance of the species for converting between mass and number fractions.
Definition composition.h:71
bool setMassFracMode(double meanMolarMass)
Sets the mode to mass fraction mode.
std::string m_symbol
The chemical symbol of the species.
Definition composition.h:65
CompositionEntry()
Default constructor.
double m_massFraction
The mass fraction of the species.
Definition composition.h:69
bool m_initialized
True if the composition entry has been initialized.
Definition composition.h:73
bool getMassFracMode() const
Gets the mode of the composition entry.
Represents the global composition of a system. This tends to be used after finalize and is primarily ...
Definition composition.h:53