Skip to content

Commit 1e6ec37

Browse files
committed
env handling
1 parent fc41581 commit 1e6ec37

File tree

12 files changed

+269
-42
lines changed

12 files changed

+269
-42
lines changed

pyphare/pyphare/cpp/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,19 @@ def build_config():
3636
return cpp_etc_lib().phare_build_config()
3737

3838

39+
def env_vars():
40+
return cpp_etc_lib().phare_env_vars()
41+
42+
43+
def print_env_vars_info():
44+
for k, v in cpp_etc_lib().phare_env_vars().items():
45+
print(f"{k}: {v.desc}")
46+
print("Options:")
47+
for k, v in v.options:
48+
print(f" {k}: {v}")
49+
print("")
50+
51+
3952
def build_config_as_json():
4053
return json.dumps(build_config())
4154

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: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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+
env::Var const PHARE_SCOPE_TIMING{
90+
"PHARE_SCOPE_TIMING", "Enable function scope timing", {{{"1", "ON"}, {"0", "OFF"}}}, "0"};
91+
92+
map_t<env::Var> const vars = {{
93+
{"PHARE_LOG", &PHARE_LOG},
94+
{"PHARE_SCOPE_TIMING", &PHARE_SCOPE_TIMING},
95+
}};
96+
97+
auto& operator()(std::string const& s) const
98+
{
99+
assert(vars.count(s));
100+
return *vars.at(s);
101+
}
102+
103+
private:
104+
static inline std::unique_ptr<Env> self = nullptr;
105+
};
106+
107+
} // namespace PHARE
108+
109+
#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: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,22 @@ inline bool is_init()
4040
return flag > 0;
4141
}
4242

43+
44+
struct Lifecycle
45+
{ // mostly used in core tests without samrai
46+
Lifecycle(int argc, char** argv)
47+
{
48+
if (MPI_Init(&argc, &argv) != MPI_SUCCESS)
49+
throw std::runtime_error("MPI Initialization failed");
50+
}
51+
~Lifecycle()
52+
{
53+
if (MPI_Finalize() != MPI_SUCCESS)
54+
std::cerr << "MPI Finalization failed" << std::endl;
55+
}
56+
};
57+
58+
4359
template<typename Data>
4460
NO_DISCARD auto mpi_type_for()
4561
{

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/phare/phare.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#include "core/def/phlop.hpp" // scope timing
66

7+
#include "core/env.hpp" // scope timing
8+
79
#include "simulator/simulator.hpp"
810
#include "core/utilities/algorithm.hpp"
911
#include "core/utilities/mpi_utils.hpp"
@@ -41,7 +43,7 @@ class SamraiLifeCycle
4143
= std::make_shared<StreamAppender>(StreamAppender{&std::cout});
4244
SAMRAI::tbox::Logger::getInstance()->setWarningAppender(appender);
4345
PHARE_WITH_PHLOP( //
44-
if (auto e = core::get_env("PHARE_SCOPE_TIMING", "false"); e == "1" || e == "true")
46+
if (auto e = Env::INSTANCE().PHARE_SCOPE_TIMING(); e == "1" || e == "true")
4547
phlop::ScopeTimerMan::INSTANCE()
4648
.file_name(".phare_times." + std::to_string(core::mpi::rank()) + ".txt")
4749
.init(); //

src/python3/cpp_etc.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11

2+
#include "pybind11/stl.h"
3+
#include "pybind11/stl_bind.h"
24
#include "python3/pybind_def.hpp"
35
#include "simulator/simulator.hpp"
46

7+
#include "core/env.hpp"
58
#include "core/def/phare_config.hpp"
69

7-
810
#include "amr/wrappers/hierarchy.hpp" // for HierarchyRestarter::getRestartFileFullPath
911

10-
11-
1212
namespace py = pybind11;
1313

14+
PYBIND11_MAKE_OPAQUE(std::unordered_map<std::string, PHARE::env::Var*>);
15+
1416
namespace PHARE::pydata
1517
{
1618
auto pybind_version()
@@ -55,5 +57,25 @@ PYBIND11_MODULE(cpp_etc, m)
5557
});
5658

5759
m.def("phare_build_config", []() { return PHARE::build_config(); });
60+
61+
m.def("phare_env_exists",
62+
[](std::string const& s) { return Env::INSTANCE().vars.count(s) > 0; });
63+
m.def("phare_env_val", [](std::string const& s) { return Env::INSTANCE()(s)(); });
64+
m.def("phare_env_result", [](std::string const& s) {
65+
assert(Env::INSTANCE().vars.count(s) > 0);
66+
return Env::INSTANCE().vars.at(s)->result();
67+
});
68+
py::class_<env::Var>(m, "phare_env_var")
69+
.def_readonly("id", &env::Var::id)
70+
.def_readonly("desc", &env::Var::desc)
71+
.def_readonly("options", &env::Var::options)
72+
.def_readonly("default", &env::Var::_default)
73+
.def_readonly("result", &env::Var::r);
74+
75+
py::bind_map<std::unordered_map<std::string, env::Var*>>(m, "EnvVarMap");
76+
77+
m.def(
78+
"phare_env_vars", []() -> auto& { return Env::INSTANCE().vars; },
79+
py::return_value_policy::reference);
5880
}
5981
} // 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})

0 commit comments

Comments
 (0)