Skip to content

Commit 1bce4ce

Browse files
committed
env handling
1 parent fc41581 commit 1bce4ce

File tree

10 files changed

+221
-38
lines changed

10 files changed

+221
-38
lines changed

res/cmake/test.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ if (test AND ${PHARE_EXEC_LEVEL_MIN} GREATER 0) # 0 = no tests
1818
add_subdirectory(tests/core/data/maxwellian_particle_initializer)
1919
add_subdirectory(tests/core/data/particle_initializer)
2020
add_subdirectory(tests/core/utilities/box)
21+
add_subdirectory(tests/core/utilities/env)
2122
add_subdirectory(tests/core/utilities/range)
2223
add_subdirectory(tests/core/utilities/index)
2324
add_subdirectory(tests/core/utilities/indexer)

src/core/env.hpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#ifndef PHARE_CORE_ENV_HPP
2+
#define PHARE_CORE_ENV_HPP
3+
4+
// Single source for handling env vars
5+
6+
#include <array>
7+
#include <cstdint>
8+
#include <optional>
9+
#include <string_view>
10+
#include "core/utilities/types.hpp"
11+
#include "core/utilities/mpi_utils.hpp"
12+
13+
namespace PHARE::env
14+
{
15+
16+
struct Var
17+
{
18+
using value_type = std::string;
19+
20+
std::string_view const id;
21+
std::string_view const desc;
22+
std::vector<std::pair<std::string_view, std::string_view>> const options;
23+
24+
std::optional<value_type> const _default = std::nullopt;
25+
std::function<std::string(Var const&)> const _result
26+
= [](Var const&) { return std::string{""}; }; // noop
27+
std::optional<value_type> const v = get();
28+
std::string const r = _result(*this);
29+
30+
std::optional<value_type> get() const
31+
{
32+
std::string _id{id};
33+
if (_default)
34+
return core::get_env(_id, *_default);
35+
return core::get_env(_id);
36+
}
37+
auto& operator()() const { return v; }
38+
bool exists() const { return v != std::nullopt; }
39+
operator bool() const { return exists(); }
40+
auto result() const { return r; }
41+
};
42+
43+
} // namespace PHARE::env
44+
45+
namespace PHARE
46+
{
47+
48+
class Env
49+
{
50+
public:
51+
template<typename T>
52+
using map_t = std::unordered_map<std::string, T const* const>;
53+
54+
static Env& INSTANCE()
55+
{
56+
if (!self)
57+
self = std::make_unique<Env>();
58+
return *self;
59+
}
60+
static auto& reinit() { return *(self = std::make_unique<Env>()); }
61+
62+
env::Var const PHARE_LOG{
63+
"PHARE_LOG",
64+
"Write logs to $CWD/.log",
65+
{{{"RANK_FILES", "Write logs $CWD/.log, a file per rank"},
66+
{"DATETIME_FILES", "Write logs $CWD/.log, filename per rank and datetime"},
67+
{"NONE", "print normally to std::cout"}}},
68+
std::nullopt,
69+
[](auto const& self) {
70+
if (auto const& opt = self())
71+
{
72+
auto const& val = *opt;
73+
if (val == "RANK_FILES")
74+
return ".log/" + std::to_string(core::mpi::rank()) + ".out";
75+
if (val == "DATETIME_FILES")
76+
{
77+
auto date_time = core::mpi::date_time();
78+
auto rank = std::to_string(core::mpi::rank());
79+
auto size = std::to_string(core::mpi::size());
80+
return ".log/" + date_time + "_" + rank + "_of_" + size + ".out";
81+
}
82+
if (val != "NONE")
83+
throw std::runtime_error(
84+
"PHARE_LOG invalid type, valid keys are RANK_FILES/DATETIME_FILES/NONE");
85+
}
86+
return std::string{""};
87+
} //
88+
};
89+
90+
map_t<env::Var> const vars = {{{"PHARE_LOG", &PHARE_LOG}}};
91+
92+
private:
93+
static inline std::unique_ptr<Env> self = nullptr;
94+
};
95+
96+
} // namespace PHARE
97+
98+
#endif /* PHARE_CORE_ERRORS_H */

src/core/utilities/mpi_utils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ std::size_t max(std::size_t const local, int mpi_size)
2727

2828

2929

30+
3031
bool any(bool b)
3132
{
3233
int global_sum, local_sum = static_cast<int>(b);

src/core/utilities/mpi_utils.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ inline bool is_init()
4040
return flag > 0;
4141
}
4242

43+
44+
struct Lifecycle
45+
{
46+
Lifecycle(int argc, char** argv) { MPI_Init(&argc, &argv); }
47+
~Lifecycle() { MPI_Finalize(); }
48+
};
49+
50+
4351
template<typename Data>
4452
NO_DISCARD auto mpi_type_for()
4553
{

src/core/utilities/types.hpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,15 @@ namespace core
195195
T var;
196196
};
197197

198+
template<typename T>
199+
NO_DISCARD T from_string(std::string const& s)
200+
{
201+
T t;
202+
std::stringstream ss(s);
203+
ss >> t;
204+
return t;
205+
}
206+
198207
template<typename T>
199208
NO_DISCARD std::string to_string_with_precision(T const& a_value, std::size_t const len)
200209
{
@@ -246,6 +255,24 @@ namespace core
246255
}
247256

248257

258+
template<typename T>
259+
NO_DISCARD inline std::optional<T> get_env_as(std::string const& key)
260+
{
261+
if (auto e = get_env(key))
262+
return from_string<T>(*e);
263+
return std::nullopt;
264+
}
265+
266+
267+
template<typename T>
268+
NO_DISCARD inline T get_env_as(std::string const& key, T const& t)
269+
{
270+
if (auto e = get_env(key))
271+
return from_string<T>(*e);
272+
return t;
273+
}
274+
275+
249276

250277
} // namespace core
251278
} // namespace PHARE

src/hdf5/detail/h5/h5_file.hpp

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,7 @@ class HighFiveFile
6969

7070
~HighFiveFile() {}
7171

72-
NO_DISCARD HiFile& file()
73-
{
74-
return h5file_;
75-
}
72+
NO_DISCARD HiFile& file() { return h5file_; }
7673

7774

7875
template<typename T, std::size_t dim = 1>
@@ -146,15 +143,11 @@ class HighFiveFile
146143
void write_attribute(std::string const& keyPath, std::string const& key, Data const& data)
147144
{
148145
// assumes all keyPaths and values are identical, and no null patches
149-
// clang-format off
150-
PHARE_DEBUG_DO(
146+
PHARE_DEBUG_DO( //
151147
auto const paths = core::mpi::collect(keyPath, core::mpi::size());
152-
for (auto const& path : paths)
153-
PHARE_LOG_LINE_STR(std::to_string(core::mpi::size()) << " " << path)
154-
if (!core::all(paths, [&](auto const& path) { return path == paths[0]; }))
155-
throw std::runtime_error("Function does not support different paths per mpi core");
148+
if (!core::all(paths, [&](auto const& path) { return path == paths[0]; })) throw std::
149+
runtime_error("Function does not support different paths per mpi core"); //
156150
)
157-
// clang-format on
158151

159152
constexpr bool data_is_vector = core::is_std_vector_v<Data>;
160153

src/python3/cpp_etc.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "python3/pybind_def.hpp"
33
#include "simulator/simulator.hpp"
44

5+
#include "core/env.hpp"
56
#include "core/def/phare_config.hpp"
67

78

@@ -55,5 +56,16 @@ PYBIND11_MODULE(cpp_etc, m)
5556
});
5657

5758
m.def("phare_build_config", []() { return PHARE::build_config(); });
59+
60+
m.def("phare_env_exists",
61+
[](std::string const& s) { return Env::INSTANCE().vars.count(s) > 0; });
62+
m.def("phare_env_val", [](std::string const& s) {
63+
assert(Env::INSTANCE().vars.count(s) > 0);
64+
return (*Env::INSTANCE().vars.at(s))();
65+
});
66+
m.def("phare_env_result", [](std::string const& s) {
67+
assert(Env::INSTANCE().vars.count(s) > 0);
68+
return Env::INSTANCE().vars.at(s)->result();
69+
});
5870
}
5971
} // namespace PHARE::pydata

src/simulator/simulator.hpp

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "phare_types.hpp"
88

99
#include "core/def.hpp"
10+
#include "core/env.hpp"
1011
#include "core/logger.hpp"
1112
#include "core/utilities/types.hpp"
1213
#include "core/utilities/mpi_utils.hpp"
@@ -110,32 +111,7 @@ class Simulator : public ISimulator
110111
private:
111112
auto find_model(std::string name);
112113

113-
auto static log_file_name()
114-
{
115-
// ".log" directory is not created here, but in python if PHARE_LOG != "NONE"
116-
if (auto log = core::get_env("PHARE_LOG"))
117-
{
118-
if (log == "RANK_FILES")
119-
return ".log/" + std::to_string(core::mpi::rank()) + ".out";
120-
121-
122-
if (log == "DATETIME_FILES")
123-
{
124-
auto date_time = core::mpi::date_time();
125-
auto rank = std::to_string(core::mpi::rank());
126-
auto size = std::to_string(core::mpi::size());
127-
return ".log/" + date_time + "_" + rank + "_of_" + size + ".out";
128-
}
129-
130-
if (log != "NONE")
131-
throw std::runtime_error(
132-
"PHARE_LOG invalid type, valid keys are RANK_FILES/DATETIME_FILES/NONE");
133-
}
134-
135-
return std::string{""}; // unused
136-
}
137-
138-
std::ofstream log_out{log_file_name()};
114+
std::ofstream log_out{Env::INSTANCE().PHARE_LOG.result()};
139115
std::streambuf* coutbuf = nullptr;
140116
std::shared_ptr<PHARE::amr::Hierarchy> hierarchy_;
141117
std::unique_ptr<Integrator> integrator_;
@@ -186,7 +162,7 @@ namespace
186162
inline auto logging(std::ofstream& log_out)
187163
{
188164
std::streambuf* buf = nullptr;
189-
if (auto log = core::get_env("PHARE_LOG"); log != "NONE")
165+
if (auto log = Env::INSTANCE().PHARE_LOG(); log and log != "NONE")
190166
{
191167
buf = std::cout.rdbuf();
192168
std::cout.rdbuf(log_out.rdbuf());
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
cmake_minimum_required (VERSION 3.20.1)
2+
3+
project(test-phare-env)
4+
5+
set(SOURCES test_env.cpp)
6+
7+
add_executable(${PROJECT_NAME} ${SOURCES})
8+
9+
target_include_directories(${PROJECT_NAME} PRIVATE
10+
${GTEST_INCLUDE_DIRS}
11+
)
12+
13+
target_link_directories(${PROJECT_NAME} PUBLIC ${MPI_LIBRARY_PATH})
14+
15+
target_link_libraries(${PROJECT_NAME} PRIVATE
16+
phare_core
17+
MPI::MPI_C
18+
${GTEST_LIBS}
19+
)
20+
21+
add_phare_test(${PROJECT_NAME} ${CMAKE_CURRENT_BINARY_DIR})

tests/core/utilities/env/test_env.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
2+
3+
#include "core/env.hpp"
4+
#include "gtest/gtest.h"
5+
#include <cstdlib>
6+
7+
namespace PHARE
8+
{
9+
10+
TEST(Env, logging_works)
11+
{
12+
setenv("PHARE_LOG", "NONE", true);
13+
auto& env = Env::reinit(); // init
14+
15+
EXPECT_EQ(env.vars.size(), 1);
16+
17+
for (auto const& [key, var] : env.vars)
18+
{
19+
EXPECT_EQ(key, std::string{"PHARE_LOG"});
20+
EXPECT_EQ(key, std::string{var->id});
21+
}
22+
}
23+
24+
TEST(Env, logging_works_rank_files)
25+
{
26+
setenv("PHARE_LOG", "RANK_FILES", true);
27+
auto& env = Env::reinit(); // init
28+
29+
EXPECT_EQ(env.vars.size(), 1);
30+
31+
for (auto const& [key, var] : env.vars)
32+
{
33+
EXPECT_EQ(key, std::string{"PHARE_LOG"});
34+
EXPECT_EQ(key, std::string{var->id});
35+
EXPECT_EQ(var->result(), ".log/" + std::to_string(core::mpi::rank()) + ".out");
36+
}
37+
}
38+
39+
} // namespace PHARE
40+
41+
int main(int argc, char** argv)
42+
{
43+
::testing::InitGoogleTest(&argc, argv);
44+
PHARE::core::mpi::Lifecycle mpi_init(argc, argv);
45+
return RUN_ALL_TESTS();
46+
}

0 commit comments

Comments
 (0)