Program Listing for File EvolvingStellarQuantity.cpp¶
↰ Return to documentation for file (/home/kpenev/projects/git/poet/poet_src/StellarEvolution/EvolvingStellarQuantity.cpp)
#define BUILDING_LIBRARY
#include "EvolvingStellarQuantity.h"
#include <memory>
namespace StellarEvolution {
void EvolvingStellarQuantity::check_grid_range() const
{
if(
__track_masses.size() != 1
&&
__track_feh.size() != 1
&&
(
__mass < __track_masses[0]
||
__mass > __track_masses[__track_masses.size() - 1]
||
__feh < __track_feh[0]
||
__feh > __track_feh[__track_feh.size() - 1]
)
) {
std::ostringstream msg;
msg << "Stellar mass: "
<< __mass
<< " and [Fe/H]: "
<< __feh
<< " are outside the range of masses: "
<< __track_masses[0]
<< " - "
<< __track_masses[__track_masses.size() - 1]
<< " and [Fe/H]: "
<< __track_feh[0]
<< " - "
<< __track_feh[__track_feh.size() - 1]
<< " covered by the stellar evolution grid.";
throw Core::Error::BadFunctionArguments(msg.str());
}
}
void EvolvingStellarQuantity::find_cell(
const std::valarray<double> &boundaries,
double value,
size_t &below_index,
size_t &above_index
) const
{
const double *first_ptr = &boundaries[0],
*last_ptr = first_ptr + boundaries.size(),
*above_ptr = std::lower_bound(first_ptr,
last_ptr,
value),
*below_ptr = above_ptr;
if(boundaries.size() == 1) {
below_index = above_index = 0;
return;
}
#ifndef NDEBUG
if(above_ptr >= last_ptr )
std::cerr << "Value: "
<< value
<< " out of bounds: ["
<< boundaries[0]
<< ", "
<< boundaries[boundaries.size() - 1]
<< "]"
<< std::endl;
#endif
assert( above_ptr < last_ptr );
if( value < *below_ptr) --below_ptr;
#ifndef NDEBUG
if(*above_ptr < value)
std::cerr << "Value: " << value
<< " not in [" << *below_ptr << ", " << *above_ptr << "]"
<< std::endl;
#endif
assert(*above_ptr >= value);
assert(value >= *below_ptr);
below_index = below_ptr - first_ptr;
above_index = above_ptr - first_ptr;
}
void EvolvingStellarQuantity::set_interp_age_ranges()
{
size_t track_i = 0;
for(
size_t feh_i = 0;
feh_i < __track_feh.size();
++feh_i
) {
double track_feh = __track_feh[feh_i];
for(
size_t mass_i = 0;
mass_i < __track_masses.size();
++mass_i
) {
double track_mass = __track_masses[mass_i];
double min_track_age =
__evolution_tracks[track_i]->range_low(),
max_track_age =
__evolution_tracks[track_i]->range_high();
if(__log_age) {
min_track_age = std::exp(min_track_age);
max_track_age = std::exp(max_track_age);
}
__min_interp_ages[track_i] = interp_param_to_age(
age_to_interp_param(min_track_age,
track_mass,
track_feh)
);
__max_interp_ages[track_i] = interp_param_to_age(
age_to_interp_param(max_track_age,
track_mass,
track_feh)
);
#ifndef NDEBUG
std::cerr << "Track "
<< track_i
<< " age range: ["
<< __min_interp_ages[track_i]
<< ", "
<< __max_interp_ages[track_i]
<< "]"
<< std::endl;
#endif
++track_i;
}
}
}
double EvolvingStellarQuantity::evaluate_track(
double age,
const OneArgumentDiffFunction &track,
const FunctionDerivatives **derivatives
) const
{
double track_argument = (__log_age ? std::log(age) : age);
if(track_argument < track.range_low()) {
assert(std::abs(track_argument - track.range_low())
<
1e-8 * std::abs(track_argument));
track_argument = track.range_low();
}
if(derivatives) {
*derivatives = new RemoveLogDeriv(
(__log_age ? age : NaN),
false,
track.deriv(track_argument),
true
);
return (*derivatives)->order(0);
} else return track(track_argument);
}
void EvolvingStellarQuantity::check_grid_expansion_directions(
AllowedGridGrowth &grow,
double age
) const
{
if(__min_interp_mass_index == 0)
grow.block_lighter();
if(__max_interp_mass_index == __track_masses.size())
grow.block_heavier();
if(__min_interp_feh_index == 0)
grow.block_poorer();
if(__max_interp_feh_index == __track_feh.size())
grow.block_richer();
for(
size_t feh_index = __min_interp_feh_index;
(
(grow.lighter() || grow.heavier())
&&
feh_index < __max_interp_feh_index
);
++feh_index
) {
if(
grow.lighter()
&&
!track_in_range(__min_interp_mass_index - 1,
feh_index,
age)
)
grow.block_lighter();
if(
grow.heavier()
&&
!track_in_range(__max_interp_mass_index,
feh_index,
age)
)
grow.block_heavier();
}
for(
size_t mass_index = __min_interp_mass_index;
(
(grow.poorer() || grow.richer())
&&
mass_index < __max_interp_mass_index
);
++mass_index
) {
if(
grow.poorer()
&&
!track_in_range(mass_index,
__min_interp_feh_index - 1,
age)
)
grow.block_poorer();
if(
grow.richer()
&&
!track_in_range(mass_index,
__max_interp_feh_index,
age)
)
grow.block_richer();
}
}
bool EvolvingStellarQuantity::expand_grid(const AllowedGridGrowth &grow,
double age) const
{
if(
grow.lighter()
&&
track_in_range(__min_interp_mass_index - 1,
std::max(size_t(1),
__min_interp_feh_index) - 1,
age)
&&
track_in_range(__min_interp_mass_index - 1,
std::min(__track_feh.size() - 1,
__max_interp_feh_index),
age)
) {
--__min_interp_mass_index;
return true;
}
if(
grow.heavier()
&&
track_in_range(__max_interp_mass_index,
std::max(size_t(1),
__min_interp_feh_index) - 1,
age)
&&
track_in_range(__max_interp_mass_index,
std::min(__track_feh.size() - 1,
__max_interp_feh_index),
age)
) {
++__max_interp_mass_index;
return true;
}
if(
grow.poorer()
&&
track_in_range(std::max(size_t(1), __min_interp_mass_index) - 1,
__min_interp_feh_index - 1,
age)
&&
track_in_range(std::min(__max_interp_mass_index,
__track_masses.size() - 1),
__min_interp_feh_index - 1,
age)
) {
--__min_interp_feh_index;
return true;
}
if(
grow.richer()
&&
track_in_range(std::max(size_t(1), __min_interp_mass_index) - 1,
__max_interp_feh_index,
age)
&&
track_in_range(std::min(__max_interp_mass_index,
__track_masses.size() - 1),
__max_interp_feh_index,
age)
) {
++__max_interp_feh_index;
return true;
}
std::valarray<size_t> padding(4);
padding[0] = __min_interp_mass_index - __mass_index_below;
padding[1] = (__min_interp_feh_index
-
__feh_index_below);
padding[2] =
__max_interp_feh_index - __feh_index_above - 1;
padding[3] = __max_interp_mass_index - __mass_index_above - 1;
if(grow.lighter() && padding[0] == padding.min()) {
--__min_interp_mass_index;
return true;
}
padding[0] = std::numeric_limits<size_t>::max();
if(grow.poorer() && padding[1] == padding.min()) {
--__min_interp_feh_index;
return true;
}
padding[1] = std::numeric_limits<size_t>::max();
if(grow.richer() && padding[2] == padding.min()) {
++__max_interp_feh_index;
return true;
}
if(grow.heavier()) {
++__max_interp_mass_index;
return true;
}
return false;
}
void EvolvingStellarQuantity::update_interpolation_grid() const
{
std::vector<double>::const_iterator
lower_age = __next_grid_change_age;
if(__next_grid_change_age == __interp_grid_change_ages.begin()) {
assert(__initially_zero);
__min_interp_mass_index = 0;
__max_interp_mass_index = 0;
__min_interp_feh_index = 0;
__max_interp_feh_index = 0;
__interp_masses.setlength(0);
__interp_feh.setlength(0);
return;
}
--lower_age;
double age = 0.5 * (*lower_age + *__next_grid_change_age);
if(__min_interp_ages.max() < age && __max_interp_ages.min() > age) {
__min_interp_mass_index = 0;
__max_interp_mass_index = __track_masses.size();
__min_interp_feh_index = 0;
__max_interp_feh_index = __track_feh.size();
} else {
AllowedGridGrowth grow;
if(__mass_index_above == __mass_index_below)
grow.block_lighter().block_heavier();
if(__feh_index_above == __feh_index_below)
grow.block_poorer().block_richer();
__max_interp_mass_index = __mass_index_above + 1;
__min_interp_mass_index = __mass_index_below;
__max_interp_feh_index = __feh_index_above + 1;
__min_interp_feh_index = __feh_index_below;
while(grow) {
check_grid_expansion_directions(grow, age);
if(grow && !expand_grid(grow, age)) break;
}
}
__interp_masses.setcontent(
__max_interp_mass_index - __min_interp_mass_index,
&__track_masses[__min_interp_mass_index]
);
__interp_feh.setcontent(
__max_interp_feh_index - __min_interp_feh_index,
&__track_feh[__min_interp_feh_index]
);
}
double EvolvingStellarQuantity::interpolate(
double age,
const FunctionDerivatives **derivatives
) const
{
if(age < __min_age && __initially_zero) {
if(derivatives) *derivatives=new Core::ZeroDerivatives;
return 0.0;
}
#ifndef NDEBUG
if(age < __min_age || age > __max_age)
std::cerr << "Age: " << age
<< " not in [" << __min_age << ", " << __max_age << "]"
<< std::endl;
#endif
assert(__min_age <= age && age <= __max_age);
assert(__next_grid_change_age != __interp_grid_change_ages.begin());
#ifndef NDEBUG
std::vector<double>::const_iterator
lower_age = __next_grid_change_age;
--lower_age;
assert(*lower_age <= age && age <= *__next_grid_change_age);
#endif
double interp_param = age_to_interp_param(age);
size_t num_interp_tracks = (
(__max_interp_feh_index - __min_interp_feh_index)
*
(__max_interp_mass_index - __min_interp_mass_index)
);
alglib::real_1d_array track_values;
track_values.setlength(num_interp_tracks);
std::vector<const FunctionDerivatives *>
*track_derivatives
=
new std::vector<const FunctionDerivatives *>(num_interp_tracks);
size_t value_index = 0;
for(
size_t feh_index = __min_interp_feh_index;
feh_index < __max_interp_feh_index;
++feh_index
) {
double
track_feh = __track_feh[feh_index];
for(
size_t mass_index = __min_interp_mass_index;
mass_index < __max_interp_mass_index;
++mass_index
) {
double track_mass = __track_masses[mass_index];
double track_age = interp_param_to_age(interp_param,
track_mass,
track_feh);
track_values[value_index] = evaluate_track(
track_age,
*__evolution_tracks[track_index(mass_index,
feh_index)],
(derivatives
? &((*track_derivatives)[value_index])
: NULL)
);
++value_index;
}
}
if(derivatives == NULL) {
double result = mass_feh_interp(__interp_masses,
__interp_feh,
track_values,
__mass,
__feh);
delete track_derivatives;
return (__log_quantity ? std::exp(result) : result);
} else {
*derivatives = new InterpolatedDerivatives(
__mass,
__feh,
track_derivatives,
__interp_masses,
__interp_feh,
NaN,
__log_quantity,
true
);
return (*derivatives)->order(0);
}
}
double EvolvingStellarQuantity::age_to_interp_param(
double age,
double mass,
double feh
) const
{
//t * (1.0 + (t / 5.0) * m**5 * 10.0**(-0.2*feh))* m**2.3 * 10.0**(-0.4*feh)
double feh_factor = std::pow(10.0, -feh / 5.0);
return std::log(
age
*
(1.0 + age / 5.0 * std::pow(mass, 5) * feh_factor)
*
std::pow(mass, 2.3)
*
std::pow(feh_factor, 2)
);
}
double EvolvingStellarQuantity::interp_param_to_age(
double interp_param,
double mass,
double feh
) const
{
double feh_factor = std::pow(10.0, -feh / 5.0),
c = (std::exp(interp_param)
*
std::pow(mass, -2.3)
/
std::pow(feh_factor, 2)),
a = 0.2 * std::pow(mass, 5) * feh_factor,
discr = 1.0 + 4.0 * a * c;
return (std::sqrt(discr) - 1.0) / (2.0 * a);
}
EvolvingStellarQuantity::EvolvingStellarQuantity(
double mass,
double feh,
const std::valarray<double> &track_masses,
const std::valarray<double> &track_feh,
const std::vector<const OneArgumentDiffFunction *> &evolution_tracks,
bool log_age,
bool log_quantity,
bool starts_zero
) :
__mass(mass),
__feh(feh),
__log_age(log_age),
__log_quantity(log_quantity),
__initially_zero(starts_zero),
__track_masses(track_masses),
__track_feh(track_feh),
__min_interp_ages(evolution_tracks.size()),
__max_interp_ages(evolution_tracks.size()),
__evolution_tracks(evolution_tracks)
{
#ifndef NDEBUG
std::cerr << "Creating quantity with mass: " << mass
<< ", [Fe/H]: " << feh << std::endl
<< ", with track masses: " << track_masses << std::endl
<< " and track [Fe/H]: " << track_feh << std::endl
<< " with " << evolution_tracks.size() << " tracks"
<< std::endl;
#endif
assert(evolution_tracks.size() == (track_masses.size()
*
track_feh.size()));
check_grid_range();
set_interp_age_ranges();
if(starts_zero) {
__interp_grid_change_ages.reserve(2 * evolution_tracks.size()
+
1);
__interp_grid_change_ages.insert(__interp_grid_change_ages.end(),
-Core::Inf);
} else {
__interp_grid_change_ages.reserve(2 * evolution_tracks.size());
}
__interp_grid_change_ages.insert(
__interp_grid_change_ages.end(),
&__min_interp_ages[0],
&__min_interp_ages[0] + __min_interp_ages.size()
);
__interp_grid_change_ages.insert(
__interp_grid_change_ages.end(),
&__max_interp_ages[0],
&__max_interp_ages[0] + __max_interp_ages.size()
);
std::sort(__interp_grid_change_ages.begin(),
__interp_grid_change_ages.end());
std::unique(__interp_grid_change_ages.begin(),
__interp_grid_change_ages.end());
__next_grid_change_age = __interp_grid_change_ages.end();
find_cell(__track_masses,
mass,
__mass_index_below,
__mass_index_above);
find_cell(__track_feh,
feh,
__feh_index_below,
__feh_index_above);
__min_age = __min_interp_ages[
track_index(__mass_index_below, __feh_index_below)
];
__max_age = __max_interp_ages[
track_index(__mass_index_below, __feh_index_below)
];
__min_age = std::max(
__min_age,
__min_interp_ages[track_index(__mass_index_above,
__feh_index_below)]
);
__max_age = std::min(
__max_age,
__max_interp_ages[track_index(__mass_index_above,
__feh_index_below)]
);
__min_age = std::max(
__min_age,
__min_interp_ages[track_index(__mass_index_below,
__feh_index_above)]
);
__max_age = std::min(
__max_age,
__max_interp_ages[track_index(__mass_index_below,
__feh_index_above)]
);
__min_age = std::max(
__min_age,
__min_interp_ages[track_index(__mass_index_above,
__feh_index_above)]
);
__max_age = std::min(
__max_age,
__max_interp_ages[track_index(__mass_index_above,
__feh_index_above)]
);
}
void EvolvingStellarQuantity::select_interpolation_region(double age)
const
{
#ifndef NDEBUG
std::cerr << "Finding interpolation region for age = "
<< age
<< ", grid change ages: "
<< std::endl;
for(
std::vector<double>::const_iterator
change_age_i = __interp_grid_change_ages.begin();
change_age_i != __interp_grid_change_ages.end();
++change_age_i
)
std::cerr << "\t" << *change_age_i << std::endl;
#endif
std::vector<double>::const_iterator new_grid_change_age =
std::upper_bound(
__interp_grid_change_ages.begin(),
__interp_grid_change_ages.end(),
age
);
if(__next_grid_change_age != new_grid_change_age) {
__next_grid_change_age = new_grid_change_age;
assert(__next_grid_change_age
!=
__interp_grid_change_ages.end());
update_interpolation_grid();
}
}
const FunctionDerivatives *EvolvingStellarQuantity::deriv(double age)
const
{
const FunctionDerivatives *deriv;
interpolate(age, &deriv);
return deriv;
}
double EvolvingStellarQuantity::previous_discontinuity() const
{
if(__next_grid_change_age == __interp_grid_change_ages.begin()) {
assert(__initially_zero);
return -Core::Inf;
}
std::vector<double>::const_iterator result = __next_grid_change_age;
return *(--result);
}
void EvolvingStellarQuantity::enable_next_interpolation_region() const
{
assert(__next_grid_change_age != __interp_grid_change_ages.end());
++__next_grid_change_age;
update_interpolation_grid();
}
} //End StellarEvolution namespace