Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_Parser.H
Go to the documentation of this file.
1#ifndef AMREX_PARSER_H_
2#define AMREX_PARSER_H_
3
9#include <AMReX_Arena.H>
10#include <AMReX_Array.H>
11#include <AMReX_GpuDevice.H>
12#include <AMReX_Parser_Exe.H>
13#include <AMReX_REAL.H>
14#include <AMReX_Vector.H>
15
16#include <concepts>
17#include <map>
18#include <memory>
19#include <string>
20#include <set>
21#include <utility>
22
23namespace amrex {
24
25namespace parser_detail {
26
27// Store the arguments in an array. On the host, volatile keeps the stores
28// scalar so that parser_exe_eval's loads can forward from them.
29template <typename... Ts>
31void store_args (double* l_var, Ts... var) noexcept
32{
34 int i = 0;
35 ([&] { l_var[i++] = static_cast<double>(var); }(), ...);
36 ))
38 volatile double* p = l_var;
39 int i = 0;
40 ([&] { p[i++] = static_cast<double>(var); }(), ...);
41 ))
42}
43
44}
45
56template <int N>
58{
67 double operator() () const noexcept requires (N==0)
68 {
69 AMREX_IF_ON_DEVICE((return parser_exe_eval(m_device_executor, nullptr);))
70 AMREX_IF_ON_HOST((return eval_host(nullptr);))
71 }
72
79 template <typename... Ts>
80 requires (N > 0 && sizeof...(Ts) == N && !amrex::Same<float,Ts...>::value)
82 double operator() (Ts... var) const noexcept
83 {
84 double l_var[N];
85 parser_detail::store_args(l_var, var...);
86 AMREX_IF_ON_DEVICE((return parser_exe_eval(m_device_executor, l_var);))
87 AMREX_IF_ON_HOST((return eval_host(l_var);))
88 }
89
97 template <std::same_as<float>... Ts>
98 requires (N > 0 && sizeof...(Ts) == N)
100 float operator() (Ts... var) const noexcept
101 {
102 double l_var[N];
103 parser_detail::store_args(l_var, var...);
104 AMREX_IF_ON_DEVICE((return static_cast<float>(parser_exe_eval(m_device_executor, l_var));))
105 AMREX_IF_ON_HOST((return static_cast<float>(eval_host(l_var));))
106 }
107
114 double operator() (GpuArray<double,N> const& var) const noexcept
115 {
116 AMREX_IF_ON_DEVICE((return parser_exe_eval(m_device_executor, var.data());))
117 AMREX_IF_ON_HOST((return eval_host(var.data());))
118 }
119
124 explicit operator bool () const {
125 AMREX_IF_ON_DEVICE((return m_device_executor != nullptr;))
126 AMREX_IF_ON_HOST((return m_host_executor != nullptr;))
127 }
128
129 char* m_host_executor = nullptr;
130#ifdef AMREX_USE_GPU
131 char* m_device_executor = nullptr;
132#endif
133 bool m_threaded = false;
134
135private:
137 double eval_host (double const* var) const noexcept
138 {
139#if AMREX_PARSER_THREADED_DISPATCH
140 if (m_threaded) { return parser_exe_eval_threaded(m_host_executor, var); }
141#endif
142 return parser_exe_eval(m_host_executor, var);
143 }
144};
145
147
160{
161public:
166 Parser (std::string const& func_body);
168 Parser () = default;
173 void define (std::string const& func_body);
174
176 explicit operator bool () const;
177
183 void setConstant (std::string const& name, double c);
184
192 void registerVariables (Vector<std::string> const& vars);
193
200 void registerUserFn1 (std::string const& name, ParserUserFn1 fh, ParserUserFn1 fd);
207 void registerUserFn2 (std::string const& name, ParserUserFn2 fh, ParserUserFn2 fd);
214 void registerUserFn3 (std::string const& name, ParserUserFn3 fh, ParserUserFn3 fd);
221 void registerUserFn4 (std::string const& name, ParserUserFn4 fh, ParserUserFn4 fd);
222
224 void print () const;
226 void printExe () const;
227
232 [[nodiscard]] int depth () const;
237 [[nodiscard]] int maxStackSize () const;
238
243 [[nodiscard]] std::string expr () const;
244
249 [[nodiscard]] std::set<std::string> symbols () const;
250
255 [[nodiscard]] std::map<std::string,int> const& userFunctions () const;
256
262 template <int N> [[nodiscard]] ParserExecutor<N> compile () const;
263
269 template <int N> [[nodiscard]] ParserExecutor<N> compileHost () const;
270
271private:
272
273 template <int argc, typename F>
274 void register_user_fn (std::string const& name, F fh, F fd);
275
277 struct Data {
278 std::string m_expression;
279 struct amrex_parser* m_parser = nullptr;
280 int m_nvars = 0;
281 bool m_use_arena = true;
282 char* m_host_executor = nullptr;
283#ifdef AMREX_USE_GPU
284 char* m_device_executor = nullptr;
285#endif
286 int m_max_stack_size = 0;
287 int m_exe_size = 0;
288 Vector<char const*> m_locals;
289 Data () = default;
290 ~Data ();
291 void clear_host_executor ();
292 Data (Data const&) = delete;
293 Data (Data &&) = delete;
294 Data& operator= (Data const&) = delete;
295 Data& operator= (Data &&) = delete;
296 };
298
299 std::shared_ptr<Data> m_data;
300 std::map<std::string,int> m_ufs;
301 std::map<std::string,std::pair<void*,void*>> m_uf_ptrs;
302 Vector<std::string> m_vars;
303};
304
305template <int N>
306ParserExecutor<N>
308{
309 if (m_data && m_data->m_parser) {
310 AMREX_ALWAYS_ASSERT(N == m_data->m_nvars);
311
312 // Make sure all user functions have been registered.
313 for (auto const& [ufname, nargs] : m_ufs) {
314 if (m_uf_ptrs.find(ufname) == m_uf_ptrs.cend()) {
315 amrex::Abort("amrex::Parser: Unknown user function "+ufname
316 + " in " + m_data->m_expression);
317 }
319 }
320
321 if (!(m_data->m_host_executor)) {
322 int stack_size;
323 m_data->m_exe_size = static_cast<int>
324 (parser_exe_size(m_data->m_parser, m_data->m_max_stack_size,
325 stack_size));
326
327 if (m_data->m_max_stack_size > AMREX_PARSER_STACK_SIZE) {
328 amrex::Abort("amrex::Parser: AMREX_PARSER_STACK_SIZE, "
329 + std::to_string(AMREX_PARSER_STACK_SIZE) + ", is too small for "
330 + m_data->m_expression);
331 }
332 if (stack_size != 0) {
333 amrex::Abort("amrex::Parser: something went wrong with parser stack! "
334 + std::to_string(stack_size));
335 }
336
337 m_data->m_host_executor = (char*)The_Pinned_Arena()->alloc(m_data->m_exe_size);
338 if (m_data->m_host_executor == nullptr) { // Arena is not ready yet
339 m_data->m_host_executor = (char*) std::malloc(m_data->m_exe_size);
340 m_data->m_use_arena = false;
341 }
342
343 try {
344 m_data->m_locals = parser_compile(m_data->m_parser, m_uf_ptrs,
345 m_data->m_host_executor);
346 } catch (const std::runtime_error& e) {
347 // Do not leave a partially compiled executor behind.
348 m_data->clear_host_executor();
349 throw std::runtime_error(std::string(e.what()) + " in Parser expression \""
350 + m_data->m_expression + "\"");
351 }
352 }
353
354 bool threaded = parser_exe_use_threaded(m_data->m_exe_size,
355 m_data->m_max_stack_size);
356#ifdef AMREX_USE_GPU
357 return ParserExecutor<N>{m_data->m_host_executor, m_data->m_device_executor, threaded};
358#else
359 return ParserExecutor<N>{m_data->m_host_executor, threaded};
360#endif
361 } else {
362 return ParserExecutor<N>{};
363 }
364}
365
366template <int N>
369{
370 if (m_data && m_data->m_parser) {
371 auto const unsupported = parser_get_device_unsupported_functions(m_data->m_parser);
372 if (!unsupported.empty()) {
373 std::string msg("amrex::Parser: these functions cannot be used in device code:");
374 for (auto const& f : unsupported) { msg.append(" ").append(f); }
375 amrex::Abort(msg + ". Use compileHost instead for " + m_data->m_expression);
376 }
377 }
378
379 auto exe = compileHost<N>();
380
381#ifdef AMREX_USE_GPU
382 if (m_data && m_data->m_parser && !(m_data->m_device_executor)
383 && m_data->m_use_arena)
384 {
385 m_data->m_device_executor = (char*)The_Arena()->alloc(m_data->m_exe_size);
386 Gpu::htod_memcpy_async(m_data->m_device_executor, m_data->m_host_executor,
387 m_data->m_exe_size);
389 exe.m_device_executor = m_data->m_device_executor;
390 }
391#endif
392
393 return exe;
394}
395
396template <int argc, typename F>
397void
398Parser::register_user_fn (std::string const& name, F fh, F fd)
399{
400#if !defined(AMREX_USE_GPU)
401 // For pure CPU builds, we allow the user to pass the function pointer with either fh or fd
402 if (fh == nullptr) { std::swap(fh, fd); }
403#endif
404
405 auto it = m_ufs.find(name);
406 if (it != m_ufs.end()) {
407 if (it->second == argc) {
408 m_uf_ptrs[name] = std::make_pair((void*)fh, (void*)fd);
409 } else {
410 amrex::Abort("Parser::registerUserFn: incorrect no. of arguments in "+name);
411 }
412 } else {
413 amrex::Abort("Parser::registerUserFn: unknown user define function "+name);
414 }
415}
416
417}
418
419#endif
Memory arena base class and global arena accessors.
Fixed-size array types for use on GPU and CPU.
#define AMREX_ALWAYS_ASSERT(EX)
Definition AMReX_BLassert.H:50
#define AMREX_FORCE_INLINE
Definition AMReX_Extension.H:124
#define AMREX_IF_ON_DEVICE(CODE)
Definition AMReX_GpuQualifiers.H:56
#define AMREX_IF_ON_HOST(CODE)
Definition AMReX_GpuQualifiers.H:58
#define AMREX_GPU_HOST_DEVICE
Definition AMReX_GpuQualifiers.H:20
while parser_exe_t p
Definition AMReX_Parser_Exe_Body.H:33
virtual void * alloc(std::size_t sz)=0
Allocate sz bytes from this arena.
Front-end for parsing scalar expressions into GPU/CPU executors.
Definition AMReX_Parser.H:160
std::set< std::string > symbols() const
Return the set of symbols (variables and functions) referenced by the expression.
Definition AMReX_Parser.cpp:182
void registerUserFn3(std::string const &name, ParserUserFn3 fh, ParserUserFn3 fd)
Register a user-defined ternary function.
Definition AMReX_Parser.cpp:132
void registerUserFn1(std::string const &name, ParserUserFn1 fh, ParserUserFn1 fd)
Register a user-defined unary function available to expressions.
Definition AMReX_Parser.cpp:120
void print() const
Print a human-readable representation of the parser tree to stdout.
Definition AMReX_Parser.cpp:144
int maxStackSize() const
Return the maximum stack usage recorded during compilation.
Definition AMReX_Parser.cpp:162
ParserExecutor< N > compileHost() const
Compile the current expression into a host-only executor.
Definition AMReX_Parser.H:307
void registerUserFn4(std::string const &name, ParserUserFn4 fh, ParserUserFn4 fd)
Register a user-defined function with four arguments.
Definition AMReX_Parser.cpp:138
std::string expr() const
Return the sanitized expression string currently managed by the Parser.
Definition AMReX_Parser.cpp:172
void define(std::string const &func_body)
Parse and own a new expression, replacing any previous state.
Definition AMReX_Parser.cpp:24
ParserExecutor< N > compile() const
Compile the current expression into a host/device executor.
Definition AMReX_Parser.H:368
Parser()=default
Default-construct; call define() before compilation.
void printExe() const
Dump the compiled bytecode to stdout for debugging.
Definition AMReX_Parser.cpp:198
void setConstant(std::string const &name, double c)
Bind a named constant to c.
Definition AMReX_Parser.cpp:88
void registerVariables(Vector< std::string > const &vars)
Register the ordered list of variable names used by the expression.
Definition AMReX_Parser.cpp:99
std::map< std::string, int > const & userFunctions() const
Access the map of registered user-defined functions.
Definition AMReX_Parser.cpp:192
int depth() const
Return the maximum tree depth of the parsed expression.
Definition AMReX_Parser.cpp:152
void registerUserFn2(std::string const &name, ParserUserFn2 fh, ParserUserFn2 fd)
Register a user-defined binary function.
Definition AMReX_Parser.cpp:126
This class is a thin wrapper around std::vector. Unlike vector, Vector::operator[] provides bound che...
Definition AMReX_Vector.H:29
Arena * The_Pinned_Arena()
Definition AMReX_Arena.cpp:869
Arena * The_Arena()
Definition AMReX_Arena.cpp:829
void streamSynchronize() noexcept
Definition AMReX_GpuDevice.H:310
void htod_memcpy_async(void *p_d, const void *p_h, const std::size_t sz) noexcept
Definition AMReX_GpuDevice.H:421
Definition AMReX_Amr.cpp:50
__host__ __device__ void ignore_unused(const Ts &...)
No-op helper that marks variables as intentionally unused.
Definition AMReX.H:273
void Abort(const std::string &msg)
Print a fatal-error message to stderr and abort execution.
Definition AMReX.cpp:244
Fixed-size array that can be used on GPU.
Definition AMReX_Array.H:52
Callable wrapper around a compiled parser expression with N variables.
Definition AMReX_Parser.H:58
char * m_host_executor
Pointer to host-visible bytecode.
Definition AMReX_Parser.H:129
bool m_threaded
Use threaded dispatch on the host (long bytecode).
Definition AMReX_Parser.H:133
__host__ __device__ double operator()() const noexcept
Evaluate the expression with no arguments.
Definition AMReX_Parser.H:67
char * m_device_executor
Pointer to device-visible bytecode (if copied).
Definition AMReX_Parser.H:131
Definition AMReX_TypeTraits.H:133