Fundamentals¶
Several high-level types are defined as aliases since they may change based on
configuration: for example, size_type
is a 32-bit integer when building
with device code enabled, but is a 64-bit integer on other 64-bit systems.
-
using celeritas::size_type = unsigned int¶
Standard type for container sizes, optimized for GPU use.
-
using celeritas::real_type = double¶
Numerical type for real numbers.
Debug assertions¶
Celeritas exception types and assertions are defined in
corecel/Assert
. Debug assertions (see Test thoroughly) are only
enabled when the
CELERITAS_DEBUG
(host code) and CELERITAS_DEVICE_DEBUG
(device code)
CMake configuration options are set.
The assertion macros CELER_EXPECT
, CELER_ASSERT
, and CELER_ENSURE
correspond to “precondition contract”, “internal assertion”, and “postcondition
contract”.
-
CELER_EXPECT(COND)¶
Precondition debug assertion macro.
We “expect” that the input values or initial state satisfy a precondition, and we throw exception in debug mode if they do not.
-
CELER_ASSERT(COND)¶
Internal debug assertion macro.
This replaces standard
assert
usage.
-
CELER_ENSURE(COND)¶
Postcondition debug assertion macro.
Use to “ensure” that return values or side effects are as expected when leaving a function.
The following two macros will throw debug assertions or cause undefined behavior at runtime to allow compiler optimizations:
-
CELER_ASSERT_UNREACHABLE()¶
Throw an assertion if the code point is reached.
When debug assertions are turned off, this changes to a compiler hint that improves optimization (and may force the code to exit uncermoniously if the point is encountered, rather than continuing on with undefined behavior).
-
CELER_ASSUME(COND)¶
Always-on compiler assumption.
This should be used very rarely: you should make sure the resulting assembly is simplified in optimize mode from using the assumption. For example, sometimes informing the compiler of an assumption can reduce code bloat by skipping standard library exception handling code (e.g. in
std::visit
by assuming).!var_obj.valueless_by_exception()
Finally, a few runtime macros will always throw helpful errors based on incorrect configuration or input values.
-
CELER_VALIDATE(COND, MSG)¶
Always-on runtime assertion macro.
This can check user input and input data consistency, and will raise RuntimeError on failure with a descriptive error message that is streamed as the second argument. This macro cannot be used in
__device__
-annotated code.The error message should read:
"<PROBLEM> (<WHY IT'S A PROBLEM>) <SUGGESTION>?"
Examples with correct casing and punctuation:
”failed to open ‘{filename}’ (should contain relaxation data)”
”unexpected end of file ‘{filename}’ (data is inconsistent with boundaries)”
”MPI was not initialized (needed to construct a communicator). Maybe set the environment variable CELER_DISABLE_PARALLEL=1 to disable externally?”
”invalid min_range={opts.min_range} (must be positive)”
This looks in practice like:
CELER_VALIDATE(file_stream, << "failed to open '" << filename << "' (should contain relaxation data)");
An always-on debug-type assertion without a detailed message can be constructed by omitting the stream (but leaving the comma):
CELER_VALIDATE(file_stream,);
-
CELER_NOT_CONFIGURED(WHAT)¶
Assert if the code point is reached because an optional feature is disabled.
This generally should be used for the constructors of dummy class definitions in, e.g.,
Foo.nocuda.cc
:Foo::Foo() { CELER_NOT_CONFIGURED("CUDA"); }
-
CELER_NOT_IMPLEMENTED(WHAT)¶
Assert if the code point is reached because a feature has yet to be fully implemented.
This placeholder is so that code paths can be “declared but not defined” and implementations safely postponed in a greppable manner. This should not be used to define “unused” overrides for virtual classes. A correct use case would be:
if (z > AtomicNumber{26}) { CELER_NOT_IMPLEMENTED("physics for heavy nuclides"); }
Utility macros¶
The corecel/Macros.hh
file defines language and compiler abstraction
macro definitions.
-
CELER_TRY_HANDLE(STATEMENT, HANDLE_EXCEPTION)¶
“Try” to execute the statement, and “handle” all thrown errors by calling the given function-like error handler with a
std::exception_ptr
object.Note
A file that uses this macro must include the
<exception>
header (but since theHANDLE_EXCEPTION
needs to take an exception pointer, it’s got to be included anyway).
-
CELER_TRY_HANDLE_CONTEXT(STATEMENT, HANDLE_EXCEPTION, CONTEXT_EXCEPTION)¶
Try the given statement, and if it fails, chain it into the given exception.
The given
CONTEXT_EXCEPTION
must be an expression that yields an rvalue to astd::exception
subclass that isn’tfinal
. The resulting chained exception will be passed intoHANDLE_EXCEPTION
for processing.
-
CELER_DEFAULT_COPY_MOVE(CLS)¶
Explicitly declare defaulted copy and move constructors and assignment operators.
Use this if the destructor is declared explicitly, or as part of the “protected” section of an interface class to prevent assignment between incompatible classes.
-
CELER_DELETE_COPY_MOVE(CLS)¶
Explicitly declare deleted copy and move constructors and assignment operators.
Use this for scoped RAII classes.
-
CELER_DEFAULT_MOVE_DELETE_COPY(CLS)¶
Explicitly declare defaulted copy and move constructors and assignment operators.
Use this if the destructor is declared explicitly.
-
CELER_DISCARD(CODE)¶
The argument is an unevaluated operand which will generate no code but force the expression to be used.
This is used in place of the
attribute, which actually generates warnings in older versions of GCC.[[maybe_unused]]