Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 42 additions & 31 deletions src/cosmology/tabulated_dynamicalDE_EoS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,56 +78,67 @@ void TabulatedDynamicalDarkEnergyEoS::Set_DynamicalDE_Density()
}
}

void TabulatedDynamicalDarkEnergyEoS::Setup_DynamicalDE_EquationOfState_(const std::string& path)
static int count_lines(std::istream& in)
{
chprintf("Loading wDE info... \n");
int line_count = 0;
std::string line;
while (std::getline(in, line)) line_count++;
in.clear(); // <- we need to clear the failure state set by final std::getline call
in.seekg(0); // <- rewind the position of in
return line_count;
}

void TabulatedDynamicalDarkEnergyEoS::Setup_DynamicalDE_EquationOfState_(std::istream& in, const std::string& path,
bool silent)
{
if (not silent) chprintf("Loading wDE info... \n");

std::fstream in(path);
int n_lines = count_lines(in);
std::string line;
std::vector<std::vector<float>> v;
int i = 0;
if (in.is_open()) {
while (std::getline(in, line)) {
if (line.find('#') == 0) continue;

float value;
std::stringstream ss(line);
v.emplace_back();

while (ss >> value) {
v[i].push_back(value);
}
i += 1;
int lineno = 0;
while (std::getline(in, line)) {
lineno++; // <- increment the line number (it is 1-indexed)
if (line.find('#') == 0) continue;
if (line.empty()) {
if (lineno == n_lines) continue; // <- allow empty final line
CHOLLA_ERROR("%s:%d is empty", path.c_str(), lineno);
}
in.close();
} else {
chprintf(" Error: Unable to open DE equation of state file: %s\n", path.c_str());
exit(1);

float value;
std::stringstream ss(line);
std::vector<float>& latest_pack = v.emplace_back();

while (ss >> value) {
latest_pack.push_back(value);
}
CHOLLA_ASSERT(latest_pack.size() == 2, "%s:%d doesn't specify 2 elements", path.c_str(), lineno);
}
int n_lines = i;
int n_entries = v.size();
CHOLLA_ASSERT(n_entries > 0, "%s doesn't contain any data", path.c_str());

for (i = 0; i < n_lines; i++) {
for (int i = 0; i < n_entries; i++) {
dynamicalDE_table_z.push_back(v[i][0]);
dynamicalDE_table_w.push_back(v[i][1]);
}

for (i = 0; i < n_lines - 1; i++) {
for (int i = 0; i < n_entries - 1; i++) {
if (dynamicalDE_table_z[i] > dynamicalDE_table_z[i + 1]) {
chprintf(
CHOLLA_ERROR(
" ERROR: equation of state must be ordered such that redshift is increasing "
"as the rows increase in the file\n",
path.c_str());
exit(2);
}
}

chprintf(" Loaded DE equation of state file : \n");
chprintf(" N redshift values: %d \n", dynamicalDE_table_z.size());
chprintf(" z_min = %f z_max = %f \n", dynamicalDE_table_z.front(), dynamicalDE_table_z.back());
chprintf(" w(z_min) = %f w(z_max) = %f \n", dynamicalDE_table_w.front(), dynamicalDE_table_w.back());
if (not silent) {
chprintf(" Loaded DE equation of state file : \n");
chprintf(" N redshift values: %d \n", dynamicalDE_table_z.size());
chprintf(" z_min = %f z_max = %f \n", dynamicalDE_table_z.front(), dynamicalDE_table_z.back());
chprintf(" w(z_min) = %f w(z_max) = %f \n", dynamicalDE_table_w.front(), dynamicalDE_table_w.back());
}

if (dynamicalDE_table_z[0] != 0.) {
chprintf("We require z_min = 0 so that w(z=0) is well defined \n");
exit(1);
CHOLLA_ERROR("We require z_min = 0 so that w(z=0) is well defined \n");
}
}
24 changes: 21 additions & 3 deletions src/cosmology/tabulated_dynamicalDE_EoS.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@

#pragma once

#include <fstream>
#include <iostream>
#include <string>
#include <vector>

#include "../global/global.h"
#include "../utils/error_handling.h"

class TabulatedDynamicalDarkEnergyEoS
{
Expand All @@ -18,18 +22,32 @@ class TabulatedDynamicalDarkEnergyEoS
std::vector<float> dynamicalDE_table_density;

/*! Load redshift and dark energy equation of state table z, wDE(z), only called once to setup dynamical DE case */
void Setup_DynamicalDE_EquationOfState_(const std::string& path);
void Setup_DynamicalDE_EquationOfState_(std::istream& in, const std::string& fname, bool silent);

/*! Calculate dark energy density normalized to z=0, populate dynamicalDE_table_density */
void Set_DynamicalDE_Density();

void Setup_Full(std::istream& in, const std::string& fname) {}

public:
/*! Interpolate dynamicalDE_table_density to find rhoDE(z) / rhoDE(z=0) at z=1/a - 1 */
Real Get_DynamicalDE_Density_from_a(Real a);

explicit TabulatedDynamicalDarkEnergyEoS(const std::string& path)
/*! Construct a new instance
*
* By default, when \p f is a ``nulllptr, this function tries to open the file named
* \p path. Otherwise, this function treats \p f as if it's a newly openned stream
* associated with \p path (this secondary behavior is useful for testing purposes).
*/
explicit TabulatedDynamicalDarkEnergyEoS(const std::string& path, std::istream* f = nullptr, bool silent = false)
{
Setup_DynamicalDE_EquationOfState_(path);
std::fstream tmp;
if (f == nullptr) {
tmp.open(path, std::ios_base::in);
CHOLLA_ASSERT(tmp.is_open(), "Unable to open DE equation of state file: %s\n", path.c_str());
f = dynamic_cast<std::istream*>(&tmp);
}
Setup_DynamicalDE_EquationOfState_(*f, path, silent);
Set_DynamicalDE_Density();
}
};
154 changes: 154 additions & 0 deletions src/cosmology/tabulated_dynamicalDE_EoS_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*! \file
* Holds tests for \ref TabulatedDynamicalDarkEnergyEoS
*/
#include <sstream>
#include <string>

// External Includes
#include <gtest/gtest.h> // Include GoogleTest and related libraries/headers

#include "tabulated_dynamicalDE_EoS.h"

// this is the path we use when we construct a TabulatedDynamicalDarkEnergyEoS directly
// from a string
const char* string_file_path_("dummy-file-path");

/*! A helper function that constructs a \ref TabulatedDynamicalDarkEnergyEoS instance
* by treating \p contents as the contents of a file
*/
TabulatedDynamicalDarkEnergyEoS construct_from_string_(const std::string& contents)
{
std::istringstream file_contents(contents);
// we pass silent=true to suppress informational messages summarizing properties of
// the file when we read it
TabulatedDynamicalDarkEnergyEoS dynamical_eos(string_file_path_, dynamic_cast<std::istream*>(&file_contents), true);
return dynamical_eos;
}

/*! Encapsulates a sample input file that will be used to read in
* \ref TabulatedDynamicalDarkEnergyEOS
*/
struct FileVariation {
std::string description;
std::string content;
};

// teach GoogleTest how to print File Variation
void PrintTo(const FileVariation& fv, std::ostream* os) { *os << fv.description; }

// -------------------------------------------------------------------------------------

// we are going to run a simple test case where we try to parse variants of the
// following string
const std::string GOOD_CONTENTS_ = R"LITERAL(# z, w
0.000000000000000000e+00 -9.767616499273475972e-01
6.938631476027579126e-03 -9.769941793369097960e-01
# this is a random meaningless comment!
1.392540755881421788e-02 -9.772263520514015145e-01)LITERAL";

class tALLTabulatedDynamicalDarkEnergyEoS : public testing::TestWithParam<FileVariation>
{
};

TEST_P(tALLTabulatedDynamicalDarkEnergyEoS, SimpleOpen)
{
TabulatedDynamicalDarkEnergyEoS dynamical_eos = construct_from_string_(GetParam().content);

// the value at z=0 is always normalized to be 1.0
EXPECT_EQ(dynamical_eos.Get_DynamicalDE_Density_from_a(1.0), 1.0);

// check that the value before the earliest redshfift are all the same
EXPECT_EQ(dynamical_eos.Get_DynamicalDE_Density_from_a(0.1), dynamical_eos.Get_DynamicalDE_Density_from_a(0.01));

// it would be great to actually make some strong checks rather than just checking
// the bounds (but that would require a more detailed understanding of the
// calculation than I currently have)
}

const FileVariation valid_variants_[3] = {
{"NoTerminalNewline", GOOD_CONTENTS_},
{"WithTerminalNewline", GOOD_CONTENTS_ + '\n'},
{"EmptyFinalLine", GOOD_CONTENTS_ + "\n\n"},
};

INSTANTIATE_TEST_SUITE_P(
/* 1st arg intentionally empty */, tALLTabulatedDynamicalDarkEnergyEoS, testing::ValuesIn(valid_variants_),
testing::PrintToStringParamName());

// -------------------------------------------------------------------------------------

// if we ever decide that DeathTests are too expensive, we can transition to using exceptions
// in the following tests

TEST(tALLTabulatedDynamicalDarkEnergyEoSDeathTest, InvalidPath)
{
std::string path = "not/a/real/path.txt";
ASSERT_DEATH({ TabulatedDynamicalDarkEnergyEoS dyanmical_eos(path); },
"Unable to open DE equation of state file: " + path);
}

TEST(tALLTabulatedDynamicalDarkEnergyEoSDeathTest, MissingRedshift0)
{
const std::string contents = R"LITERAL(# z, w
6.938631476027579126e-03 -9.769941793369097960e-01
1.392540755881421788e-02 -9.772263520514015145e-01)LITERAL";

ASSERT_DEATH({ TabulatedDynamicalDarkEnergyEoS dyanmical_eos = construct_from_string_(contents); },
"We require z_min = 0 so that w\\(z=0\\) is well defined");
}

TEST(tALLTabulatedDynamicalDarkEnergyEoSDeathTest, OutOfOrder)
{
const std::string contents = R"LITERAL(# z, w
0.000000000000000000e+00 -9.767616499273475972e-01
1.392540755881421788e-02 -9.772263520514015145e-01
6.938631476027579126e-03 -9.769941793369097960e-01)LITERAL";

ASSERT_DEATH(
{ TabulatedDynamicalDarkEnergyEoS dyanmical_eos = construct_from_string_(contents); },
"ERROR: equation of state must be ordered such that redshift is increasing as the rows increase in the file");
}

TEST(tALLTabulatedDynamicalDarkEnergyEoSDeathTest, SingleElemOnLine2)
{
const std::string contents = R"LITERAL(# z, w
0.000000000000000000e+00
1.392540755881421788e-02 -9.772263520514015145e-01)LITERAL";

ASSERT_DEATH({ TabulatedDynamicalDarkEnergyEoS dyanmical_eos = construct_from_string_(contents); },
string_file_path_ + std::string(":2 doesn't specify 2 elements"));
}

TEST(tALLTabulatedDynamicalDarkEnergyEoSDeathTest, ThreeElemsOnLine2)
{
const std::string contents = R"LITERAL(# z, w
0.0 -0.97 342
1.392540755881421788e-02 -9.772263520514015145e-01)LITERAL";

ASSERT_DEATH({ TabulatedDynamicalDarkEnergyEoS dyanmical_eos = construct_from_string_(contents); },
string_file_path_ + std::string(":2 doesn't specify 2 elements"));
}

// -------------------------------------------------------------------------------------

// define parametrized tests where we try to read files without real contents

class tALLTabulatedDynamicalDarkEnergyEoSNoContentsDeathTest : public testing::TestWithParam<FileVariation>
{
};

TEST_P(tALLTabulatedDynamicalDarkEnergyEoSNoContentsDeathTest, Simple)
{
ASSERT_DEATH({ TabulatedDynamicalDarkEnergyEoS dynamical_eos = construct_from_string_(GetParam().content); },
string_file_path_ + std::string(" doesn't contain any data"));
}

const FileVariation invalid_variants_[] = {
{"Empty", ""},
{"SingleBlankLine", "\n"},
{"SingleComment", "# this is a comment!"},
};

INSTANTIATE_TEST_SUITE_P(
/* 1st arg intentionally empty */, tALLTabulatedDynamicalDarkEnergyEoSNoContentsDeathTest,
testing::ValuesIn(invalid_variants_), testing::PrintToStringParamName());
Loading