#include "fourdst/composition/io/standard_compositions.h" #include "fourdst/composition/io/StandardMetalFractionsBinary.h" #include "fourdst/composition/composition.h" #include "fourdst/atomic/atomicSpecies.h" #include "fourdst/atomic/species.h" #include "fourdst/composition/utils.h" #include #include #include #include #include #include #include #include #include #include namespace fourdst:: composition::io { namespace { inline void ltrim(std::string &s) { s.erase( s.begin(), std::ranges::find_if(s, [](const unsigned char ch) { return !std::isspace(ch); }) ); } inline void rtrim(std::string &s) { s.erase( std::find_if( s.rbegin(), s.rend(), [](const unsigned char ch) { return !std::isspace(ch); }).base(), s.end() ); } inline void trim(std::string &s) { ltrim(s); rtrim(s); } } bool to_bool(std::string s) { std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c){ return std::tolower(c); }); return s == "true"; } CompositionData ChemicalFileParser::parse_compositon_data(const std::vector& data,const std::string& scheme) const { // get file and metal_fraction_scheme // Load the file // find the metal_fraction_scheme // return abundances // LOG_TRACE_L1(m_logger, "Parsing chemical abundance for: {}", scheme); bool debug = false; if (debug){ std::println("Parsing chemical abundance for: {}", scheme); } std::istringstream stream(std::string(data.begin(), data.end())); // add error message if something goes wrong std::string line; int start_line = 0; int i = 0; CompositionData comp; while (std::getline(stream, line)) { // find where the end of the scheme block is auto end_pos = std::ranges::search(line,std::format("END {}", scheme)); // exit if have reached the end of block if (!end_pos.empty()) { break; } if (start_line>0){ const size_t colon_pos = line.find(':'); line = line.substr(colon_pos+1); line.erase(std::remove_if(line.begin(), line.end(), [](char c){ return c == '[' || c == ']'; }), line.end()); trim(line); std::string item; std::stringstream ss(line); double val; switch(i-start_line){ case 1: comp.comment_str = line; break; case 2: comp.he_abundance = std::pow(10.0,std::stod(line)); break; case 3: comp.requires_atomic_weight = to_bool(line); break; case 4: while(std::getline(ss, item, ',')) { comp.elements.push_back(item); } break; case 5: while(std::getline(ss, item, ',')) { val = std::pow(10.0,std::stod(item)); comp.abundances.push_back(val); } break; } } // find where the start of the scheme block is auto start_pos = std::ranges::search(line, std::format("BEGIN {}", scheme)); if (!start_pos.empty()) { start_line = i; } i+=1; } // if (start_pos==0): // raise error ("Scheme {} not found", scheme) if (debug){ std::println("he_abundance: {}", comp.he_abundance); std::println("requires_atomic_weight: {}", comp.requires_atomic_weight); std::println("elements: {}",comp.elements); std::println("abundances: {}", comp.abundances); } return comp; } IsotopicPercentage ChemicalFileParser::parse_isotopic_percentage(const std::vector& data,const std::string& scheme) const { // get file and iso_scheme // Load the file // find the iso_scheme // get iso_comp data // IsotopicPercentage object bool debug = false; if (debug){ std::println("Parsing Isotopic Percentage for: {}", scheme); } std::istringstream stream(std::string(data.begin(), data.end())); // add error message if something goes wrong ParsedChemicalData parsed; std::string line; int start_line = 0; int i = 0; IsotopicPercentage iso; while (std::getline(stream, line)) { // find where the end of the scheme block is auto end_pos = std::ranges::search(line,std::format("END {}", scheme)); // exit if have reached the end of block if (!end_pos.empty()) { break; } if (start_line>0){ const size_t colon_pos = line.find(':'); line = line.substr(colon_pos+1); line.erase(std::remove_if(line.begin(), line.end(), [](char c){ return c == '[' || c == ']'; }), line.end()); trim(line); std::string item; std::stringstream ss(line); parsed.push_back(line); switch(i-start_line){ case 1: iso.comment_str = line; break; case 2: while(std::getline(ss, item, ',')) { iso.atomic_numbers.push_back(std::stoi(item)); } break; case 3: while(std::getline(ss, item, ',')) { iso.elements.push_back(item); } break; case 4: while(std::getline(ss, item, ',')) { iso.mass_numbers.push_back(std::stoi(item)); } break; case 5: while(std::getline(ss, item, ',')) { iso.percentages.push_back(std::stod(item)); } break; } } // find where the start of the scheme block is auto start_pos = std::ranges::search(line, std::format("BEGIN {}", scheme)); if (!start_pos.empty()) { start_line = i; } i+=1; } if (debug){ std::println("atomic_numbers: {}", iso.atomic_numbers); std::println("elements: {}",iso.elements); std::println("mass_numbers: {}", iso.mass_numbers); std::println("percentages: {}", iso.percentages); } return iso; } } namespace fourdst::composition { Composition get_composition_record(const std::string& metal_fraction_scheme, const std::string& isotopic_percentage_scheme, double initial_z, double initial_y) { std::vector data; io::ChemicalFileParser parser; io::CompositionData metals; io::IsotopicPercentage isotopes; data = std::ranges::to>(StandardMetalFractions); metals = parser.parse_compositon_data(data,metal_fraction_scheme); isotopes = parser.parse_isotopic_percentage(data,isotopic_percentage_scheme); std::string name; std::vector species; // construct name of the isotopes for all elements for (const auto [E,A] : std::ranges::views::zip(isotopes.elements, isotopes.mass_numbers)){ if (std::ranges::contains(metals.elements,E ) || E == "H" || E == "He") { name = std::format("{}-{}",E,A); auto SpeciesObject = atomic::species.at(name); species.push_back(SpeciesObject); // std::println("Species: {} has mass: {}", SpeciesObject.name(), SpeciesObject.mass()); } } std::vector massFracs; std::unordered_map metal_fractions; // hydrogen and helium are treated separately // H1 massFracs.push_back(std::max(0.0, std::min(1.0, 1.0 - (initial_z + initial_y)))); // H2 massFracs.push_back(0.0); // He3 // anders & grevesse 1989 solar mass fractions double xsol_he3,xsol_he4; xsol_he3 = 2.9291e-05; xsol_he4 = 2.7521e-01; massFracs.push_back(initial_y*xsol_he3/(xsol_he3 + xsol_he4)); // He4 massFracs.push_back(initial_y*xsol_he4/(xsol_he3 + xsol_he4)); // Metals double ztotal = 1.0-std::accumulate(massFracs.begin(), massFracs.end(), 0.0); // multiply by atomic weight if needed if (metals.requires_atomic_weight){ // get isotope with max abundance for each metal // and store the corresponding mass number auto element_atomic_weight = [&isotopes]() { std::unordered_map> elem_info; for (const auto& [iso, prcnt, a] : std::views::zip(isotopes.elements, isotopes.percentages, isotopes.mass_numbers)) { if (iso == "H" || iso == "He") { continue; } if (elem_info.contains(iso) && elem_info.at(iso).first <= prcnt) { elem_info[iso] = std::make_pair(prcnt, a); } else if (! elem_info.contains(iso)) { elem_info[iso] = std::make_pair(prcnt, a); } } return elem_info; }(); for (const auto [E,A] : std::ranges::views::zip(metals.elements, metals.abundances)) { // std::println("element: {}", E); auto name = std::format("{}-{}",E,element_atomic_weight.at(E).second); // std::println("{}", name); auto SpeciesObject = atomic::species.at(name); double weight = SpeciesObject.mass(); metal_fractions.emplace(E,A*weight); // std::println("End"); } } else { for (const auto [E,A] : std::ranges::views::zip(metals.elements, metals.abundances)) { metal_fractions.emplace(E,A); } } double sum = [&metal_fractions]() { double accumulator = 0.0; for (const auto& frac : metal_fractions | std::views::values) { accumulator += frac; } return accumulator; }(); for (auto& frac : metal_fractions | std::views::values) { frac/=sum; } double zsum = 0.0; // get mass Fracs for each metal and scale it to required ztotal for (size_t i = 0; i < species.size();++i) { size_t Z = isotopes.atomic_numbers[i]; if (Z<=2) continue; if (metal_fractions.contains(isotopes.elements[i])) { double frac = 1e-2*isotopes.percentages[i]*species[i].mass(); double frac_sum = 0.0; size_t j = i ; while (j 0.0) { for (size_t i = 0; i < massFracs.size();++i) { if (isotopes.atomic_numbers[i]<=2) continue; massFracs[i] *= ztotal/zsum; } } // Composition comp = buildCompositionFromMassFractions(species, massFracs); return comp; } Composition get_composition_record(const io::SolarCompositions metal_fraction_scheme, const io::IsotopicPercentages isotopic_percentage_scheme, double initial_z, double initial_y) { return get_composition_record( io::SolarCompositions_to_string_map.at(metal_fraction_scheme), io::IsotopicPercentages_to_string_map.at(isotopic_percentage_scheme), initial_z, initial_y ); } }