diff --git a/.gitlab/machines.yml b/.gitlab/machines.yml index 95b01e765..73e87f572 100644 --- a/.gitlab/machines.yml +++ b/.gitlab/machines.yml @@ -8,7 +8,7 @@ variables: HOSTNAME: 'ruby' PARTITION: pdebug - BUILD_ALLOC: srun -N 1 -c 36 -p pdebug -t 60 + BUILD_ALLOC: salloc -N 1 -p pdebug -t 60 --exclusive TEST_ALLOC: '' CLEAN_ALLOC: srun -n 20 extends: [.on_toss_4_x86] diff --git a/.gitlab/scripts.yml b/.gitlab/scripts.yml index 5b44f650f..aadab5bd2 100644 --- a/.gitlab/scripts.yml +++ b/.gitlab/scripts.yml @@ -27,11 +27,7 @@ script: - CI_BUILD_DIR=$(cat ci-dir.txt) - cd $CI_BUILD_DIR && cat job-name.txt - - $BUILD_ALLOC ./$SCRIPT_DIR/devtools/host-config-build.py --host-config gitlab.cmake --build $EXTRA_CMAKE_ARGS - artifacts: - paths: - - ci-dir.txt - - job-name.txt + - $BUILD_ALLOC ./$SCRIPT_DIR/devtools/host-config-build.py --host-config gitlab.cmake --build --test $EXTRA_CMAKE_ARGS .build_and_test: extends: [.build] diff --git a/.gitlab/specs.yml b/.gitlab/specs.yml index 229deebaf..90f9759d6 100644 --- a/.gitlab/specs.yml +++ b/.gitlab/specs.yml @@ -37,7 +37,7 @@ .cuda_11_gcc_spectrum: variables: SPEC: 'gcc@$GCC_VERSION+cuda cuda_arch=70' - EXTRA_CMAKE_ARGS: '-DENABLE_TIMER=On' + EXTRA_CMAKE_ARGS: '-DENABLE_TIMER=On -DSPHERAL_ENABLE_VVI=On' .oneapi_2022_1_mvapich2: variables: diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 9e5d37f99..73c73b8a1 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -5,6 +5,15 @@ Version vYYYY.MM.p -- Release date YYYY-MM-DD Notable changes include: * New features / API changes: + * Value-View Interface for wrapping complex classes in a GPU compatible Interface. + * Enabled w/ `SPHERAL_ENABLE_VVI=On`. + * Example executables demonstrating : + * Basic class w/ ManagedVector member. + * CRTP pattern. + * `Spheral::Field` like structure w/ an abstract inferface base class. + * VVI Implmenetation of `Spheral::QuadraticInterpolator`. + * CTest unit tests for `QuadraticInterpolator` and `Field`. + * CTest runs during GitlabCI build stage. * MPI variables are now wrapped as ``` SPHERAL_OP_SUM, SPHERAL_OP_MAX, SPHERAL_OP_MIN diff --git a/cmake/CMakeDefinitions.cmake b/cmake/CMakeDefinitions.cmake index d8b2f2340..a31f75c29 100644 --- a/cmake/CMakeDefinitions.cmake +++ b/cmake/CMakeDefinitions.cmake @@ -16,6 +16,15 @@ else() add_definitions("-DDEBUG=0") endif() +if (SPHERAL_ENABLE_VVI) + message("-- Value-View Interface Pattern (VVI) : Enabled") + message("------ WARNING ------") + message("-- VVI is an experimental implementation necessary for the GPU port. Use with caution!") + message("---------------------") +else() + message("-- Semantic Class Interface Pattern (VVI) : Disabled") +endif() + # The DBC flag if (DBC_MODE STREQUAL "All") message("-- DBC (design by contract) set to All") diff --git a/cmake/InstallTPLs.cmake b/cmake/InstallTPLs.cmake index 9055a0c6f..105b93253 100644 --- a/cmake/InstallTPLs.cmake +++ b/cmake/InstallTPLs.cmake @@ -117,11 +117,13 @@ message("----------------------------------------------------------------------- find_package(RAJA REQUIRED NO_DEFAULT_PATH PATHS ${raja_DIR}) if (RAJA_FOUND) message("Found RAJA External Package.") + blt_convert_to_system_includes(TARGET RAJA) endif() message("-----------------------------------------------------------------------------") find_package(umpire REQUIRED NO_DEFAULT_PATH PATHS ${umpire_DIR}) if (umpire_FOUND) message("Found umpire External Package.") + blt_convert_to_system_includes(TARGET umpire) endif() message("-----------------------------------------------------------------------------") diff --git a/cmake/SetupSpheral.cmake b/cmake/SetupSpheral.cmake index d93c4fd41..5aa1fccc1 100644 --- a/cmake/SetupSpheral.cmake +++ b/cmake/SetupSpheral.cmake @@ -11,6 +11,11 @@ if (NOT SPHERAL_CMAKE_MODULE_PATH) endif() list(APPEND CMAKE_MODULE_PATH "${SPHERAL_CMAKE_MODULE_PATH}") +#------------------------------------------------------------------------------- +# Add Spheral CMake Macros for tests and executables +#------------------------------------------------------------------------------- +include(SpheralMacros) + #------------------------------------------------------------------------------- # Set Compiler Flags / Options #------------------------------------------------------------------------------- @@ -85,7 +90,10 @@ if(ENABLE_CUDA) #set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -arch=${CUDA_ARCH} --expt-relaxed-constexpr --extended-lambda -Xcudafe --display_error_number") set(CMAKE_CUDA_STANDARD 17) list(APPEND SPHERAL_CXX_DEPENDS cuda) + set(SPHERAL_ENABLE_CUDA On) + set(SPHERAL_ENABLE_VVI On) endif() +message("Enable Value-View Interface Pattern (VVI) : ${SPHERAL_ENABLE_VVI}") #-------------------------------------------------------------------------------# # Set a default build type if none was specified @@ -140,6 +148,10 @@ set_property(GLOBAL PROPERTY SPHERAL_CXX_DEPENDS "${SPHERAL_CXX_DEPENDS}") #------------------------------------------------------------------------------- # Prepare to build the src #------------------------------------------------------------------------------- +configure_file(${SPHERAL_ROOT_DIR}/src/config.hh.in + ${PROJECT_BINARY_DIR}/src/config.hh) +include_directories(${PROJECT_BINARY_DIR}/src) + add_subdirectory(${SPHERAL_ROOT_DIR}/src) #------------------------------------------------------------------------------- @@ -153,7 +165,7 @@ endif() # Build C++ tests and install tests to install directory #------------------------------------------------------------------------------- if (ENABLE_TESTS) - add_subdirectory(${SPHERAL_ROOT_DIR}/tests/unit) + add_subdirectory(${SPHERAL_ROOT_DIR}/tests) # A macro to preserve directory structure when installing files macro(install_with_directory) diff --git a/cmake/SpheralMacros.cmake b/cmake/SpheralMacros.cmake new file mode 100644 index 000000000..ec2b955aa --- /dev/null +++ b/cmake/SpheralMacros.cmake @@ -0,0 +1,91 @@ +macro(spheral_add_executable) + set(options ) + set(singleValueArgs NAME TEST REPRODUCER BENCHMARK) + set(multiValueArgs SOURCES DEPENDS_ON) + + cmake_parse_arguments(arg + "${options}" "${singleValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (ENABLE_OPENMP) + list (APPEND arg_DEPENDS_ON openmp) + endif () + + if (ENABLE_CUDA) + list (APPEND arg_DEPENDS_ON cuda) + endif () + + if (ENABLE_HIP) + list (APPEND arg_DEPENDS_ON blt::hip) + list (APPEND arg_DEPENDS_ON blt::hip_runtime) + endif () + + if (${arg_TEST}) + set (_output_dir ${CMAKE_BINARY_DIR}/test) + elseif (${arg_REPRODUCER}) + set (_output_dir ${CMAKE_BINARY_DIR}/reproducers) + elseif (${arg_BENCHMARK}) + set (_output_dir ${CMAKE_BINARY_DIR}/benchmark) + else () + set (_output_dir ${CMAKE_BINARY_DIR}/bin) + endif() + + blt_add_executable( + NAME ${arg_NAME} + SOURCES ${arg_SOURCES} + DEPENDS_ON ${arg_DEPENDS_ON} + OUTPUT_DIR ${_output_dir} + ) + + target_include_directories(${arg_NAME} SYSTEM PRIVATE ${SPHERAL_EXTERN_INCLUDES}) + target_include_directories(${arg_NAME} SYSTEM PRIVATE ${SPHERAL_ROOT_DIR}/src) + target_include_directories(${arg_NAME} SYSTEM PRIVATE ${PROJECT_BINARY_DIR}/src) + +endmacro(spheral_add_executable) + +macro(spheral_add_test) + set(options DEBUG_LINKER) + set(singleValueArgs NAME) + set(multiValueArgs SOURCES DEPENDS_ON) + + cmake_parse_arguments(arg + "${options}" "${singleValueArgs}" "${multiValueArgs}" ${ARGN}) + + set(original_test_name ${arg_NAME}) + set(original_src ${arg_SOURCES}) + set(original_deps ${arg_DEPENDS_ON}) + + if (ENABLE_DEV_BUILD) + message("Skipping ${original_test_name} : NOT compatible with ENABLE_DEV_BUILD.") + else() + get_property(SPHERAL_BLT_DEPENDS GLOBAL PROPERTY SPHERAL_BLT_DEPENDS) + + blt_add_library( + NAME ${original_test_name}_lib + SOURCES ${TEST_LIB_SOURCE} + SOURCES ${SPHERAL_ROOT_DIR}/src/spheralCXX.cc + DEPENDS_ON ${SPHERAL_BLT_DEPENDS} ${original_deps} + SHARED False + ) + + target_link_options(${original_test_name}_lib PRIVATE "-Wl,--unresolved-symbols=ignore-in-object-files") + + spheral_add_executable( + NAME ${original_test_name}.exe + SOURCES ${original_src} + DEPENDS_ON gtest ${CMAKE_THREAD_LIBS_INIT} ${original_test_name}_lib + TEST On) + + blt_add_test( + NAME ${original_test_name} + COMMAND ${TEST_DRIVER} ${original_test_name}.exe) + + target_include_directories(${original_test_name}.exe SYSTEM PRIVATE ${SPHERAL_ROOT_DIR}/tests/cpp/include) + + if (${arg_DEBUG_LINKER}) + target_link_options(${original_test_name}.exe PUBLIC "-Wl,--warn-unresolved-symbols") + else() + target_link_options(${original_test_name}.exe PUBLIC "-Wl,--unresolved-symbols=ignore-all") + endif() + endif() + +endmacro(spheral_add_test) diff --git a/docs/build_guide/include/appendecies/cmake_config.rst.inc b/docs/build_guide/include/appendecies/cmake_config.rst.inc index 4fb343fa6..615653ebd 100644 --- a/docs/build_guide/include/appendecies/cmake_config.rst.inc +++ b/docs/build_guide/include/appendecies/cmake_config.rst.inc @@ -88,6 +88,9 @@ In this section we list the CMake variables that can be tweaked for a Spheral bu ``ENABLE_DEV_BUILD`` (On, **Off**) Builds separate internal C++ libraries for faster code development. +``SPHERAL_ENABLE_VVI`` (On, *Off*) + Builds Spheral with the experimental Value-View Interface for GPU porting capabilities. + ``_DIR`` Directory of previously built TPL. diff --git a/docs/developer/design/value_view_interface.rst b/docs/developer/design/value_view_interface.rst new file mode 100644 index 000000000..5014ae615 --- /dev/null +++ b/docs/developer/design/value_view_interface.rst @@ -0,0 +1,540 @@ +================================== +Value-View Interface Documentation +================================== + +************ +Introduction +************ + +The Value-View Interface (VVI) pattern is a tool to aid in porting complex class structures and design patterns that are not traditionally conducive to execution on the GPU. It can also be used as a stepping stone for performing incremental integration of a large codebase to the GPU. Allowing a code to port it's existing CPU friendly patterns to the GPU without a major initial refactor. + +VVI's philosophy is based largely upon ideas from CHAI. VVI builds upon from CHAI's ability to perform implicit data migration between the CPU and GPU for CHAI containers (such as ``chai::ManagedArray``\ ) by extending that capability to user defined types. + +.. note:: + + CHAI is designed to integrate as a plugin to RAJA codes, therefore the use of RAJA and CHAI are **essential** for the VVI pattern. + + +``chai::ManagedSharedPtr`` (developed specifically for VVI) is the backbone of the VVI Pattern. It is a ``std::shared_ptr`` implementation that supports implicit copies of the owned object between the CPU and GPU as well as dynamic dispatch with base types on the GPU. + +.. note:: + + We also provided CHAI with ``chai::ManagedVector``\ , a ``std::vector`` like CHAI class. This was not necessary for VVI but was instrumental in the port for Spheral to the GPU and will be seen later in this document. + + +******************* +The VALUE Interface +******************* + +The Value-View interface enables a class to switch between value (\ *VALUE*\ ) and reference (\ *VIEW*\ ) semantics. A *VALUE* interface allows use of objects in a way that is indicative of normal C++. The semantics of a *VIEW* interface allow us to use the same instance of an object with reference semantics and provides the ability to be implicitly copyable between the HOST and DEVICE. + +****************** +The VIEW Interface +****************** + +There are two paths for defining a *VIEW* interface. Each has it's tradeoffs and should be selected based on situation. + +Reference Syntax Interface +========================== + +The Reference Interface lets a View act like a reference, calling functions of a class similarly to how you would with any reference of a type. This interface is the more verbose and allows users to specify exactly which calls can and cannot be made by the View interface. This is a more type safe implementation. Compile time errors will be thrown if a function call that is only usable in the Value Interface is called from a View Interface. However, if your code targeted for offload to the GPU often references the Implementation class by pointer, this could require many tedious changes and the Pointer Syntax Interface might be a better first step. + +Pointer Syntax Interface +======================== + +The pointer interface allows a View object to act like a pointer. The interface required to define this implementation can be considerably smaller for many classes. It does not provide type safety. Therefore, you *could* call a function that should only be used by a Value object from a View object (you should still get a ``__host__ __device__`` warning if this happens in a GPU context). It is up to the developer to understand what they can and cannot call from a view like object. This is the "quick and dirty" conversion but is really useful for rapid implementation of VVI in certain cases. + +***************** +Use Cases for VVI +***************** + +VVI was designed to help overcome a variety of complex obstacles in regards to GPU porting, such as: + + +* Classes with dynamically resizable container members and metadata members associated with the class that need to be consistent across execution contexts. +* Dynamic container-like classes of pointers to other dynamic container-like classes that need to be accessed with double index notation. +* Polymorphic objects with complex implementations. Where they are accessed through base class pointers with dynamic dispatch. +* CRTP compile time polymorphism design patterns across a variety of core classes. + +VVI should be avoided when possible. Simple classes with trivial members should be converted to be GPU capable directly. VVI is most useful when a copy of an object could result in a considerable data reallocation or when the class structure is not conducive to traditional GPU use. + +*********** +Definitions +*********** + +Implementation Class (\ ``ImplType``\ ) +======================================= + +The Implementation Class is a class targeted for use with the VVI pattern. This is usually a pre-existing class that a developer wants to use on the GPU. VVI is required when it's current members or class hierarchy / structure are not conventionally suited for device side use. + +Value Class (\ ``ValueType``\ ) +=============================== + +An interface to the underlying implmentation class. This class is named the same as the ``ImplType``\ , and thus should require no changes to the current user code to use this type. The ``ValueType`` uses traditional C++ value copy semantics by default. However the semantic interface of how the type should be used should directly match the semantic conditions of the original ``ImplType``. + +View Class (\ ``ViewType``\ ) +============================= + +The View interface is what allows VVI to work seamlessly with ``RAJA`` execution contexts. A ``ViewType`` will have reference semantic like behavior. Meaning that copy construction of a ``ViewType`` does not invoke an allocation of the underlying class data, it instead references the underlying ``ValueType`` instance that it is associated with. + +************************************** +Preparing Implementation Class for VVI +************************************** + +Private Members +=============== + + +* All data members must be private. They may be accessed through ``get`` and ``set`` methods. This should be done first as this may require some changes through the codebase. + +.. important:: + + When using VVI the ``ImplType`` members cannot be accessed directly from *VALUE* interfaces so we must expose public members by forwarding a function that returns a reference to them. If we don't make this change, usage of the ``ImplType`` will be different when VVI is enabled vs disabled. + + +.. code-block:: c++ + + class foo { + public: + int m_var = 1; + std::vector m_vec; + }; + + ... + + foo f; + f.m_var = 5; + +.. code-block:: c++ + + class foo { + int m_var = 1; + std::vector m_vec; + public: + int& var() {return m_var} + }; + + ... + + foo f; + f.var() = 5 + +std::vector members +=================== + + +* ``std::vector``\ members should be converted to ``vvi::vector``. ``vvi::vector`` is an alias for ``chai::ManagedVector`` that will revert to ``std::vector`` when VVI is disabled. + +.. code-block:: c++ + + class foo { + int m_var = 1; + vvi::vector m_vec; + public: + int& var() {return m_var} + }; + + +* A ``deepCopy()`` method must be provided that calls ``deepCopy()`` on each ``vvi``\ member. When VVI is enabled the ``vvi`` types use reference semantics. ``deepCopy()`` allows the *VALUE* interface to copy the object correctly. + +DeepCopy & Compare + In order to correctly perform Copy and Equivalence operations from the *VALUE* interface, special ``deepCopy`` and ``compare`` method needs to be provided from the ``ImplType``. Helper macros have been developed to aid in declaring these in your classes. + +DeepCopy + ``VVI_IMPL_DEEPCOPY(impl_t, )`` This deep copy macro first performs the ``ImplType`` Copy Ctor, It will then manually perform a deep-copy on any other ``vvi`` type that exists as a member of the ``ImplType`` defined in the list. + +Compare + ``VVI_IMPL_COMPARE(impl_t, )`` This macro will perform a value based comparison on all member names given (you probably want to list all members...). + +.. code-block:: c++ + + class foo { + int m_var = 1; + vvi::vector m_vec; + + public: + int& var() {return m_var} + + VVI_IMPL_DEEPCOPY(foo, m_vec) + VVI_IMPL_COMPARE(foo, m_vec, m_var) + }; + +chai::CHAICopyable +================== + + +* If you intend to use the class as the element type of a chai container then a **default constructor** must be provided. +* A class containing a ``vvi`` type member above needs to inherit from ``chai::CHAICopyable``. This allows CHAI to recursively copy the appropriate data . + +.. code-block:: c++ + + class foo : public chai::CHAICopyable { + int m_var = 1; + vvi::vector m_vec; + + public: + int& var() {return m_var} + + VVI_IMPL_DEEPCOPY(foo, m_vec) + VVI_IMPL_COMPARE(foo, m_vec, m_var) + }; + + ... + + chai::ManagedArray my_arr; + +Classes w/ virtual methods +========================== + + +* If the class has a virtual method such that a virtual table would be present for it or its derived classes the class needs to inherit from ``chai::Poly``. + +.. code-block:: c++ + + class base { + virtual void fooBar() = 0; + }; + + class derived : public base { + virtual void fooBar() override {...} + }; + +.. code-block:: c++ + + class base : chai::Poly{ + virtual void fooBar() = 0; + }; + + class derived : public base { + virtual void fooBar() override {...} + }; + +.. note:: + + Inheriting from ``chai::CHAIPoly`` includes the behavior of ``chai::CHAICopyable``. + + +***************************************** +Value View Interface Declaration Examples +***************************************** + +Basic Class Interface +===================== + +Below we demonstrate how to implement the Value-View Interface (VVI) pattern on a very basic class. + +We need to wrap our implementation class between ``VVI_IMPL_BEGIN`` and ``VVI_IMPL_END``. These macros encapsulate the code within a namespace ``vvimpl``. This is to allow us to name the future *VALUE* interface class with the same typename as the original implementation. + +.. code-block:: c++ + + VVI_IMPL_BEGIN + class foo + { + void doSomething() {}; + } + VVI_IMPL_END + +We need to forward declare *VALUE* class names. VVI details should be guarded with a ``VVI_ENABLED`` preprocessor check. + +.. code-block:: c++ + + #ifdef VVI_ENABLED + class foo; + +Basic VIEW Interface +-------------------- + +*VIEW* interfaces inherit default constructor, copy constructor, and assignment operator behavior provided by the underlying ``ViewInterface`` type that all *VIEW* interface classes inherit from. + +Most of the Value and View Interface class structure is repeatable and can be rather convoluted to write by hand. ``(PTR/REF)_(VALUE/VIEW)_METACLASS_DECL`` macros are provided to allow users to declare the Value and View interfaces type. + +We use helper macros with the naming convention ``__`` to aid in writing interfaces. Below we pass the type names of ``()``\ , ``()``\ , ``()`` to each ``_METACLASS_DECL`` macro. The final argument ``(code)`` is used to forward the class definitions further below. + +.. note:: + + **ALL** arguments to the ``_METACLASS_DECL`` macros **MUST** be wrapped in ``()``. + + +.. code-block:: c++ + + #define fooView__(code) PTR_VIEW_METACLASS_DECL( \ + (foo), (fooView), (vvimpl::foo), (code) ) + + class fooView__(); + +.. note:: + + We are using the ``PTR_`` implementation of the VVI Pattern in this case. This means all members of the implementation class are available to view objects through ``operator->()``. This leads to many definitions of *VIEW* interfaces to be empty. + +.. tip:: + + For empty *VIEW* interfaces, using default behavior as above, we can consolidate the class declaration and definition into one line with ``PTR_VIEW_METACLASS_DEFAULT`` + + .. code-block:: c++ + + class PTR_VIEW_METACLASS_DEFAULT( (foo), (fooView), (vvimpl::foo) ) + + +Basic Value Interface +--------------------- + +*VALUE* interface default behavior is inherited from the ``vvi::detail::ValueInterface`` class. If not defined a Default Constructor will be used for the *VALUE* interface. Copy constructor, Assignment and Equivalence behavior is baked in, assuming the appropriate ``DEEPCOPY`` and ``COMPARE`` interfaces have been defined in the ``ImplType``. + +VVI Supplies ``VVI_IMPL_INST`` as a way to access the underlying instantiation of the implementation class and should be used to forward functions to the interface as seen below. + +.. code-block:: c++ + + #define foo__(code) PTR_VALUE_METACLASS_DECL( \ + (foo), (fooView), (vvimpl::foo), (code) ) + + class foo__( + public: + void doSomething() { VVI_IMPL_INST().doSomething(); } + ); + #endif + +Class w/ Custom Constructor & Vector Member +=========================================== + +Here we demonstrate using VVI with a class containing a ``vvi::vector`` member and a non default constructor. We have already made the necessary changes for VVI preparation. We will also assume that this class may be used as an element type to another CHAI container, therefore it must inherit from ``chai::CHAICopyable``. + +.. code-block:: c++ + + VVI_IMPL_BEGIN + class foo : public chai::CHAICopyable + { + public: + using vec_t = vvi::vector; + + foo(size_t sz) : m_vec(sz) {} + + vec_t& vec() {return m_vec;} + void doSomething() {}; + + VVI_IMPL_DEEPCOPY(foo, m_vec) + private: + vec_t m_vec; + } + VVI_IMPL_END + +Type Alias View Interface +------------------------- + +Declaring the interface class names and the *VIEW* interface are the same as above. However we would like to expose the type information for ``vec_t``. Defining the type alias in the *VIEW* interface makes it publicly visible to both *VIEW* and *VALUE* interfaces. + +.. code-block:: c++ + + #ifdef VVI_ENABLED + class foo; + + #define fooView__(code) PTR_VIEW_METACLASS_DECL( \ + (foo), (fooView), (vvimpl::foo), (code) ) + + class fooView__( + public: + using vec_t = ImplType::vec_t; + ); + +Custom Ctor Value Interface +--------------------------- + +When defining a non-default constructor for *VALUE* interfaces we need to forward the arguments with ``VVI_VALUE_CTOR_ARGS()`` as below. The argument list needs to be passed within ``()``. + +.. code-block:: c++ + + #define foo__(code) PTR_VALUE_METACLASS_DECL( \ + (foo), (fooView), (vvimpl::foo), (code) ) + + class foo__( + public: + foo(size_t sz) : VVI_VALUE_CTOR_ARGS( (sz) ) {} + +``vvi::vector`` is an alias to a CHAI container type when VVI is enabled. CHAI containers use reference semantics and need to have their allocations released manually. The containers lifetime should be tied to the lifetime of ``foo``. Therefore, we need to manually free the underlying ``m_vec`` member in the destructor of the value class. + +.. code-block:: c++ + + ~foo() { VVI_IMPL_INST().m_vec.free(); } + + void doSomething() { VVI_IMPL_INST().doSomething(); } + vvi::vector& vec() { return VVI_IMPL_INST().vec(); } + ); + #endif + +Template Class Interface +======================== + +Templated classes can be used with the VVI Pattern. + +.. code-block:: c++ + + VVI_IMPL_BEGIN + template + class foo + { + T doSomething() {}; + } + VVI_IMPL_END + +Remember to template the forward declaration. + +.. code-block:: c++ + + #ifdef VVI_ENABLED + template + class foo; + +Template Interface Declarations +------------------------------- + +When defining the ``_METACLASS_DECL`` macros we need to template the exclusive types to the interface we are representing: + + +* For the ``VIEW`` macro: template **only** the ``value`` and ``impl`` type-names. +* For the ``VALUE`` macro : Template **only** the ``view`` and ``impl`` type-names. + +.. code-block:: c++ + + #define fooView__(code) PTR_VIEW_METACLASS_DECL( \ + (foo), (fooView), (vvimpl::foo), (code) ) + + #define foo__(code) PTR_VALUE_METACLASS_DECL( \ + (foo), (fooView), (vvimpl::foo), (code) ) + +In the interface declarations we need to ensure we template the interfaces. + +.. code-block:: c++ + + template + class fooView__(); + + template + class foo__( + public: + T doSomething() { return VVI_IMPL_INST().doSomething(); } + ); + + #endif + +Abstract & Polymorphic Class Interface +====================================== + +One of the strongest features of the VVI Pattern is the ability to use abstract class interfaces on the GPU. Below we are converting a *very* basic example of a polymorphic class for use in VVI. + +Implmentation Classes w/ Virtual Functions +------------------------------------------ + +Base classes with virtual interfaces need to inherit from ``vvi::poly``. This allows VVI to perform a specialized copies between the host and device such that the vitual table is preserved across execution spaces. + +.. note:: + + The implementation classes **MUST** provide default constructors in order to be used with VVI. + + +.. code-block:: c++ + + VVI_IMPL_BEGIN + class base : public chai::CHAIPoly + { + virtual int doSomething() = 0; + }; + + class derived : public base + { + derived() : base () {} + virtual int doSomething() override {} + }; + VVI_IMPL_END + +We need to forward declare for both ``base`` and ``derived``. + +.. code-block:: c++ + + #ifdef VVI_ENABLED + class base; + class derived; + +.. code-block:: c++ + + #define baseView__(code) PTR_VIEW_METACLASS_DECL( \ + (base), (baseView), (vvimpl::base), (code) ) + #define base__(code) PTR_VALUE_METACLASS_DECL( \ + (base), (baseView), (vvimpl::base), (code) ) + + #define derivedView__(code) PTR_VIEW_METACLASS_DECL( \ + (derived), (derivedView), (vvimpl::derived), (code) ) + #define derived__(code) PTR_VALUE_METACLASS_DECL( \ + (derived), (derivedView), (vvimpl::derived), (code) ) + +Base Class Interface +-------------------- + +``base`` is an abstract interface class. It should **NEVER** be constructable. However, we may want to declare the class to query type name information. We use ``VVI_DELETED_INTERFACE()`` to ensure a ``base`` *VALUE* type is not constructed. + +.. code-block:: c++ + + class baseView__(); + + class base__( + VVI_DELETED_INTERFACE(base) + ); + + #endif + +.. tip:: + + There exist shorthand macros for interfaces, the set of which are detailed in sections below. + + .. code-block:: c++ + + class PTR_VIEW_METACLASS_DEFAULT( (base), (baseView), (vvimpl::base) ) + class PTR_VALUE_METACLASS_DELETED( (base), (baseView), (vvimpl::base) ) + + +Derived Class Interface +----------------------- + +The ``derived`` interface can be defined similarly to other *VALUE* and *VIEW* interfaces. Here we add ``VVI_UPCAST_CONVERSION_OP()`` to allow us to upcast a derived type to the base. This allows conversion from ``derivedView -> baseView`` as well as conversions of ``derived -> baseView``. + +.. code-block:: c++ + + class derivedView__( + VVI_UPCAST_CONVERSION_OP(baseView) + ); + + class derived__( + public: + T doSomething() { return VVI_IMPL_INST().doSomething(); } + ); + + #endif + +***************** +Macro Definitions +***************** + +VALUE Interface Definition Macros +================================= + +* ``VVI_VALUE_CTOR_ARGS((args))`` : Forward arguments of a non-default constructor for a *VALUE* interface. +* ``VVI_VALUE_DEF_CTOR(value_t)`` : Define default constructor for a *VALUE* interface. + + +Interface Behavior Macros +========================= + +* ``VVI_DELETED_INTERFACE(type)`` : Delete the constructor, copy constructor and assignment operator. +* ``VVI_UPCAST_CONVERSION_OP(parent_t)`` : Define an implicit conversion operator to the defined parent type. + + +Class Declaration Macros +======================== + +* ``PTR_VALUE_METACLASS_DECL((value_t), (view_t), (impl_t), (code))`` +* ``REF_VALUE_METACLASS_DECL((value_t), (view_t), (impl_t), (code))`` +* ``PTR_VIEW_METACLASS_DECL((value_t), (view_t), (impl_t), (code))`` +* ``REF_VIEW_METACLASS_DECL((value_t), (view_t), (impl_t), (code))`` + +* ``PTR_VIEW_METACLASS_DEFAULT((value_t), (view_t), (impl_t))`` +* ``PTR_VALUE_METACLASS_DEFAULT((value_t), (view_t), (impl_t))`` + +* ``PTR_VALUE_METACLASS_DELETED((value_t), (view_t), (impl_t))`` diff --git a/docs/developer/design_docs.rst b/docs/developer/design_docs.rst index 160d101d1..e2a43bfec 100644 --- a/docs/developer/design_docs.rst +++ b/docs/developer/design_docs.rst @@ -9,5 +9,6 @@ Welcome to Spheral's design documentation. This documentation is a work in progr design/python_module_install.rst design/spack_uberenv_design.rst + design/value_view_interface.rst diff --git a/extern/chai b/extern/chai index 79cb51f83..eb235813b 160000 --- a/extern/chai +++ b/extern/chai @@ -1 +1 @@ -Subproject commit 79cb51f8347c05b38ec2f0feaaa640b46bd1c440 +Subproject commit eb235813b5474fb649702f05ad4415855fdb4856 diff --git a/scripts/devtools/host-config-build.py b/scripts/devtools/host-config-build.py index 0c90c45e8..9e04332b3 100755 --- a/scripts/devtools/host-config-build.py +++ b/scripts/devtools/host-config-build.py @@ -31,6 +31,9 @@ def parse_args(): parser.add_argument('--build', action='store_true', help='Run make -j install after configuring build dirs.') + parser.add_argument('--test', action='store_true', + help='Run make test after building.') + parser.add_argument('--lc-modules', type=str, default="", help='LC Modules to use during build, install and smoke test. This is not used if --build is not enabled.') @@ -109,5 +112,13 @@ def main(): sexe("{0} {1} --build . -j 48 --target install".format(ml_cmd, cmake_cmd), echo=True, ret_output=False) + if args.test: + print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") + print("~~~~~ Running make test Spheral") + print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") + + sexe("make test".format(), echo=True, ret_output=False) + + if __name__ == "__main__": main() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6feaa5e6d..f6b1ad2ae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,7 +6,6 @@ include(${SPHERAL_CMAKE_MODULE_PATH}/spheral/SpheralAddLibs.cmake) include(${SPHERAL_CMAKE_MODULE_PATH}/spheral/SpheralInstallPythonFiles.cmake) include_directories(.) -include_directories(${PROJECT_BINARY_DIR}/src) set(SPHERAL_PYTHON_INSTALL ${PROJECT_BINARY_DIR}/lib) @@ -75,9 +74,6 @@ if(NOT ENABLE_CXXONLY) PYB11) endif() -configure_file(config.hh.in - ${PROJECT_BINARY_DIR}/src/config.hh) - foreach(_package ${_packages}) add_subdirectory(${_package}) endforeach() diff --git a/src/Kernel/TableKernel.cc b/src/Kernel/TableKernel.cc index 7ff2796c9..fe9bf9545 100644 --- a/src/Kernel/TableKernel.cc +++ b/src/Kernel/TableKernel.cc @@ -169,6 +169,30 @@ gradf1Integral(const KernelType& W, numbins); } +//------------------------------------------------------------------------------ +// Functors for building interpolation of kernel +//------------------------------------------------------------------------------ +template +struct Wlookup { + const KernelType& mW; + Wlookup(const KernelType& W): mW(W) {} + double operator()(const double x) const { return mW(x, 1.0); } +}; + +template +struct gradWlookup { + const KernelType& mW; + gradWlookup(const KernelType& W): mW(W) {} + double operator()(const double x) const { return mW.grad(x, 1.0); } +}; + +template +struct grad2Wlookup { + const KernelType& mW; + grad2Wlookup(const KernelType& W): mW(W) {} + double operator()(const double x) const { return mW.grad2(x, 1.0); } +}; + } // anonymous //------------------------------------------------------------------------------ @@ -184,9 +208,9 @@ TableKernel::TableKernel(const KernelType& kernel, mNumPoints(numPoints), mMinNperh(std::max(minNperh, 1.1/kernel.kernelExtent())), mMaxNperh(maxNperh), - mInterp(0.0, kernel.kernelExtent(), numPoints, [&](const double x) { return kernel(x, 1.0); }), - mGradInterp(0.0, kernel.kernelExtent(), numPoints, [&](const double x) { return kernel.grad(x, 1.0); }), - mGrad2Interp(0.0, kernel.kernelExtent(), numPoints, [&](const double x) { return kernel.grad2(x, 1.0); }), + mInterp(0.0, kernel.kernelExtent(), numPoints, Wlookup(kernel)), + mGradInterp(0.0, kernel.kernelExtent(), numPoints, gradWlookup(kernel)), + mGrad2Interp(0.0, kernel.kernelExtent(), numPoints, grad2Wlookup(kernel)), mNperhLookup(), mWsumLookup() { diff --git a/src/Utilities/ManagedVector.hh b/src/Utilities/ManagedVector.hh new file mode 100644 index 000000000..ad0fc086a --- /dev/null +++ b/src/Utilities/ManagedVector.hh @@ -0,0 +1,350 @@ +#ifndef __Spheral_lvarray_hh__ +#define __Spheral_lvarray_hh__ + +#include "config.hh" +//#include "LvArray/Array.hpp" +//#include "LvArray/ChaiBuffer.hpp" +#include "chai/ManagedArray.hpp" + +#include + +constexpr uint32_t pow2_ceil(uint32_t v) { + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + + return v; +} + +namespace Spheral { + +//#define SPHERAL_CALLBACK_ENABLED +#define STD_OUT_LOG + +#ifdef STD_OUT_LOG +#define SPHERAL_LOG(A, B) std::cout << #A << " : " << B << std::endl; +#else +#define SPHERAL_LOG(A, B) UMPIRE_LOG(A, B) +#endif + + + +//#define MV_VALUE_SEMANTICS + +template +class ManagedVector; + +template +ManagedVector deepCopy(ManagedVector const& array); + +template +bool compare(ManagedVector const&, ManagedVector const&); + +template +class ManagedVector: + private chai::ManagedArray{ + using MA = chai::ManagedArray; + +public: + + inline static constexpr size_t initial_capacity = 8u; + + using MA::setUserCallback; + + using iterator = DataType*; + using const_iterator = const DataType*; + + iterator begin() { return MA::begin(); } + const_iterator begin() const { return MA::begin(); } + + iterator end() { return begin() + m_size; } + const_iterator end() const { return begin() + m_size; } + + // --------------------- + // Constructors + // --------------------- + SPHERAL_HOST_DEVICE ManagedVector() : + MA() + { +#if !defined(SPHERAL_GPU_ACTIVE) + setCallback(); + +#endif // SPHERAL_GPU_ACTIVE + } + + SPHERAL_HOST_DEVICE ManagedVector(size_t elems) : + MA(), + m_size(elems) + { +#if !defined(SPHERAL_GPU_ACTIVE) + MA::allocate(m_size < initial_capacity ? initial_capacity: pow2_ceil(m_size), chai::CPU, getCallback()); + for (size_t i = 0; i < m_size; i++) new (&MA::operator[](i)) DataType(); + MA::registerTouch(chai::CPU); +#endif // SPHERAL_GPU_ACTIVE + } + + SPHERAL_HOST ManagedVector(size_t elems, DataType identity) : + MA(), + m_size(elems) + { +#if !defined(SPHERAL_GPU_ACTIVE) + MA::allocate(m_size < initial_capacity ? initial_capacity: pow2_ceil(m_size), chai::CPU, getCallback()); + for (size_t i = 0; i < m_size; i++) new (&MA::operator[](i)) DataType(identity); + MA::registerTouch(chai::CPU); +#endif // SPHERAL_GPU_ACTIVE + } + +#ifdef MV_VALUE_SEMANTICS + // --------------------- + // Destructor + // --------------------- + SPHERAL_HOST ~ManagedVector() + { + MA::free(); + } +#endif + using MA::move; + using MA::free; + + // --------------------- + // Copy Constructor + // --------------------- +#ifdef MV_VALUE_SEMANTICS + SPHERAL_HOST_DEVICE constexpr inline ManagedVector(ManagedVector const& rhs) noexcept : + ManagedVector(rhs.m_size) + {printf("MV Copy w/ Value Semantics.\n"); + for (size_t i = 0; i < m_size; i++) new (&MA::operator[](i)) DataType(rhs[i]); + } +#else + SPHERAL_HOST_DEVICE constexpr inline ManagedVector(ManagedVector const& rhs) noexcept : MA(rhs), m_size(rhs.m_size) {} +#endif + + // --------------------- + // Assignment + // --------------------- +#ifdef MV_VALUE_SEMANTICS + SPHERAL_HOST_DEVICE ManagedVector& operator=(ManagedVector const& rhs) { + if (capacity() != rhs.capacity()) MA::reallocate(rhs.capacity()); + m_size = rhs.m_size; + for (size_t i = 0; i < m_size; i++) new (&MA::operator[](i)) DataType(rhs[i]); + return *this; + } +#else + SPHERAL_HOST_DEVICE ManagedVector& operator=(ManagedVector const& rhs) { + MA::operator=(rhs); + m_size = rhs.m_size; + return *this; + } +#endif + + // --------------------- + // Equivalence + // --------------------- +#ifdef MV_VALUE_SEMANTICS + SPHERAL_HOST bool operator==(ManagedVector const& rhs) const { + if (m_size != rhs.m_size) return false; + for (size_t i = 0; i < m_size; i++) { + if (MA::operator[](i) != rhs[i]) { + return false; + } + } + return true; + } +#else + SPHERAL_HOST_DEVICE bool operator==(ManagedVector const& rhs) const { + if (m_size != rhs.m_size) return false; + return MA::operator==(rhs); + } +#endif + SPHERAL_HOST_DEVICE bool operator!=(ManagedVector const& rhs) const { + return !(*this == rhs); + } + + SPHERAL_HOST void push_back(const DataType& value) { + if (capacity() == 0) MA::allocate(initial_capacity, chai::CPU, getCallback()); + if (m_size >= capacity()) MA::reallocate(pow2_ceil(m_size + 1)); + new(&MA::operator[](m_size)) DataType(value); + m_size++; + } + + SPHERAL_HOST void push_back(DataType&& value) { + if (capacity() == 0) MA::allocate(initial_capacity, chai::CPU, getCallback()); + if (m_size >= capacity()) MA::reallocate(pow2_ceil(m_size + 1)); + //MA::operator[](m_size) = std::move(value); + new(&MA::operator[](m_size)) DataType(value); + m_size++; + } + template + SPHERAL_HOST + DataType& emplace_back(Args&&... args) { + if (capacity() == 0) MA::allocate(initial_capacity, chai::CPU, getCallback()); + if (m_size >= capacity()) MA::reallocate(pow2_ceil(m_size + 1)); + + new(&MA::data()[m_size]) DataType(std::forward(args)...); + return MA::data()[m_size++]; + } + + SPHERAL_HOST + void reserve(size_t c) { + if (capacity() == 0) MA::allocate(c < initial_capacity ? initial_capacity: pow2_ceil(c), chai::CPU, getCallback()); + if (c >= capacity()) MA::reallocate(pow2_ceil(c)); + } + + SPHERAL_HOST + void resize(size_t size) { + const size_t old_size = m_size; + + if (old_size != size){ + if (old_size < size) { + if (capacity() == 0) MA::allocate(size < initial_capacity ? initial_capacity: pow2_ceil(size), chai::CPU, getCallback()); + else if (capacity() < size) MA::reallocate(pow2_ceil(size)); + for (size_t i = old_size; i < size; i++) new(&MA::data(chai::CPU, false)[i]) DataType(); + } + if (old_size > size) { + destroy(begin() + old_size, begin() + size); + } + m_size = size; + MA::registerTouch(chai::CPU); + } + } + + SPHERAL_HOST + void insert(iterator pos, DataType const& value) { + auto delta = std::distance(begin(), pos); + if (m_size == 0) { + push_back(value); + }else { + resize(m_size + 1); + for (iterator it = end() - 1; it > begin() + delta; it--) { + *it = std::move(*(it - 1)); + //*it = (*(it - 1)); + } + new(begin() + delta) DataType(value); + } + } + + SPHERAL_HOST + void clear() { + destroy(begin(), end()); + m_size = 0; + } + + SPHERAL_HOST + void erase(iterator pos) { + for (iterator it = pos; it < end(); it++) { + *it = std::move(*(it + 1)); + } + m_size--; + } + + SPHERAL_HOST_DEVICE size_t capacity() const {return MA::size();} + SPHERAL_HOST_DEVICE size_t size() const {return m_size;} + + SPHERAL_HOST_DEVICE DataType& operator[](size_t idx) {return MA::data()[idx]; } + SPHERAL_HOST_DEVICE DataType& operator[](size_t idx) const {return MA::data()[idx]; } + + + // ******************************************************* + // Required to Allow ManagedVector to be properly CHAICopyable + SPHERAL_HOST_DEVICE ManagedVector& operator=(std::nullptr_t) { MA::operator=(nullptr); return *this; } + SPHERAL_HOST_DEVICE void shallowCopy(const ManagedVector& other) { + m_size=other.m_size; + MA::shallowCopy(other); + } + // ******************************************************* + + //SPHERAL_HOST operator std::vector() const { return std::vector(begin(), end()); } + + template< typename U=ManagedVector< DataType > > + SPHERAL_HOST + void setCallback() { + MA::setUserCallback( getCallback() ); + } + + template< typename U=ManagedVector< DataType > > + SPHERAL_HOST + auto getCallback() { +//#ifdef SPHERAL_CALLBACK_ENABLED +// std::string const typeString = LvArray::system::demangleType< U >(); +// return [typeString] (const chai::PointerRecord* record, chai::Action action, chai::ExecutionSpace exec) { +// std::string const size = LvArray::system::calculateSize(record->m_size); +// std::string const paddedSize = std::string( 9 - size.size(), ' ' ) + size; +// char const * const spaceStr = ( exec == chai::CPU ) ? "HOST " : "DEVICE"; +// +// if (action == chai::Action::ACTION_MOVE){ +// SPHERAL_LOG(Info, "Moved " << paddedSize << " to the " << spaceStr << ": " << typeString << " @ " << record->m_pointers[exec] ) +// } +// if (action == chai::Action::ACTION_ALLOC){ +// SPHERAL_LOG(Info, "Allocated on " << spaceStr << " " << paddedSize << " : " << typeString << " @ " << record->m_pointers[exec] ) +// } +// if (action == chai::Action::ACTION_FREE){ +// SPHERAL_LOG(Info, "Deallocated " << paddedSize << " : " << typeString << " @ " << record->m_pointers[exec] ) +// } +// }; +//#else + return [](const chai::PointerRecord* , chai::Action , chai::ExecutionSpace ) {}; +//#endif + } + +private: + size_t m_size = 0; + + SPHERAL_HOST void destroy(iterator first, iterator last) { + if ( !std::is_trivially_destructible< DataType >::value ) { + for (iterator it = first; it < last; it++) { + *it = DataType(); + } + } + } + + ManagedVector(MA const& managed_array) : MA(managed_array), m_size(managed_array.size()) {} + + template + friend ManagedVector deepCopy(ManagedVector const& array); + + template + friend bool compare(ManagedVector const& lhs, ManagedVector const& rhs); + +}; + +template +inline +bool compare(ManagedVector const& lhs, ManagedVector const& rhs) +{ + if (lhs.size() != rhs.size()) return false; + for (size_t i = 0; i < lhs.size(); i++) { + if (lhs[i] != rhs[i]) { + return false; + } + } + return true; +} + + +template +inline +ManagedVector deepCopy(ManagedVector const& array) +{ + ManagedVector copy(array.size()); + for (size_t i = 0; i < array.size(); i++) new (©[i]) U(array[i]); + return copy; +} + +} // namespace Spheral + +//#include "SphArrayInline.hh" + +#else + +//// Forward declare the SphArrayIterator class. +//namespace Spheral { +// template class SphArrayIterator ; +//} // namespace Spheral + +#endif // __Spheral_lvarray_hh__ + + diff --git a/src/Utilities/QuadraticInterpolator.cc b/src/Utilities/QuadraticInterpolator.cc index 7d4e6edfb..6c03a0dd6 100644 --- a/src/Utilities/QuadraticInterpolator.cc +++ b/src/Utilities/QuadraticInterpolator.cc @@ -10,16 +10,7 @@ namespace Spheral { -//------------------------------------------------------------------------------ -// Default constructor -//------------------------------------------------------------------------------ -QuadraticInterpolator::QuadraticInterpolator(): - mN1(), - mXmin(), - mXmax(), - mXstep(), - mcoeffs() { -} +VVI_IMPL_BEGIN //------------------------------------------------------------------------------ // Constructor with sampled values @@ -77,22 +68,6 @@ QuadraticInterpolator::initialize(double xmin, } } -//------------------------------------------------------------------------------ -// Destructor -//------------------------------------------------------------------------------ -QuadraticInterpolator::~QuadraticInterpolator() { -} - -//------------------------------------------------------------------------------ -// Equivalence -//------------------------------------------------------------------------------ -bool -QuadraticInterpolator:: -operator==(const QuadraticInterpolator& rhs) const { - return ((mN1 == rhs.mN1) and - (mXmin == rhs.mXmin) and - (mXmax == rhs.mXmax) and - (mcoeffs == rhs.mcoeffs)); -} +VVI_IMPL_END -} +} // namespace Spheral diff --git a/src/Utilities/QuadraticInterpolator.hh b/src/Utilities/QuadraticInterpolator.hh index 06a9fd169..fb2e3d51c 100644 --- a/src/Utilities/QuadraticInterpolator.hh +++ b/src/Utilities/QuadraticInterpolator.hh @@ -12,17 +12,22 @@ #include #include +#include "Utilities/ValueViewInterface.hh" + namespace Spheral { +VVI_IMPL_BEGIN + class QuadraticInterpolator { public: + using CoeffsType = vvi::vector; //--------------------------- Public Interface ---------------------------// // Constructors, destructors template QuadraticInterpolator(double xmin, double xmax, size_t n, const Func& F); + QuadraticInterpolator(double xmin, double xmax, const std::vector& yvals); - QuadraticInterpolator(); - ~QuadraticInterpolator(); + SPHERAL_HOST_DEVICE QuadraticInterpolator() = default; // Initialize after construction, either with a function or tabulated values template @@ -33,34 +38,95 @@ public: bool operator==(const QuadraticInterpolator& rhs) const; // Interpolate for the y value - double operator()(const double x) const; - double prime(const double x) const; // First derivative - double prime2(const double x) const; // Second derivative + SPHERAL_HOST_DEVICE double operator()(const double x) const; + SPHERAL_HOST_DEVICE double prime(const double x) const; // First derivative + SPHERAL_HOST_DEVICE double prime2(const double x) const; // Second derivative // Same as above, but use a pre-computed table position (from lowerBound) - double operator()(const double x, const size_t i0) const; - double prime(const double x, const size_t i0) const; // First derivative - double prime2(const double x, const size_t i0) const; // Second derivative + SPHERAL_HOST_DEVICE double operator()(const double x, const size_t i0) const; + SPHERAL_HOST_DEVICE double prime(const double x, const size_t i0) const; // First derivative + SPHERAL_HOST_DEVICE double prime2(const double x, const size_t i0) const; // Second derivative // Return the lower bound index in the table for the given x coordinate - size_t lowerBound(const double x) const; + SPHERAL_HOST_DEVICE size_t lowerBound(const double x) const; // Allow read access the internal data representation - size_t size() const; // The size of the tabulated coefficient arrays - double xmin() const; // Minimum x coordinate for table - double xmax() const; // Maximum x coordinate for table - double xstep() const; // delta x between tabulated values - const std::vector& coeffs() const; // the fitting coefficients + SPHERAL_HOST_DEVICE size_t size() const; // The size of the tabulated coefficient arrays + SPHERAL_HOST_DEVICE double xmin() const; // Minimum x coordinate for table + SPHERAL_HOST_DEVICE double xmax() const; // Maximum x coordinate for table + SPHERAL_HOST_DEVICE double xstep() const; // delta x between tabulated values + SPHERAL_HOST_DEVICE const CoeffsType& coeffs() const; // the fitting coefficients + VVI_IMPL_DEEPCOPY(QuadraticInterpolator, mcoeffs) + VVI_IMPL_COMPARE(QuadraticInterpolator, mN1, mXmin, mXmax, mcoeffs) private: //--------------------------- Private Interface --------------------------// // Member data size_t mN1; double mXmin, mXmax, mXstep; - std::vector mcoeffs; + CoeffsType mcoeffs; }; -} +VVI_IMPL_END + +#ifdef VVI_ENABLED + +class QuadraticInterpolator; + +class PTR_VIEW_METACLASS_DEFAULT \ + ((QuadraticInterpolator), (QuadraticInterpolatorView), (vvimpl::QuadraticInterpolator)) + +#define QuadraticInterpolator__(code) PTR_VALUE_METACLASS_DECL \ + ((QuadraticInterpolator), (QuadraticInterpolatorView), (code)) + +class QuadraticInterpolator__( + + using CoeffsType = typename ImplType::CoeffsType; + + template + QuadraticInterpolator(const double xmin, + const double xmax, + const size_t n, + const Func& F) + : VVI_VALUE_CTOR_ARGS((xmin,xmax,n,F)) {} + + QuadraticInterpolator(double xmin, + double xmax, + const std::vector& yvals) + : VVI_VALUE_CTOR_ARGS((xmin,xmax,yvals)) {} + + VVI_VALUE_DEF_CTOR(QuadraticInterpolator) + + // Alternatively initialize from tabulated values + void initialize(const double xmin, const double xmax, + const std::vector& yvals) + { VVI_IMPL_INST().initialize(xmin, xmax, yvals); } + + // Interpolate for the y value + double operator()(const double x) const { return VVI_IMPL_INST()(x); } + double prime(const double x) const { return VVI_IMPL_INST().prime(x); } + double prime2(const double x) const { return VVI_IMPL_INST().prime2(x); } + + // Same as above, but use a pre-computed table position (from lowerBound) + double operator()(const double x, const size_t i0) const { return VVI_IMPL_INST()(x, i0); } + double prime(const double x, const size_t i0) const { return VVI_IMPL_INST().prime(x, i0); } + double prime2(const double x, const size_t i0) const { return VVI_IMPL_INST().prime2(x, i0); } + + // Return the lower bound index in the table for the given x coordinate + size_t lowerBound(const double x) const { return VVI_IMPL_INST().lowerBound(x); } + + // Allow read access the internal data representation + size_t size() const { return VVI_IMPL_INST().size(); } + double xmin() const { return VVI_IMPL_INST().xmin(); } + double xmax() const { return VVI_IMPL_INST().xmax(); } + double xstep() const { return VVI_IMPL_INST().xstep(); } + const vvi::vector& coeffs() const { return VVI_IMPL_INST().coeffs(); } +); + +#endif // VVI_ENABLED + + +} // namespace Spheral #include "QuadraticInterpolatorInline.hh" diff --git a/src/Utilities/QuadraticInterpolatorInline.hh b/src/Utilities/QuadraticInterpolatorInline.hh index ed007778d..a054e1f88 100644 --- a/src/Utilities/QuadraticInterpolatorInline.hh +++ b/src/Utilities/QuadraticInterpolatorInline.hh @@ -5,6 +5,8 @@ namespace Spheral { +VVI_IMPL_BEGIN + //------------------------------------------------------------------------------ // Construct to fit the given function //------------------------------------------------------------------------------ @@ -44,6 +46,18 @@ QuadraticInterpolator::initialize(double xmin, this->initialize(xmin, xmax, yvals); } +//------------------------------------------------------------------------------ +// Equivalence +//------------------------------------------------------------------------------ +bool +inline +QuadraticInterpolator::operator==(const QuadraticInterpolator& rhs) const { + return ((mN1 == rhs.mN1) and + (mXmin == rhs.mXmin) and + (mXmax == rhs.mXmax) and + (mcoeffs == rhs.mcoeffs)); +} + //------------------------------------------------------------------------------ // Interpolate for the given x value. //------------------------------------------------------------------------------ @@ -105,7 +119,7 @@ QuadraticInterpolator::prime2(const double /*x*/, inline size_t QuadraticInterpolator::lowerBound(const double x) const { - const auto result = 3u*std::min(mN1, size_t(std::max(0.0, x - mXmin)/mXstep)); + const auto result = 3u*RAJA_MIN(mN1, size_t(RAJA_MAX(0.0, x - mXmin)/mXstep)); ENSURE(result <= 3u*mN1); return result; } @@ -138,9 +152,11 @@ QuadraticInterpolator::xstep() const { } inline -const std::vector& +const vvi::vector& QuadraticInterpolator::coeffs() const { return mcoeffs; } +VVI_IMPL_END + } diff --git a/src/Utilities/ValueViewInterface.hh b/src/Utilities/ValueViewInterface.hh new file mode 100644 index 000000000..e66027724 --- /dev/null +++ b/src/Utilities/ValueViewInterface.hh @@ -0,0 +1,171 @@ +#ifndef __SPHERAL_VALUEVIEWINTERFACE__ +#define __SPHERAL_VALUEVIEWINTERFACE__ + +#include "config.hh" +#include "ManagedVector.hh" +#include "chai/ManagedSharedPtr.hpp" +#include "ValueViewInterfaceImpl.hh" + +#include + + +namespace Spheral { + +// An interface cass to ensure all required methods are defined by Data +// classes that need to be automatically copied by another CHAICopyable or +// SPHERALCopyable class. +using SPHERALCopyable = chai::CHAICopyable; + +} // namespace Spheral + +// ---------------------------------------------------------------------------- +// IMPL class declaration macros +// ---------------------------------------------------------------------------- + +#if defined(VVI_ENABLED) +#define VVI_IMPL_BEGIN namespace vvimpl { +#define VVI_IMPL_END } // namespace vvimpl +#else +#define VVI_IMPL_BEGIN +#define VVI_IMPL_END +#endif // defined(VVI_ENABLED) + +#if defined(VVI_ENABLED) + +#define VVI_IMPL_DEEPCOPY(...) VVI_IMPL_DEEPCOPY__( __VA_ARGS__ , void) +#define VVI_IMPL_COMPARE(...) VVI_IMPL_COMPARE__( __VA_ARGS__, void) + +#else + +#define VVI_IMPL_DEEPCOPY(...) +#define VVI_IMPL_COMPARE(...) + +#endif + +// ---------------------------------------------------------------------------- +// VALUE class declaration macros +// ---------------------------------------------------------------------------- + +#define PTR_VALUE_METACLASS_DECL(value_t, view_t, code) \ + VALUE_METACLASS_DECL_BEGIN((UNPACK value_t), (UNPACK view_t)) \ + REF_OPERATOR() \ + UNPACK code \ + VALUE_METACLASS_DECL_END() + + +#define REF_VALUE_METACLASS_DECL(value_t, view_t, code) \ + VALUE_METACLASS_DECL_BEGIN((UNPACK value_t), (UNPACK view_t)) \ + UNPACK code \ + VALUE_METACLASS_DECL_END() + +// ---------------------------------------------------------------------------- +// VIEW class declaration macros +// ---------------------------------------------------------------------------- + +#define PTR_VIEW_METACLASS_DECL(value_t, view_t, impl_t, code) \ + VIEW_METACLASS_DECL_BEGIN((UNPACK value_t), (UNPACK view_t), (UNPACK impl_t)) \ + POINTER_SYNTAX_OPERATORS() \ + UNPACK code \ + VIEW_METACLASS_DECL_END() + +#define REF_VIEW_METACLASS_DECL(value_t, view_t, impl_t, code) \ + VIEW_METACLASS_DECL_BEGIN((UNPACK value_t), (UNPACK view_t), (UNPACK impl_t)) \ + UNPACK code \ + VIEW_METACLASS_DECL_END() + +#define PTR_VIEW_METACLASS_DEFAULT(value_t, view_t, impl_t) \ + PTR_VIEW_METACLASS_DECL( value_t, view_t, impl_t, ( ) ); + +#define PTR_VALUE_METACLASS_DEFAULT(value_t, view_t, impl_t) \ + PTR_VALUE_METACLASS_DECL( value_t, view_t, impl_t, ( ) ); + +#define PTR_VALUE_METACLASS_DELETED(value_t, view_t, impl_t) \ + PTR_VALUE_METACLASS_DECL( value_t, view_t, ( VVI_DELETED_INTERFACE(UNPACK value_t) ) ); + +// ---------------------------------------------------------------------------- +// VALUE class definition macros +// ---------------------------------------------------------------------------- + +#define VVI_VALUE_CTOR_ARGS(args) \ + Base(VVI_MAKE_SHARED(UNPACK args)) + +#define VVI_VALUE_DEF_CTOR(value_t) \ + value_t() : Base() {} + +#define VVI_VALUE_COPY_CTOR(value_t) \ + value_t(value_t const& rhs) : Base(rhs) {} + +/* +#define VVI_VALUE_ASSIGNEMT_OP() \ + ValueType& operator=(ValueType const& rhs) { \ + Base::operator=(rhs); \ + return *this; \ + } + +#define VVI_VALUE_EQ_OP() \ + bool operator==(const ValueType& rhs) const \ + { return compare(VVI_SPTR_DATA_REF__(), rhs.VVI_SPTR_DATA_REF__()); } + +#define VVI_VALUE_TYPE_DEFAULT_ASSIGNMENT_OP(value_t) \ + value_t& operator=(value_t const& rhs) { \ + ViewType::operator=( ViewType( new ImplType(deepCopy(rhs.ViewInterfaceType::sptr_data())) ) ); \ + return *this; \ + } +*/ + +// ---------------------------------------------------------------------------- +// VIEW class definition macros +// ---------------------------------------------------------------------------- + +/* +#define VVI_VIEW_COPY_CTOR(view_t) \ + view_t(view_t const& rhs) = default; + +#define VVI_VIEW_ASSIGNEMT_OP() \ + SPHERAL_HOST_DEVICE ViewType& operator=(ViewType const& rhs) = default; + +#define VVI_VIEW_EQ_OP() \ + bool operator==(const ViewType& rhs) const \ + { return sptr_data() == rhs.sptr_data(); } +*/ + +// ---------------------------------------------------------------------------- +// Special case macros +// ---------------------------------------------------------------------------- + +// This is used for allowing implicit upcast conversions to a Base Class +#define VVI_UPCAST_CONVERSION_OP(parent_t) \ +public: \ + operator parent_t() const {return parent_t(this->sptr());} + +// This is used when declaring a Value interface of an abstract class. +#define VVI_DELETED_INTERFACE(type) \ +public: \ + type() = delete; \ + type(type const&) = delete; \ + type& operator=(type const&) = delete; + +/* +// Used for allowing default class behavior from the compiler. Typically this +// will be used for invoking a basic view interface when using poiter syntax. +#define VVI_VIEW_DEFAULT(view_t) \ + VVI_VIEW_DEF_CTOR(view_t) \ + VVI_VIEW_COPY_CTOR(view_t) \ + VVI_VIEW_ASSIGNEMT_OP() +*/ + +// Creates a function that allows interface objects to access member variables. +// through a function of the same name as the variable itself. +#define VVI_MEMBER_ACCESSOR(type, var) \ + SPHERAL_HOST_DEVICE type & var() { return Base::get()->var; } \ + SPHERAL_HOST_DEVICE type & var() const { return Base::get()->var; } + + +// Grab the actual insatance of the implementation class the interface is accessing. +// Used when calling out to functions in the interface definitions. +//#define VVI_IMPL_INST this->sptr_data() +#define VVI_IMPL_INST VVI_SPTR_DATA_REF__ + + + +#endif // __SPHERAL_VALUEVIEWINTERFACE__ diff --git a/src/Utilities/ValueViewInterfaceImpl.hh b/src/Utilities/ValueViewInterfaceImpl.hh new file mode 100644 index 000000000..07b0cb800 --- /dev/null +++ b/src/Utilities/ValueViewInterfaceImpl.hh @@ -0,0 +1,275 @@ +#ifndef __SPHERAL_VALUEVIEWINTERFACEIMPL__ +#define __SPHERAL_VALUEVIEWINTERFACEIMPL__ + +// Macro tool for unpacking types with multiple template arguments. +#define UNPACK( ... ) __VA_ARGS__ + +// ---------------------------------------------------------------------------- +// Class definitions for Value and View Intreface class structure. +// ---------------------------------------------------------------------------- + +namespace vvi { + +template +#if defined(VVI_ENABLED) +using shared_ptr = chai::ManagedSharedPtr; +#define VVI_MAKE_SHARED chai::make_shared +#else +using shared_ptr = std::shared_ptr; +#define VVI_MAKE_SHARED std::make_shared +#endif + +template +#if defined(VVI_ENABLED) +using vector = ::Spheral::ManagedVector; +//using vector = std::vector; +#else +using vector = std::vector; +#endif + +namespace detail { + + template + class ViewInterface : public vvi::shared_ptr + { + private: + using m_ImplType = impl_type; + + public: + using SmartPtrType = vvi::shared_ptr; + SPHERAL_HOST_DEVICE SmartPtrType & sptr() { return *this; } + SPHERAL_HOST_DEVICE SmartPtrType const& sptr() const { return *this; } + + SPHERAL_HOST_DEVICE m_ImplType & sptr_data() { return *(SmartPtrType::get()); } \ + SPHERAL_HOST_DEVICE m_ImplType & sptr_data() const { return *(SmartPtrType::get()); } + + SPHERAL_HOST_DEVICE ViewInterface() = default; + SPHERAL_HOST ViewInterface(SmartPtrType&& rhs) : SmartPtrType(std::forward(rhs)) {} + SPHERAL_HOST_DEVICE bool operator==(ViewInterface const& rhs) const { return this->sptr_data() == rhs.sptr_data(); } + }; + + + // Interface class for Value like objects. + template + class ValueInterface : public view_type + { + private: + using m_ImplType = typename view_type::ImplType; + using m_SmartPtrType = typename view_type::SmartPtrType; + + protected: + using ViewType = typename view_type::ViewType; + + ValueInterface() : ValueInterface(VVI_MAKE_SHARED()) {} + //ValueInterface() : ValueInterface(chai::make_shared()) {} + + public: + #if !defined(SPHERAL_ENABLE_VVI) + ValueInterface(m_ImplType* rhs) : view_type((rhs)) {} + #endif + ValueInterface(m_SmartPtrType&& s_ptr) : view_type(std::forward(s_ptr)) {} + ValueInterface(ValueInterface const& rhs) : ValueInterface(VVI_MAKE_SHARED( deepCopy(rhs.sptr_data()) )) {} + ValueInterface& operator=(ValueInterface const& rhs) { ViewType::operator=( VVI_MAKE_SHARED( deepCopy( rhs.sptr_data() ) ) ); return *this; } + bool operator==(ValueInterface const& rhs) const { return compare(this->sptr_data(), rhs.sptr_data()); } + }; + + + template + bool compare(T const& lhs, T const& rhs) + { return lhs == rhs; } + + template + bool compare(::Spheral::ManagedVector const& lhs, ::Spheral::ManagedVector const& rhs) + { return ::Spheral::compare(lhs, rhs); } + +} + +} // namespace vvi + + +// Internale macro for accessing data +#define VVI_SPTR_DATA_REF__ ViewInterfaceType::sptr_data + +// ---------------------------------------------------------------------------- +// IMPL class declaration macros +// ---------------------------------------------------------------------------- + +// Macro tool for creating macro functions with an unknown number of arguments (Max 9) +#define MKFNS(fn,...) MKFN_N(fn,##__VA_ARGS__, 9,8,7,6,5,4,3,2,1,0, void)(__VA_ARGS__) +#define MKFN_N(fn, n0, n1, n2, n3, n4, n5, n6, n7, n8, n9, n, ...) fn##n + + +// DeepCopy macro helpers + +#define VVI_IMPL_DEEPCOPY__(impl_t, ...) \ + friend impl_t deepCopy(impl_t const& rhs) { \ + impl_t result(rhs); \ + VVI_IDC_EXPAND__( __VA_ARGS__ ) \ + return result; \ + } + +#define VIDCH__(arg) result.arg = deepCopy(rhs.arg); + +#define VVI_IDC_EXPAND__(...) MKFNS(VVI_IDC_EXPAND__,##__VA_ARGS__) +#define VVI_IDC_EXPAND__0(void) +#define VVI_IDC_EXPAND__1(arg1, void) \ + VIDCH__(arg1) +#define VVI_IDC_EXPAND__2(arg1, arg2, void) \ + VIDCH__(arg1) VIDCH__(arg2) +#define VVI_IDC_EXPAND__3(arg1, arg2, arg3, void) \ + VIDCH__(arg1) VIDCH__(arg2) VIDCH__(arg3) +#define VVI_IDC_EXPAND__4(arg1, arg2, arg3, arg4, void) \ + VIDCH__(arg1) VIDCH__(arg2) VIDCH__(arg3) \ + VIDCH__(arg4) +#define VVI_IDC_EXPAND__5(arg1, arg2, arg3, arg4, arg5, void) \ + VIDCH__(arg1) VIDCH__(arg2) VIDCH__(arg3) \ + VIDCH__(arg4) VIDCH__(arg5) +#define VVI_IDC_EXPAND__6(arg1, arg2, arg3, arg4, arg5, arg6, void) \ + VIDCH__(arg1) VIDCH__(arg2) VIDCH__(arg3) \ + VIDCH__(arg4) VIDCH__(arg5) VIDCH__(arg6) +#define VVI_IDC_EXPAND__7(arg1, arg2, arg3, arg4, arg5, arg6, arg7, void) \ + VIDCH__(arg1) VIDCH__(arg2) VIDCH__(arg3) \ + VIDCH__(arg4) VIDCH__(arg5) VIDCH__(arg6) \ + VIDCH__(arg7) +#define VVI_IDC_EXPAND__8(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, void) \ + VIDCH__(arg1) VIDCH__(arg2) VIDCH__(arg3) \ + VIDCH__(arg4) VIDCH__(arg5) VIDCH__(arg6) \ + VIDCH__(arg7) VIDCH__(arg8) +#define VVI_IDC_EXPAND__9(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, void) \ + VIDCH__(arg1) VIDCH__(arg2) VIDCH__(arg3) \ + VIDCH__(arg4) VIDCH__(arg5) VIDCH__(arg6) \ + VIDCH__(arg7) VIDCH__(arg8) VIDCH__(arg9) + +// Compare macro helpers + +#define VVI_IMPL_COMPARE__(impl_t, ...) \ + friend bool compare(impl_t const& lhs, impl_t const& rhs) { \ + return VVI_ICOM_EXPAND__( __VA_ARGS__); \ + } + +#define VICOMH__(arg) vvi::detail::compare(lhs.arg, rhs.arg) + +#define VVI_ICOM_EXPAND__(...) MKFNS(VVI_ICOM_EXPAND__,##__VA_ARGS__) +#define VVI_ICOM_EXPAND__0(void) true +#define VVI_ICOM_EXPAND__1(arg1, void) \ + VICOMH__(arg1) +#define VVI_ICOM_EXPAND__2(arg1, arg2, void) \ + VICOMH__(arg1) && VICOMH__(arg2) +#define VVI_ICOM_EXPAND__3(arg1, arg2, arg3, void) \ + VICOMH__(arg1) && VICOMH__(arg2) && VICOMH__(arg3) +#define VVI_ICOM_EXPAND__4(arg1, arg2, arg3, arg4, void) \ + VICOMH__(arg1) && VICOMH__(arg2) && VICOMH__(arg3) && \ + VICOMH__(arg4) +#define VVI_ICOM_EXPAND__5(arg1, arg2, arg3, arg4, arg5, void) \ + VICOMH__(arg1) && VICOMH__(arg2) && VICOMH__(arg3) && \ + VICOMH__(arg4) && VICOMH__(arg5) +#define VVI_ICOM_EXPAND__6(arg1, arg2, arg3, arg4, arg5, arg6, void) \ + VICOMH__(arg1) && VICOMH__(arg2) && VICOMH__(arg3) &&\ + VICOMH__(arg4) && VICOMH__(arg5) && VICOMH__(arg6) +#define VVI_ICOM_EXPAND__7(arg1, arg2, arg3, arg4, arg5, arg6, arg7, void) \ + VICOMH__(arg1) && VICOMH__(arg2) && VICOMH__(arg3) && \ + VICOMH__(arg4) && VICOMH__(arg5) && VICOMH__(arg6) && \ + VICOMH__(arg7) +#define VVI_ICOM_EXPAND__8(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, void) \ + VICOMH__(arg1) && VICOMH__(arg2) && VICOMH__(arg3) && \ + VICOMH__(arg4) && VICOMH__(arg5) && VICOMH__(arg6) && \ + VICOMH__(arg7) && VICOMH__(arg8) +#define VVI_ICOM_EXPAND__9(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, void) \ + VICOMH__(arg1) && VICOMH__(arg2) && VICOMH__(arg3) && \ + VICOMH__(arg4) && VICOMH__(arg5) && VICOMH__(arg6) && \ + VICOMH__(arg7) && VICOMH__(arg8) && VICOMH__(arg9) + + +// ---------------------------------------------------------------------------- +// VALUE class declaration macros +// ---------------------------------------------------------------------------- + +#define VVI_VALUE_TOVIEW_OP__() \ + ViewType toView() { return ViewType(*this); } + + +#define VALUE_INTERFACE_METACLASS(view_t) \ +public: \ + using ViewType = UNPACK view_t; \ + using ValueType = typename ViewType::ValueType; \ + using ImplType = typename ViewType::ImplType; \ + using ViewInterfaceType = typename ViewType::ViewInterfaceType; \ +protected: \ + using Base = ::vvi::detail::ValueInterface; \ + using SmartPtrType = typename ViewType::SmartPtrType; \ +public: \ + VVI_VALUE_TOVIEW_OP__() \ +private: + + +#define VALUE_METACLASS_DECL_BEGIN(value_t, view_t) \ +UNPACK value_t : public ::vvi::detail::ValueInterface { \ + VALUE_INTERFACE_METACLASS((UNPACK view_t)) \ + + +#define VALUE_METACLASS_DECL_END() \ +} + + + +// ---------------------------------------------------------------------------- +// VIEW class declaration macros +// ---------------------------------------------------------------------------- + +// Defines a ctor that will take a "new" Data object to create the underlying +// ManagedSmartPtr. +#if !defined(SPHERAL_ENABLE_VVI) +#define VVI_VIEW_DEFINE_ALLOC_CTOR__(view_t) \ +protected: \ + view_t(ImplType* rhs) : Base(SmartPtrType(rhs, [](ImplType *p) { p->free(); } )) {} + //view_t(ImplType* rhs) : Base(SmartPtrType(rhs, [](ImplType *p) { p->free(); } )) {} +#else +#define VVI_VIEW_DEFINE_ALLOC_CTOR__(view_t) \ +public: \ + view_t(SmartPtrType&& rhs) : Base(std::forward(rhs)) {} +#endif + +#define VVI_VIEW_DEF_CTOR(view_t) \ + view_t() = default; + + +#define VIEW_INTERFACE_METACLASS(value_t, view_t, impl_t) \ +public: \ + using ImplType = UNPACK impl_t; \ + using ViewType = UNPACK view_t; \ + using ValueType = UNPACK value_t; \ + VVI_VIEW_DEF_CTOR(view_t) \ +protected: \ + using Base = ::vvi::detail::ViewInterface; \ + using ViewInterfaceType = Base; \ + using SmartPtrType = typename Base::SmartPtrType; \ + friend class UNPACK value_t; \ + VVI_VIEW_DEFINE_ALLOC_CTOR__(view_t) \ +private: + +#define VIEW_METACLASS_DECL_BEGIN(value_t, view_t, impl_t) \ +UNPACK view_t : public ::vvi::detail::ViewInterface< UNPACK impl_t> { \ + VIEW_INTERFACE_METACLASS((UNPACK value_t), (UNPACK view_t), (UNPACK impl_t)) + +#define VIEW_METACLASS_DECL_END() \ +} + + +// ---------------------------------------------------------------------------- +// Behavioral macros : These are used to allow for pointer like behavior from +// interface classes +// ---------------------------------------------------------------------------- + +#define POINTER_SYNTAX_OPERATORS() \ +public: \ + SPHERAL_HOST_DEVICE ImplType& operator*() const { return VVI_SPTR_DATA_REF__(); } \ + SPHERAL_HOST_DEVICE ImplType* operator->() const { return &VVI_SPTR_DATA_REF__(); } + + +#define REF_OPERATOR() \ +public: \ + ViewType operator&() { return toView(); } + + +#endif // __SPHERAL_VALUEVIEWINTERFACEIMPL__ + diff --git a/src/config.hh.in b/src/config.hh.in index 372a8618c..75c7c9186 100644 --- a/src/config.hh.in +++ b/src/config.hh.in @@ -1,6 +1,26 @@ #ifndef SPHERAL_config_HPP #define SPHERAL_config_HPP +#include "RAJA/RAJA.hpp" + #define SPHERAL_CXX_COMPILER_ID "@CMAKE_CXX_COMPILER_ID@" +#cmakedefine SPHERAL_ENABLE_OPENMP + +#cmakedefine SPHERAL_ENABLE_CUDA +#cmakedefine SPHERAL_ENABLE_VVI + +#if defined(SPHERAL_ENABLE_CUDA) && defined(__CUDA_ARCH__) +#define SPHERAL_GPU_ACTIVE +#endif // SPHERAL_ENABLE_CUDA && __CUDACC__ + +#if defined(SPHERAL_ENABLE_VVI) +#define VVI_ENABLED +#endif + +#define SPHERAL_HOST_DEVICE RAJA_HOST_DEVICE +#define SPHERAL_HOST RAJA_HOST +#define SPHERAL_DEVICE RAJA_DEVICE + + #endif // SPHERAL_config_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..b71a1f74c --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(cpp/Field) +add_subdirectory(cpp/Utilities) +add_subdirectory(unit) diff --git a/tests/cpp/Field/CMakeLists.txt b/tests/cpp/Field/CMakeLists.txt new file mode 100644 index 000000000..5e2604ae5 --- /dev/null +++ b/tests/cpp/Field/CMakeLists.txt @@ -0,0 +1,6 @@ +spheral_add_test( + NAME field_tests + SOURCES field_tests.cc + DEPENDS_ON Spheral_NodeList Spheral_Geometry Spheral_Hydro Spheral_DataOutput Spheral_Utilities + #DEBUG_LINKER +) diff --git a/tests/cpp/Field/field_tests.cc b/tests/cpp/Field/field_tests.cc new file mode 100644 index 000000000..a8bf68cc6 --- /dev/null +++ b/tests/cpp/Field/field_tests.cc @@ -0,0 +1,218 @@ +#include "test-utilities.hh" +#include "test-basic-exec-policies.hh" + +#include "Field/Field.hh" +#include "NodeList/NodeList.hh" + +using DIM3 = Spheral::Dim<3>; +using FieldBase = Spheral::FieldBase; +using FieldDouble = Spheral::Field; +using NodeList_t = Spheral::NodeList; + +class FieldTest : public::testing::Test{ + public: + NodeList_t test_node_list = NodeList_t("DataNodeList", 10, 0); + int someInt = 5; + + void SetUp() override { + + } + +}; + +// Setting up G Test for Fiedl +TYPED_TEST_SUITE_P(FieldTypedTest); +template +class FieldTypedTest : public FieldTest {}; + +TEST_F(FieldTest, NameCtor) +{ + { + std::string field_name = "Field::NameCtor"; + FieldDouble field(field_name); + SPHERAL_ASSERT_EQ(field.name(), field_name); + SPHERAL_ASSERT_EQ(field.size(), 0); + SPHERAL_ASSERT_EQ(this->test_node_list.numFields(), 5); + } + SPHERAL_ASSERT_EQ(this->test_node_list.numFields(), 5); +} + +TEST_F(FieldTest, NameNodeListCtor) +{ + { + std::string field_name = "Field::NodeListCtor"; + FieldDouble field(field_name, this->test_node_list); + SPHERAL_ASSERT_EQ(field.name(), field_name); + SPHERAL_ASSERT_EQ(field.size(), 10); + SPHERAL_ASSERT_EQ(this->test_node_list.numFields(), 6); + } + SPHERAL_ASSERT_EQ(this->test_node_list.numFields(), 5); +} + + + +GPU_TYPED_TEST_P(FieldTypedTest, NameNodeListValCtor) +{ + //using WORK_EXEC_POLICY = TypeParam; + + { + std::string field_name = "Field::NodeListValCtor"; + FieldDouble field(field_name, gpu_this->test_node_list, 4); + SPHERAL_ASSERT_EQ(field.name(), field_name); + SPHERAL_ASSERT_EQ(field.size(), 10); + + SPHERAL_ASSERT_EQ(gpu_this->test_node_list.numFields(), 6); + } + SPHERAL_ASSERT_EQ(gpu_this->test_node_list.numFields(), 5); +} + + +GPU_TYPED_TEST_P(FieldTypedTest, CopyCtor) +{ + //using WORK_EXEC_POLICY = TypeParam; + + { + std::string field_name = "Field::CopyCtor"; + FieldDouble field(field_name, gpu_this->test_node_list, 4); + + FieldDouble copy_field(field); + + SPHERAL_ASSERT_EQ(copy_field.name(), field_name); + SPHERAL_ASSERT_EQ(copy_field.size(), 10); + + SPHERAL_ASSERT_EQ(gpu_this->test_node_list.numFields(), 7); + SPHERAL_ASSERT_NE(&field[0], ©_field[0]); + } + SPHERAL_ASSERT_EQ(gpu_this->test_node_list.numFields(), 5); +} + + +TEST_F(FieldTest, AssignmentFieldBase) +{ + { + std::string field_name = "Field::AssignmentFieldBase"; + FieldDouble field(field_name, this->test_node_list, 4); + + FieldBase* base = &field; + + SPHERAL_ASSERT_EQ(base->name(), field_name); + SPHERAL_ASSERT_EQ(field.size(), base->size()); + SPHERAL_ASSERT_EQ(this->test_node_list.numFields(), 6); + } + SPHERAL_ASSERT_EQ(this->test_node_list.numFields(), 5); +} + +GPU_TYPED_TEST_P(FieldTypedTest, AssignmentField) +{ + //using WORK_EXEC_POLICY = TypeParam; + + { + std::string field_name = "Field::AssignmentField"; + FieldDouble field(field_name, gpu_this->test_node_list, 4); + + FieldDouble copy_field("SomeOtherField"); + copy_field = field; + + SPHERAL_ASSERT_EQ(copy_field.size(), 10); + + SPHERAL_ASSERT_NE(&field[0], ©_field[0]); + + // Is this behavior correct? Shouldn't it be 7? + SPHERAL_ASSERT_EQ(gpu_this->test_node_list.numFields(), 6); + } + SPHERAL_ASSERT_EQ(gpu_this->test_node_list.numFields(), 5); +} + +GPU_TYPED_TEST_P(FieldTypedTest, AssignmentContainerType) +{ + // Field is not inplemented with VVI at this time + // only run on host. + //using WORK_EXEC_POLICY = TypeParam; + + { + std::string field_name = "Field::AssignmentContainer"; + FieldDouble field(field_name, gpu_this->test_node_list, 4); + + using ContainerType = std::vector; + ContainerType data(10); + + RAJA::forall(TRS_UINT(0,10), + [&] SPHERAL_HOST (int i) { + data[i] = i; + }); + + field = data; + auto field_v = &field; + + RAJA::forall(TRS_UINT(0,10), + [=] SPHERAL_HOST_DEVICE (int i) { + field_v->at(i) *= 2; + }); + + RAJA::forall(TRS_UINT(0,10), + [=] SPHERAL_HOST_DEVICE (int i) { + SPHERAL_ASSERT_EQ(field_v->at(i), i*2); + }); + + SPHERAL_ASSERT_NE(&field[0], &data[0]); + + SPHERAL_ASSERT_EQ(gpu_this->test_node_list.numFields(), 6); + } + SPHERAL_ASSERT_EQ(gpu_this->test_node_list.numFields(), 5); +} + +TEST_F(FieldTest, AssignmentDataType) +{ + SPHERAL_ASSERT_EQ(this->test_node_list.numFields(), 5); + { + std::string field_name = "Field::AssignmentDataType"; + FieldDouble field(field_name, this->test_node_list, 4); + + SPHERAL_ASSERT_EQ(field.size(), 10); + + auto field_v = &field; + + RAJA::forall(TRS_UINT(0,10), + [=] SPHERAL_HOST (int i) { + SPHERAL_ASSERT_EQ(field_v->at(i), 4); + }); + + field = double(3); + + RAJA::forall(TRS_UINT(0,10), + [=] SPHERAL_HOST (int i) { + SPHERAL_ASSERT_EQ(field_v->at(i), 3); + }); + + } + SPHERAL_ASSERT_EQ(this->test_node_list.numFields(), 5); +} + +GPU_TYPED_TEST_P(FieldTypedTest, size) +{ + //using WORK_EXEC_POLICY = TypeParam; + + { + std::string field_name = "Field::size"; + FieldDouble field(field_name, gpu_this->test_node_list); + //auto field_v = field.toView(); + SPHERAL_ASSERT_EQ(field.size(), 10); + + //EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + // SPHERAL_ASSERT_EQ(field_v.size(), 10); + //EXEC_IN_SPACE_END() + SPHERAL_ASSERT_EQ(gpu_this->test_node_list.numFields(), 6); + } + SPHERAL_ASSERT_EQ(gpu_this->test_node_list.numFields(), 5); +} + +REGISTER_TYPED_TEST_SUITE_P(FieldTypedTest, + NameNodeListValCtor, + CopyCtor, + AssignmentField, + AssignmentContainerType, + size + ); + +INSTANTIATE_TYPED_TEST_SUITE_P(Field, FieldTypedTest, EXEC_TYPES,); + diff --git a/tests/cpp/Utilities/CMakeLists.txt b/tests/cpp/Utilities/CMakeLists.txt new file mode 100644 index 000000000..73e848e1a --- /dev/null +++ b/tests/cpp/Utilities/CMakeLists.txt @@ -0,0 +1,16 @@ +spheral_add_test( + NAME quadratic_interpolator_tests + SOURCES quadratic_interpolator_tests.cc + DEPENDS_ON Spheral_Utilities + DEBUG_LINKER +) + +spheral_add_test( + NAME managed_vector_tests + SOURCES managed_vector_tests.cc +) + + +add_subdirectory(ValueViewInterface) + + diff --git a/tests/cpp/Utilities/ValueViewInterface/CMakeLists.txt b/tests/cpp/Utilities/ValueViewInterface/CMakeLists.txt new file mode 100644 index 000000000..61160c52a --- /dev/null +++ b/tests/cpp/Utilities/ValueViewInterface/CMakeLists.txt @@ -0,0 +1,30 @@ +if(SPHERAL_ENABLE_VVI) + +# This test is based off a basic implementation of Utilities/QuadraticInterpolator. +spheral_add_test( + NAME value_view_ptr_basic_class_tests + SOURCES value_view_ptr_basic_class_tests.cc + #DEBUG_LINKER +) + + +# This test is based off a basic implementation of Utilities/QuadraticInterpolator. +spheral_add_test( + NAME value_view_ref_basic_class_tests + SOURCES value_view_ref_basic_class_tests.cc + #DEBUG_LINKER +) + +# This tests demonstrates CRTP as seen in Kernel. +spheral_add_test( + NAME value_view_crtp_pattern_tests + SOURCES value_view_crtp_pattern_tests.cc +) + +# This test demonstrates use with a templated abstract base class. +spheral_add_test( + NAME value_view_template_inheritance_tests + SOURCES value_view_template_inheritance_tests.cc +) + +endif() diff --git a/tests/cpp/Utilities/ValueViewInterface/value_view_crtp_pattern_tests.cc b/tests/cpp/Utilities/ValueViewInterface/value_view_crtp_pattern_tests.cc new file mode 100644 index 000000000..2a6af7d36 --- /dev/null +++ b/tests/cpp/Utilities/ValueViewInterface/value_view_crtp_pattern_tests.cc @@ -0,0 +1,204 @@ +#include "test-utilities.hh" +#include "test-basic-exec-policies.hh" + +#include "Utilities/ValueViewInterface.hh" +#include "Utilities/ManagedVector.hh" + +//-------------------------------- +// Impl Interface + +VVI_IMPL_BEGIN + +template +class Kernel : public Spheral::SPHERALCopyable { + + SPHERAL_HOST_DEVICE Desc& asDesc() { + return static_cast(const_cast&>(*this)); + } +public: + SPHERAL_HOST_DEVICE operator Desc() const { + return static_cast(const_cast&>(*this)); + } + SPHERAL_HOST_DEVICE void doSomething() { printf("Ki HD doSomething()\n"); asDesc().doSomething(); } + + VVI_IMPL_DEEPCOPY(Kernel) +}; + +template +class TableKernel : public Kernel> { +public: + SPHERAL_HOST_DEVICE void doSomething() { printf("TKi HD doSomething()\n"); printf("TableKernel doSomething\n"); } +}; + +template +class OtherKernel : public Kernel> { +public: + SPHERAL_HOST_DEVICE void doSomething() { printf("OKi HD doSomething()\n"); printf("OtherKernel doSomething\n"); } +}; + +VVI_IMPL_END + +//-------------------------------- +// View Interface + +template +class Kernel; +template +class TableKernel; +template +class OtherKernel; + +template +class PTR_VIEW_METACLASS_DEFAULT( (Kernel), (KernelView), (vvimpl::Kernel) ) + +template +class PTR_VIEW_METACLASS_DEFAULT( (TableKernel), (TableKernelView), (vvimpl::TableKernel) ) + +template +class PTR_VIEW_METACLASS_DEFAULT( (OtherKernel), (OtherKernelView), (vvimpl::OtherKernel) ) + + +//-------------------------------- +// Value Interface + +#define Kernel__(code) PTR_VALUE_METACLASS_DECL((Kernel), (KernelView), (code)) +#define TableKernel__(code) PTR_VALUE_METACLASS_DECL((TableKernel), (TableKernelView), (code)) +#define OtherKernel__(code) PTR_VALUE_METACLASS_DECL((OtherKernel), (OtherKernelView), (code)) + +template +class Kernel__( +public: + void doSomething() { printf("K H doSomething()\n"); VVI_IMPL_INST().doSomething(); } +); + +template +class TableKernel__( +public: + void doSomething() { printf("TK H doSomething()\n"); VVI_IMPL_INST().doSomething(); } +); + +template +class OtherKernel__( +public: + void doSomething() { printf("OK H doSomething()\n"); VVI_IMPL_INST().doSomething(); } +); + +class Dim1 {}; + +// +// Setting up G Test for QuadraticInterpolator +template +class KernelParallelInterface : public::testing::Test {}; + +// All QuadraticInterpolatorTets cases will run over each type in EXEC_TYPES. +TYPED_TEST_CASE(KernelParallelInterface, EXEC_TYPES); + + + +TEST(KernelParallelImpl, Impl) +{ + + vvimpl::TableKernel tk_impl; + + tk_impl.doSomething(); + + vvimpl::Kernel> k_impl; + + k_impl.doSomething(); + +} + +GPU_TYPED_TEST(KernelParallelInterface, TableKernelInterface) +{ + using WORK_EXEC_POLICY = TypeParam; + + TableKernel tk; + + tk.doSomething(); + + TableKernelView tkv = &tk; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + tkv->doSomething(); + EXEC_IN_SPACE_END() +} + +GPU_TYPED_TEST(KernelParallelInterface, TableKernelInterfaceCopy) +{ + using WORK_EXEC_POLICY = TypeParam; + + TableKernel tk; + TableKernel tk2 = tk; + + tk.doSomething(); + + TableKernelView tkv = &tk; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + tkv->doSomething(); + EXEC_IN_SPACE_END() +} + +GPU_TYPED_TEST(KernelParallelInterface, KernelInterface) +{ + using WORK_EXEC_POLICY = TypeParam; + + Kernel> k; + + k.doSomething(); + + KernelView> kv = &k; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + kv->doSomething(); + EXEC_IN_SPACE_END() + + KernelView> kv_tkv = &k; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + kv_tkv->doSomething(); + EXEC_IN_SPACE_END() + + Kernel> k_tkv; + + k_tkv->doSomething(); + + KernelView> kv_tkv2 = &k_tkv; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + kv_tkv2->doSomething(); + EXEC_IN_SPACE_END() + +} + +GPU_TYPED_TEST(KernelParallelInterface, OtherKernelInterface) +{ + using WORK_EXEC_POLICY = TypeParam; + + Kernel> k; + + k->doSomething(); + + KernelView> kv = &k; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + kv->doSomething(); + EXEC_IN_SPACE_END() + + KernelView> kv_tkv = &k; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + kv_tkv->doSomething(); + EXEC_IN_SPACE_END() + + Kernel> k_tkv; + + k_tkv->doSomething(); + + KernelView> kv_tkv2 = &k_tkv; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + kv_tkv2->doSomething(); + EXEC_IN_SPACE_END() + +} diff --git a/tests/cpp/Utilities/ValueViewInterface/value_view_ptr_basic_class_tests.cc b/tests/cpp/Utilities/ValueViewInterface/value_view_ptr_basic_class_tests.cc new file mode 100644 index 000000000..49f209ab5 --- /dev/null +++ b/tests/cpp/Utilities/ValueViewInterface/value_view_ptr_basic_class_tests.cc @@ -0,0 +1,133 @@ +#include "test-utilities.hh" +#include "test-basic-exec-policies.hh" + +//----------------------------------------------------------------------------- +// Quadratic Interpolator Example implementation +//----------------------------------------------------------------------------- + +#include "Utilities/ValueViewInterface.hh" + +VVI_IMPL_BEGIN + +class QInt : public Spheral::SPHERALCopyable{ +public: + + SPHERAL_HOST_DEVICE QInt() = default; + SPHERAL_HOST_DEVICE QInt(QInt const& rhs) = default; + SPHERAL_HOST_DEVICE QInt& operator=(QInt const& rhs) = default; + + using CoeffsType = vvi::vector; + + double mXmin, mXmax, mXstep; + CoeffsType mcoeffs; + + SPHERAL_HOST void initialize(size_t min) + { + mXmin = min; + mcoeffs.resize(10); + mcoeffs[0] = 0.1; + mcoeffs[1] = 0.2; + mcoeffs[2] = 0.3; + mcoeffs[9] = 0.19; + } + + SPHERAL_HOST void editData(size_t min) + { + mXmin = min; + mcoeffs[9] = 91; + } + + SPHERAL_HOST_DEVICE double xmin() const { return mXmin; } + SPHERAL_HOST_DEVICE double xmax() const { return mXmax; } + SPHERAL_HOST_DEVICE CoeffsType const& coeffs() const { return mcoeffs; } + + VVI_IMPL_DEEPCOPY(QInt, mcoeffs) + VVI_IMPL_COMPARE(QInt, mcoeffs, mXmin, mXmax, mXstep) +}; + +VVI_IMPL_END + + +#ifdef SPHERAL_ENABLE_VVI +class QInt; + +#define QIntView__(code) PTR_VIEW_METACLASS_DECL( (QInt), (QIntView), (vvimpl::QInt), (code) ) +#define QInt__(code) PTR_VALUE_METACLASS_DECL( (QInt), (QIntView), (code) ) + +class QIntView__( +public: + using CoeffsType = typename ImplType::CoeffsType; +); + +class QInt__( +public: + double xmin() const { return sptr_data().xmin(); } + double xmax() const { return sptr_data().xmax(); } + + void initialize(size_t min) const { return sptr_data().initialize(min); } + void editData(size_t min) const { return sptr_data().editData(min); } + CoeffsType coeffs() const { return deepCopy(sptr_data().coeffs()); } +); + +#endif //SPHERAL_ENABLE_VVI + + + + +// Setting up G Test for QuadraticInterpolator +template +class QIntExampleTypedTest : public::testing::Test {}; + +// All QuadraticInterpolatorTets cases will run over each type in EXEC_TYPES. +TYPED_TEST_CASE(QIntExampleTypedTest, EXEC_TYPES); + + +GPU_TYPED_TEST(QIntExampleTypedTest, SmartCopySemantics) +{ + using WORK_EXEC_POLICY = TypeParam; + + QInt::CoeffsType c; + { + QInt qq_int; + + auto qq_int_v = &qq_int; + auto qq_int_v2 = qq_int_v; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(qq_int_v->coeffs().size(), 0); + EXEC_IN_SPACE_END(); + + qq_int.initialize(4); + SPHERAL_ASSERT_EQ(qq_int_v->coeffs().size(), 10); + + //QInt qq_int2 = qq_int; + QInt qq_int2; + qq_int2 = qq_int; + SPHERAL_ASSERT_NE(qq_int.VVI_IMPL_INST().coeffs().begin(), qq_int2.VVI_IMPL_INST().coeffs().begin()); + SPHERAL_ASSERT_EQ(qq_int_v->coeffs().begin(), qq_int_v2->coeffs().begin()); + SPHERAL_ASSERT_TRUE(qq_int == qq_int2); + + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + printf("xmin : %lf\n", qq_int_v->xmin()); + SPHERAL_ASSERT_EQ(qq_int_v->xmin(), 4); + SPHERAL_ASSERT_EQ(qq_int_v->coeffs().size(), 10); + SPHERAL_ASSERT_EQ(qq_int_v->coeffs()[9], 0.19); + + EXEC_IN_SPACE_END(); + + qq_int.editData(2); + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(qq_int_v->xmin(), 2); + SPHERAL_ASSERT_EQ(qq_int_v->coeffs()[9], 91); + printf("xmin : %lf\n", qq_int_v->xmin()); + printf("coeffs[19] : %lf\n", qq_int_v->coeffs()[9]); + EXEC_IN_SPACE_END(); + + for (auto elem : qq_int.coeffs()) std::cout << elem << std::endl; + c = qq_int.coeffs(); + } + for (auto elem : c) std::cout << elem << std::endl; +} + diff --git a/tests/cpp/Utilities/ValueViewInterface/value_view_ref_basic_class_tests.cc b/tests/cpp/Utilities/ValueViewInterface/value_view_ref_basic_class_tests.cc new file mode 100644 index 000000000..23f54b047 --- /dev/null +++ b/tests/cpp/Utilities/ValueViewInterface/value_view_ref_basic_class_tests.cc @@ -0,0 +1,128 @@ +#include "test-utilities.hh" +#include "test-basic-exec-policies.hh" + +//----------------------------------------------------------------------------- +// Quadratic Interpolator Example implementation +//----------------------------------------------------------------------------- + +#include "Utilities/ValueViewInterface.hh" +#include "Utilities/ManagedVector.hh" + +VVI_IMPL_BEGIN + +class QInt : public Spheral::SPHERALCopyable { +public: + + SPHERAL_HOST_DEVICE QInt() = default; + SPHERAL_HOST_DEVICE QInt(QInt const& rhs) = default; + SPHERAL_HOST_DEVICE QInt& operator=(QInt const& rhs) = default; + + using CoeffsType = vvi::vector; + + double mXmin, mXmax, mXstep; + CoeffsType mcoeffs; + + SPHERAL_HOST void initialize(size_t min) + { + mXmin = min; + mcoeffs.resize(10); + mcoeffs[0] = 0.1; + mcoeffs[1] = 0.2; + mcoeffs[2] = 0.3; + mcoeffs[9] = 0.19; + } + + SPHERAL_HOST void editData(size_t min) + { + mXmin = min; + mcoeffs[9] = 91; + } + + SPHERAL_HOST_DEVICE double xmin() const { return mXmin; } + SPHERAL_HOST_DEVICE double xmax() const { return mXmax; } + SPHERAL_HOST_DEVICE CoeffsType const& coeffs() const { return mcoeffs; } + +}; + +VVI_IMPL_END + + +class QInt; + +#define QIntView__(code) REF_VIEW_METACLASS_DECL( (QInt), (QIntView), (vvimpl::QInt), (code) ) +#define QInt__(code) REF_VALUE_METACLASS_DECL( (QInt), (QIntView), (code) ) + +class QIntView__( +public: + using CoeffsType = typename ImplType::CoeffsType; +protected: + // Interal interface for accessing the underlying members of impl::QInt + VVI_MEMBER_ACCESSOR(CoeffsType, mcoeffs) + +public: + // Forward View capable methods + SPHERAL_HOST_DEVICE double xmin() const { return sptr_data().xmin(); } + SPHERAL_HOST_DEVICE double xmax() const { return sptr_data().xmax(); } + SPHERAL_HOST_DEVICE CoeffsType const& coeffs() const { return sptr_data().coeffs(); } +); + + +class QInt__( +public: + // Forward Value capable methods + SPHERAL_HOST void initialize(size_t min) const { return sptr_data().initialize(min); } + SPHERAL_HOST void editData(size_t min) const { return sptr_data().editData(min); } + SPHERAL_HOST CoeffsType coeffs() const { return deepCopy(sptr_data().coeffs()); } +); + + +// Setting up G Test for QuadraticInterpolator +template +class QIntExampleTypedTest : public::testing::Test {}; + +// All QuadraticInterpolatorTets cases will run over each type in EXEC_TYPES. +TYPED_TEST_CASE(QIntExampleTypedTest, EXEC_TYPES); + + +GPU_TYPED_TEST(QIntExampleTypedTest, SmartCopySemantics) +{ + using WORK_EXEC_POLICY = TypeParam; + + QInt::CoeffsType c; + { + QInt qq_int; + + auto qq_int_v = qq_int.toView(); + auto qq_int_v2 = qq_int_v; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(qq_int_v.xmin(), 0); + SPHERAL_ASSERT_EQ(qq_int_v.coeffs().size(), 0); + EXEC_IN_SPACE_END(); + + qq_int.initialize(4); + SPHERAL_ASSERT_EQ(qq_int_v.coeffs().size(), 10); + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + printf("xmin : %lf\n", qq_int_v.xmin()); + SPHERAL_ASSERT_EQ(qq_int_v.xmin(), 4); + SPHERAL_ASSERT_EQ(qq_int_v.coeffs().size(), 10); + SPHERAL_ASSERT_EQ(qq_int_v.coeffs()[9], 0.19); + + EXEC_IN_SPACE_END(); + + qq_int.editData(2); + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(qq_int_v.xmin(), 2); + SPHERAL_ASSERT_EQ(qq_int_v.coeffs()[9], 91); + printf("xmin : %lf\n", qq_int_v.xmin()); + printf("coeffs[19] : %lf\n", qq_int_v.coeffs()[9]); + EXEC_IN_SPACE_END(); + + for (auto elem : qq_int.coeffs()) std::cout << elem << std::endl; + c = qq_int.coeffs(); + } + for (auto elem : c) std::cout << elem << std::endl; +} + diff --git a/tests/cpp/Utilities/ValueViewInterface/value_view_template_inheritance_tests.cc b/tests/cpp/Utilities/ValueViewInterface/value_view_template_inheritance_tests.cc new file mode 100644 index 000000000..0eaffb020 --- /dev/null +++ b/tests/cpp/Utilities/ValueViewInterface/value_view_template_inheritance_tests.cc @@ -0,0 +1,232 @@ +#include "test-utilities.hh" +#include "test-basic-exec-policies.hh" + + +#include "Utilities/ValueViewInterface.hh" +#include "Utilities/ManagedVector.hh" +#include + +namespace Spheral { + + +//----------------------------------------------------------------------------- +// Implementation of Existing Class in Spheral... +//----------------------------------------------------------------------------- + +VVI_IMPL_BEGIN + +class FB : chai::CHAIPoly{ + public: + SPHERAL_HOST_DEVICE FB() {} + FB(size_t h) : hash(h) {} + + SPHERAL_HOST_DEVICE size_t getHash() const { return hash; } + + size_t hash = 0; + + virtual void resize(size_t sz) = 0; + SPHERAL_HOST_DEVICE virtual size_t size() = 0; +}; + + +template +class F : public FB { +public: + vvi::vector m_data; + + SPHERAL_HOST_DEVICE F() : FB() {} + F(size_t h, size_t sz) : FB(h), m_data(sz) {} + + SPHERAL_HOST_DEVICE T* data() { return &m_data[0]; } + SPHERAL_HOST_DEVICE T& operator()(size_t idx) { return m_data[idx]; } + + virtual void resize(size_t sz) override {std::cout << "Resize : " << sz << std::endl; m_data.resize(sz); } + SPHERAL_HOST_DEVICE virtual size_t size() override { return m_data.size(); } + + VVI_IMPL_DEEPCOPY(F, m_data) + VVI_IMPL_COMPARE(F, m_data, hash) +}; + +VVI_IMPL_END + + + +#ifdef VVI_ENABLED +//----------------------------------------------------------------------------- +// Interface to support porting to the GPU. +//----------------------------------------------------------------------------- + +// We need to forward declare value classes for view interface definitions. +template +class F; +class FB; + +// Define Metaclass macros for Value/View relationships +class PTR_VIEW_METACLASS_DEFAULT((FB), (FBV), (vvimpl::FB)) +class PTR_VALUE_METACLASS_DELETED((FB), (FBV), (vvimpl::FB)) + +#define FV__(code) PTR_VIEW_METACLASS_DECL((F), (FV), (vvimpl::F), (code)) +#define F__(code) PTR_VALUE_METACLASS_DECL((F), (FV), (code)) + + +template +class FV__( + // Field inherits from FieldBase so we would like to be able to implicitly + // upcast a fieldview object to a fieldbaseview. + VVI_UPCAST_CONVERSION_OP(FBV) +); + + +template +class F__( +public: + // Custom Ctor, note we need to create the underlying implementation + // object on ctor of value interfaces. + F(size_t h, size_t sz) : VVI_VALUE_CTOR_ARGS( (h, sz) ) {} + + // Value semantics dictate that we free underlying data upon destruction. + ~F() { VVI_IMPL_INST().m_data.free(); } + + // HOST only interface + void resize(size_t sz) { VVI_IMPL_INST().resize(sz); } + + // Moved from old View Interface pattern. + T* data() {return VVI_IMPL_INST().data(); } + T& operator()(size_t idx) { return VVI_IMPL_INST().operator()(idx); } + size_t size() const { return VVI_IMPL_INST().size(); } + + size_t getHash() const { return VVI_IMPL_INST().getHash(); } +); + +#endif // !defined(SPHERAL_ENABLE_VVI) + +}// namespace Spheral + + + +//----------------------------------------------------------------------------- +// TEST SUITE +//----------------------------------------------------------------------------- + +// Setting up G Test for QuadraticInterpolator +template +class FieldParallelInheritanceTypedTest : public::testing::Test {}; + +// All QuadraticInterpolatorTets cases will run over each type in EXEC_TYPES. +TYPED_TEST_CASE(FieldParallelInheritanceTypedTest, EXEC_TYPES); + +//TEST(FieldParallelInheritance, AccessPattern) +GPU_TYPED_TEST(FieldParallelInheritanceTypedTest, AccessPattern) +{ + { + + // -------------------------------------------------------------------------- + // Field Access + + using WORK_EXEC_POLICY = TypeParam; + + Spheral::F f(2, 200); + //auto f_2 = f; + Spheral::F f_2(2, 200); + std::cout << (f == f_2) << std::endl; + + auto f_v = &f; + Spheral::FB::ViewType fb_v = f_v; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + printf("--- GPU BEGIN ---\n"); + printf("%ld, %ld\n", fb_v->getHash(), fb_v->size()); + printf("--- GPU END ---\n"); + EXEC_IN_SPACE_END() + + std::cout << "fb_v : " << fb_v->getHash() << " , " << std::endl; + std::cout << "f_v : " << f_v->getHash() << " , " << f_v->data() << " , " << (*f_v).size() << std::endl; + std::cout << "f : " << f.getHash() << " , " << f.data() << " , " << f.size() << std::endl; + + fb_v->resize(1123); + + std::cout << "f : " << f.getHash() << " , " << f.data() << " , " << f.size() << std::endl; + std::cout << "f_v : " << f_v->getHash() << " , " << f_v->data() << " , " << f_v->size() << std::endl; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + printf("--- GPU BEGIN ---\n"); + printf("%ld, %ld\n", fb_v->getHash(), fb_v->size()); + printf("--- GPU END ---\n"); + EXEC_IN_SPACE_END() + // -------------------------------------------------------------------------- + + // -------------------------------------------------------------------------- + // Arrays of Fields + Spheral::F f0(0, 0); + Spheral::F f1(1, 1); + Spheral::F f2(2, 2); + Spheral::F f3(3, 3); + Spheral::F f4(4, 4); + + vvi::vector vec_fbv; + vec_fbv.reserve(5); + vvi::vector::ViewType> vec_fv; + vec_fv.reserve(5); + + vec_fbv.push_back( &f0 ); + vec_fbv.push_back( &f1 ); + vec_fbv.push_back( &f2 ); + vec_fbv.push_back( &f3 ); + vec_fbv.push_back( &f4 ); + + vec_fv.push_back( &f0 ); + vec_fv.push_back( &f1 ); + vec_fv.push_back( &f2 ); + vec_fv.push_back( &f3 ); + vec_fv.push_back( &f4 ); + + SPHERAL_ASSERT_EQ(vec_fbv.size(), vec_fv.size()); + for(size_t i = 0; i < vec_fbv.size(); i++) SPHERAL_ASSERT_EQ(vec_fbv[i]->size(), (*vec_fv[i]).size()); + std::cout << "Arr Map Sz : " << chai::ArrayManager::getInstance()->getPointerMap().size() << std::endl; + + for(size_t i = 0; i < vec_fbv.size(); i++) + { + auto& elem = *vec_fbv[i]; + + printf("%ld, %ld\n", elem.getHash(), elem.size()); + size_t sz = elem.size(); + elem.resize(sz*2); + } + std::cout << "Arr Map Sz : " << chai::ArrayManager::getInstance()->getPointerMap().size() << std::endl; + + for(size_t i = 0; i < f0.size(); i++) { f0(i) = f0.getHash(); } + for(size_t i = 0; i < f1.size(); i++) { f1(i) = f1.getHash(); } + for(size_t i = 0; i < f2.size(); i++) { f2(i) = f2.getHash(); } + for(size_t i = 0; i < f3.size(); i++) { f3(i) = f3.getHash(); } + for(size_t i = 0; i < f4.size(); i++) { f4(i) = f4.getHash(); } + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + printf("--- GPU BEGIN ---\n"); + printf("%ld\n", vec_fv.size()); + for(size_t i = 0; i < vec_fbv.size(); i++) + { + auto& elem_b = *vec_fbv[i]; + auto& elem_v = *vec_fv[i]; + + printf("%ld, %ld\n", elem_b.getHash(), elem_b.size()); + printf("%p\n", (void*)&elem_v(0)); + for(size_t j = 0; j < elem_v.size(); j++) { printf("%f, ",vec_fv[i]->operator()(j));} + printf("\n"); + } + printf("--- GPU END ---\n"); + EXEC_IN_SPACE_END() + + vec_fbv.free(); + vec_fv.free(); + + std::cout << f0.size() << std::endl; + std::cout << f1.size() << std::endl; + std::cout << f2.size() << std::endl; + std::cout << f3.size() << std::endl; + std::cout << f4.size() << std::endl; + // -------------------------------------------------------------------------- + + } + std::cout << "Sptr Map Sz : " << chai::SharedPtrManager::getInstance()->getPointerMap().size() << std::endl; + std::cout << "Arr Map Sz : " << chai::ArrayManager::getInstance()->getPointerMap().size() << std::endl; +} diff --git a/tests/cpp/Utilities/managed_vector_tests.cc b/tests/cpp/Utilities/managed_vector_tests.cc new file mode 100644 index 000000000..11f43a512 --- /dev/null +++ b/tests/cpp/Utilities/managed_vector_tests.cc @@ -0,0 +1,384 @@ +#include "test-utilities.hh" +#include "test-basic-exec-policies.hh" + +#include "Utilities/ManagedVector.hh" +#include "chai/managed_ptr.hpp" + +using MVDouble = Spheral::ManagedVector; + +#define assert_empty_map(IGNORED) ASSERT_EQ(chai::ArrayManager::getInstance()->getPointerMap().size(),0) + + +// Setting up G Test for ManagedVector +template +class ManagedVectorTypedTest : public::testing::Test {}; + +// All ManagedVectorTets cases will run over each type in EXEC_TYPES. +TYPED_TEST_SUITE(ManagedVectorTypedTest, EXEC_TYPES,); + + +GPU_TYPED_TEST(ManagedVectorTypedTest, DefaultConstructor) +{ + using WORK_EXEC_POLICY = TypeParam; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + MVDouble array; + + SPHERAL_ASSERT_EQ(array.size(), 0); + SPHERAL_ASSERT_EQ(array.capacity(), 0); + EXEC_IN_SPACE_END(); +} + + +TEST(ManagedVectorTest, SizeConstructor) +{ + // Size Constructor will allocate initial capacity if elements + // are below initial_capacity + MVDouble array(6); + SPHERAL_ASSERT_EQ(array.size(), 6u); + SPHERAL_ASSERT_EQ(array.capacity(), MVDouble::initial_capacity); + + // Size Constructor will allocate n elements if elements + // are above initial_capacity + MVDouble array2(MVDouble::initial_capacity + 1); + SPHERAL_ASSERT_EQ(array2.size(), MVDouble::initial_capacity + 1); + SPHERAL_ASSERT_EQ(array2.capacity(), MVDouble::initial_capacity * 2); +} + + + +TEST(ManagedVectorTest, IdentityConstructor) +{ + MVDouble array(6, 5); + SPHERAL_ASSERT_EQ(array.size(), 6u); + SPHERAL_ASSERT_EQ(array.capacity(), MVDouble::initial_capacity); + + for(auto& elem: array){ + SPHERAL_ASSERT_EQ(elem, 5); + } +} + + +GPU_TYPED_TEST(ManagedVectorTypedTest, IdentityConstructor) +{ + + using WORK_EXEC_POLICY = TypeParam; + + MVDouble array(6, 5); + MVDouble array2(6); + SPHERAL_ASSERT_EQ(array.size(), 6u); + SPHERAL_ASSERT_EQ(array.capacity(), MVDouble::initial_capacity); + + RAJA::forall(TRS_UINT(0,6), + [=] RAJA_HOST_DEVICE (unsigned idx){ + array2[idx] = array[idx]; + } + ); + RAJA::forall(TRS_UINT(0,6), + [=] RAJA_HOST (unsigned idx){ + SPHERAL_ASSERT_EQ(array2[idx], 5); + } + ); + +} + + +GPU_TYPED_TEST(ManagedVectorTypedTest, CopyConstructor) +{ + using WORK_EXEC_POLICY = TypeParam; + + MVDouble array(4); + MVDouble copy_array(array); + //MVDouble copy_array = array.slice(0, array.size()); + + array.resize(6); + + SPHERAL_ASSERT_EQ(&array[0], ©_array[0]); + SPHERAL_ASSERT_EQ(array.capacity(), copy_array.capacity()); + + RAJA::forall(TRS_UINT(0,6), + [=] RAJA_HOST_DEVICE (unsigned i){ + array[i] = i; + } + ); + + RAJA::forall(TRS_UINT(0,6), + [=] RAJA_HOST (unsigned i){ + SPHERAL_ASSERT_EQ(copy_array[i], i); + } + ); + + SPHERAL_ASSERT_EQ(&array[0], ©_array[0]); + + array.resize(20); + + SPHERAL_ASSERT_EQ(&array[0], ©_array[0]); +} + +GPU_TYPED_TEST(ManagedVectorTypedTest, AssignmentOperator) +{ + using WORK_EXEC_POLICY = TypeParam; + + MVDouble array(6, 5); + + MVDouble copy_array = array; + + RAJA::forall(TRS_UINT(0,6), + [=] RAJA_HOST_DEVICE (unsigned i){ + array[i] = i; + } + ); + + RAJA::forall(TRS_UINT(0,6), + [=] RAJA_HOST (unsigned i){ + SPHERAL_ASSERT_EQ(copy_array[i], i); + } + ); + + SPHERAL_ASSERT_EQ(&array[0], ©_array[0]); +} + +GPU_TYPED_TEST(ManagedVectorTypedTest, Equivalence) +{ + using WORK_EXEC_POLICY = TypeParam; + + MVDouble array(6, 5); + MVDouble array2(6, 3); + + MVDouble copy_array = array; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + + for (size_t i = 0; i < 6u; i++) { + array[i] = i; + array2[i] = i; + } + SPHERAL_ASSERT_TRUE (copy_array == array); + SPHERAL_ASSERT_FALSE(copy_array == array2); + EXEC_IN_SPACE_END() + + EXEC_IN_SPACE_BEGIN(LOOP_EXEC_POLICY) + ASSERT_TRUE (copy_array == array); + ASSERT_FALSE(copy_array == array2); + EXEC_IN_SPACE_END() +} + +GPU_TYPED_TEST(ManagedVectorTypedTest, PushBackDefault) +{ + using WORK_EXEC_POLICY = TypeParam; + + MVDouble array; + double val = 5; + + array.push_back(val); + + SPHERAL_ASSERT_EQ(array.size(), 1u); + SPHERAL_ASSERT_EQ(array.capacity(), MVDouble::initial_capacity); + SPHERAL_ASSERT_EQ(array[0], val); + + MVDouble array2; + + for(size_t i = 0; i < 10; i++) + array2.push_back(i); + + for(size_t i = 0; i < array2.size(); i++) + SPHERAL_ASSERT_EQ(array2[i], i); + + RAJA::forall(TRS_UINT(0,10), + [=] RAJA_HOST_DEVICE (unsigned idx){ + SPHERAL_ASSERT_EQ(array2.size(), 10u); + SPHERAL_ASSERT_EQ(array2[idx], idx); + array2[idx] *= 2; + } + ); + + RAJA::forall(TRS_UINT(0,6), + [=] RAJA_HOST (unsigned idx){ + SPHERAL_ASSERT_EQ(array2[idx], idx*2); + } + ); + + SPHERAL_ASSERT_EQ(array2.capacity(), MVDouble::initial_capacity * 2); +} + +TEST(ManagedVectorTest, PushBackMove) +{ + MVDouble array; + + array.push_back(std::move(5)); + + SPHERAL_ASSERT_EQ(array.size(), 1u); + SPHERAL_ASSERT_EQ(array.capacity(), MVDouble::initial_capacity); + SPHERAL_ASSERT_EQ(array[0], 5); + + MVDouble array2; + + for(size_t i = 0; i < 10; i++) + array2.push_back(std::move(i)); + + for(size_t i = 0; i < array2.size(); i++) + SPHERAL_ASSERT_EQ(array2[i], i); + + SPHERAL_ASSERT_EQ(array2.capacity(), MVDouble::initial_capacity * 2); +} + +GPU_TYPED_TEST(ManagedVectorTypedTest, ResizeLargerNoRealloc) +{ + using WORK_EXEC_POLICY = TypeParam; + + MVDouble array; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(array.size(), 0); + SPHERAL_ASSERT_EQ(array.capacity(), 0); + EXEC_IN_SPACE_END() + + array.move(chai::CPU); + array.resize(10); + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(array.size(), 10); + SPHERAL_ASSERT_EQ(array.capacity(), 16); + EXEC_IN_SPACE_END() + + MVDouble array2(4); + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(array2.size(), 4); + //SPHERAL_ASSERT_EQ(array2.capacity(), MVDouble::initial_capacity); + EXEC_IN_SPACE_END() + + array.move(chai::CPU); + array2.resize(6); + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(array2.size(), 6); + //SPHERAL_ASSERT_EQ(array2.capacity(), MVDouble::initial_capacity); + EXEC_IN_SPACE_END() +} + +GPU_TYPED_TEST(ManagedVectorTypedTest, ResizeLargerRealloc) +{ + using WORK_EXEC_POLICY = TypeParam; + + MVDouble array(4); + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(array.size(), 4); + //SPHERAL_ASSERT_EQ(array.capacity(), 0); + EXEC_IN_SPACE_END() + + array.move(chai::CPU); + array.resize(12); + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(array.size(), 12); + SPHERAL_ASSERT_EQ(array.capacity(), 16); + EXEC_IN_SPACE_END() +} + +GPU_TYPED_TEST(ManagedVectorTypedTest, ResizeSmaller) +{ + using WORK_EXEC_POLICY = TypeParam; + + MVDouble array(4); + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(array.size(), 4); + //SPHERAL_ASSERT_EQ(array.capacity(), 0); + EXEC_IN_SPACE_END() + + array.resize(2); + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(array.size(), 2); + //SPHERAL_ASSERT_EQ(array.capacity(), MVDouble::initial_capacity); + EXEC_IN_SPACE_END() +} + +TEST(ManagedVectorTest, Erase) +{ + MVDouble array; + std::vector check = {0,1,2,3,4,5}; + + for (size_t i = 0; i < 6; i++) { + array.push_back(check[i]); + } + SPHERAL_ASSERT_EQ(array.size(), check.size()); + + // Erase the last element + array.erase(array.end() - 1); + + std::vector check2 = {0,1,2,3,4}; + SPHERAL_ASSERT_EQ(array.size(), check2.size()); + for (size_t i = 0; i < array.size(); i++) SPHERAL_ASSERT_EQ(array[i], check2[i]); + + // Erase the 3rd element + array.erase(array.begin() + 2); + + std::vector check3 = {0,1,3,4}; + SPHERAL_ASSERT_EQ(array.size(), check3.size()); + for (size_t i = 0; i < array.size(); i++) SPHERAL_ASSERT_EQ(array[i], check3[i]); + + // Erase the first element + array.erase(array.begin()); + + std::vector check4 = {1,3,4}; + SPHERAL_ASSERT_EQ(array.size(), check4.size()); + for (size_t i = 0; i < array.size(); i++) SPHERAL_ASSERT_EQ(array[i], check4[i]); +} + +TEST(ManagedVectorTest, Insert) +{ + MVDouble array; + std::vector check = {0,1,2,3,4,5}; + + for (size_t i = 0; i < 6; i++) { + array.insert(array.begin() + i, check[i]); + } + SPHERAL_ASSERT_EQ(array.size(), check.size()); + for (size_t i = 0; i < array.size(); i++) SPHERAL_ASSERT_EQ(array[i], check[i]); + + // Insert element at end + array.insert(array.end(), 6); + + std::vector check2 = {0,1,2,3,4,5,6}; + SPHERAL_ASSERT_EQ(array.size(), check2.size()); + for (size_t i = 0; i < array.size(); i++) SPHERAL_ASSERT_EQ(array[i], check2[i]); + + // Erase the 3rd element + array.insert(array.begin() + 2, 7); + + std::vector check3 = {0,1,7,2,3,4,5,6}; + SPHERAL_ASSERT_EQ(array.size(), check3.size()); + for (size_t i = 0; i < array.size(); i++) SPHERAL_ASSERT_EQ(array[i], check3[i]); + + // Erase the first element + array.insert(array.begin(), -1); + + std::vector check4 = {-1,0,1,7,2,3,4,5,6}; + SPHERAL_ASSERT_EQ(array.size(), check4.size()); + for (size_t i = 0; i < array.size(); i++) SPHERAL_ASSERT_EQ(array[i], check4[i]); +} + +TEST(ManagedVectorTest, DeepCopy) +{ + double init_val = 5; + MVDouble array(6, init_val); + + MVDouble copy_array(array); + MVDouble deep_copy_array(deepCopy(array)); + + for (size_t i = 0; i < 6u; i++) { + array[i] = 4; + } + + for (size_t i = 0; i < 6u; i++) { + ASSERT_NE(array[i], deep_copy_array[i]); + SPHERAL_ASSERT_EQ(deep_copy_array[i], init_val); + } + + ASSERT_FALSE(array == deep_copy_array); + ASSERT_NE(&array[0], &deep_copy_array[0]); +} + diff --git a/tests/cpp/Utilities/quadratic_interpolator_tests.cc b/tests/cpp/Utilities/quadratic_interpolator_tests.cc new file mode 100644 index 000000000..182d371e9 --- /dev/null +++ b/tests/cpp/Utilities/quadratic_interpolator_tests.cc @@ -0,0 +1,109 @@ +#include "test-utilities.hh" +#include "test-basic-exec-policies.hh" + +#include "Utilities/QuadraticInterpolator.hh" + +TEST(QuadraticInterpolatorTest, DefaultConstructor) +{ + Spheral::QuadraticInterpolator q_int; + SPHERAL_ASSERT_EQ(q_int.coeffs().size(), 0); +} + +TEST(QuadraticInterpolatorTest, Initialize) +{ + Spheral::QuadraticInterpolator q_int; + q_int.initialize(0,4,{0,1,2}); + + SPHERAL_ASSERT_EQ(q_int.xmin(), 0); + SPHERAL_ASSERT_EQ(q_int.xmax(), 4); + SPHERAL_ASSERT_EQ(q_int.coeffs().size(), 3); + SPHERAL_ASSERT_EQ(q_int.coeffs()[0], 0); + SPHERAL_ASSERT_EQ(q_int.coeffs()[1], 0.5); + SPHERAL_ASSERT_EQ(q_int.coeffs()[2], 0); +} + +TEST(QuadraticInterpolatorTest, CopySemantics) +{ + Spheral::QuadraticInterpolator q_int; + q_int.initialize(0,4,{0,1,2}); + + Spheral::QuadraticInterpolator q_int2 = q_int; + SPHERAL_ASSERT_EQ(q_int.xmin(), 0); + SPHERAL_ASSERT_EQ(q_int.xmax(), 4); + SPHERAL_ASSERT_EQ(q_int.coeffs().size(), 3); + SPHERAL_ASSERT_EQ(q_int.coeffs()[0], 0); + SPHERAL_ASSERT_EQ(q_int.coeffs()[1], 0.5); + SPHERAL_ASSERT_EQ(q_int.coeffs()[2], 0); + + SPHERAL_ASSERT_EQ(q_int.xmin(), q_int2.xmin()); + SPHERAL_ASSERT_EQ(q_int.xmax(), q_int2.xmax()); + SPHERAL_ASSERT_EQ(q_int.coeffs().size(), q_int2.coeffs().size()); + SPHERAL_ASSERT_EQ(q_int.coeffs()[0], q_int2.coeffs()[0]); + SPHERAL_ASSERT_EQ(q_int.coeffs()[1], q_int2.coeffs()[1]); + SPHERAL_ASSERT_EQ(q_int.coeffs()[2], q_int2.coeffs()[2]); + + SPHERAL_ASSERT_NE(&(q_int.coeffs()[0]), &(q_int2.coeffs()[0])); +} + +TEST(QuadraticInterpolatorTest, AssignmentSemantics) +{ + Spheral::QuadraticInterpolator q_int; + q_int.initialize(0,4,{0,1,2}); + + Spheral::QuadraticInterpolator q_int2;// = q_int; + q_int2 = q_int; + SPHERAL_ASSERT_EQ(q_int.xmin(), 0); + SPHERAL_ASSERT_EQ(q_int.xmax(), 4); + SPHERAL_ASSERT_EQ(q_int.coeffs().size(), 3); + SPHERAL_ASSERT_EQ(q_int.coeffs()[0], 0); + SPHERAL_ASSERT_EQ(q_int.coeffs()[1], 0.5); + SPHERAL_ASSERT_EQ(q_int.coeffs()[2], 0); + + SPHERAL_ASSERT_EQ(q_int.xmin(), q_int2.xmin()); + SPHERAL_ASSERT_EQ(q_int.xmax(), q_int2.xmax()); + SPHERAL_ASSERT_EQ(q_int.coeffs().size(), q_int2.coeffs().size()); + SPHERAL_ASSERT_EQ(q_int.coeffs()[0], q_int2.coeffs()[0]); + SPHERAL_ASSERT_EQ(q_int.coeffs()[1], q_int2.coeffs()[1]); + SPHERAL_ASSERT_EQ(q_int.coeffs()[2], q_int2.coeffs()[2]); + + SPHERAL_ASSERT_NE(&(q_int.coeffs()[0]), &(q_int2.coeffs()[0])); +} + +TEST(QuadraticInterpolatorTest, Equivalence) +{ + Spheral::QuadraticInterpolator q_int; + Spheral::QuadraticInterpolator q_int2; + Spheral::QuadraticInterpolator q_int3; + q_int.initialize(0,4,{0,1,2}); + q_int2.initialize(0,4,{0,1,2}); + q_int3.initialize(0,4,{0,1,3}); + + SPHERAL_ASSERT_TRUE(q_int == q_int2); + SPHERAL_ASSERT_FALSE(q_int == q_int3); +} + +TEST(QuadraticInterpolatorTest, OperatorParen) +{ + Spheral::QuadraticInterpolator q_int; + q_int.initialize(0,4,{0,1,2}); + + SPHERAL_ASSERT_EQ(q_int(1), 0.5); + SPHERAL_ASSERT_EQ(q_int(2), 1); + SPHERAL_ASSERT_EQ(q_int(3), 1.5); + SPHERAL_ASSERT_EQ(q_int(4), 2); + + SPHERAL_ASSERT_EQ(q_int(1, 0), 0.5); + SPHERAL_ASSERT_EQ(q_int(2, 0), 1); + SPHERAL_ASSERT_EQ(q_int(3, 0), 1.5); + SPHERAL_ASSERT_EQ(q_int(4, 0), 2); +} + +#ifdef SPHERAL_ENABLE_VVI +// Setting up G Test for QuadraticInterpolator +template +class QuadraticInterpolatorTypedTest : public::testing::Test {}; + +// All QuadraticInterpolatorTets cases will run over each type in EXEC_TYPES. +TYPED_TEST_CASE(QuadraticInterpolatorTypedTest, EXEC_TYPES); +#include "quadratic_interpolator_view_tests.hh" +#endif diff --git a/tests/cpp/Utilities/quadratic_interpolator_view_tests.hh b/tests/cpp/Utilities/quadratic_interpolator_view_tests.hh new file mode 100644 index 000000000..c3c0a2488 --- /dev/null +++ b/tests/cpp/Utilities/quadratic_interpolator_view_tests.hh @@ -0,0 +1,184 @@ +#ifndef __SPHERAL_QUADRATIC_INTERPOLATOR_VIEW_TESTS_HH__ +#define __SPHERAL_QUADRATIC_INTERPOLATOR_VIEW_TESTS_HH__ + +GPU_TYPED_TEST(QuadraticInterpolatorTypedTest, DefaultConstructor) +{ + using WORK_EXEC_POLICY = TypeParam; + Spheral::QuadraticInterpolator q_int; + auto q_int_v = &q_int; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(q_int_v->coeffs().size(), 0); + EXEC_IN_SPACE_END(); +} + +GPU_TYPED_TEST(QuadraticInterpolatorTypedTest, Initialize) +{ + using WORK_EXEC_POLICY = TypeParam; + + Spheral::QuadraticInterpolator q_int; + q_int.initialize(0,4,{0,1,2}); + + auto q_int_v = &q_int; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(q_int_v->xmin(), 0); + SPHERAL_ASSERT_EQ(q_int_v->xmax(), 4); + SPHERAL_ASSERT_EQ(q_int_v->coeffs().size(), 3); + SPHERAL_ASSERT_EQ(q_int_v->coeffs()[0], 0); + SPHERAL_ASSERT_EQ(q_int_v->coeffs()[1], 0.5); + SPHERAL_ASSERT_EQ(q_int_v->coeffs()[2], 0); + EXEC_IN_SPACE_END(); +} + +GPU_TYPED_TEST(QuadraticInterpolatorTypedTest, CopySemantics) +{ + using WORK_EXEC_POLICY = TypeParam; + + Spheral::QuadraticInterpolator::CoeffsType c_copy; + { + Spheral::QuadraticInterpolator q_int; + auto q_int_v = &q_int; + q_int.initialize(0,4,{0,1,2}); + + Spheral::QuadraticInterpolator q_int2 = q_int; + + auto q_int2_v = &q_int2; + auto q_int_v2 = q_int_v; + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(q_int_v->xmin(), 0); + SPHERAL_ASSERT_EQ(q_int_v->xmax(), 4); + SPHERAL_ASSERT_EQ(q_int_v->coeffs().size(), 3); + SPHERAL_ASSERT_EQ(q_int_v->coeffs()[0], 0); + SPHERAL_ASSERT_EQ(q_int_v->coeffs()[1], 0.5); + SPHERAL_ASSERT_EQ(q_int_v->coeffs()[2], 0); + + SPHERAL_ASSERT_EQ(q_int_v->xmin(), q_int2_v->xmin()); + SPHERAL_ASSERT_EQ(q_int_v->xmax(), q_int2_v->xmax()); + SPHERAL_ASSERT_EQ(q_int_v->coeffs().size(), q_int2_v->coeffs().size()); + SPHERAL_ASSERT_EQ(q_int_v->coeffs()[0], q_int2_v->coeffs()[0]); + SPHERAL_ASSERT_EQ(q_int_v->coeffs()[1], q_int2_v->coeffs()[1]); + SPHERAL_ASSERT_EQ(q_int_v->coeffs()[2], q_int2_v->coeffs()[2]); + + SPHERAL_ASSERT_NE(&(q_int_v->coeffs()[0]), &(q_int2_v->coeffs()[0])); + SPHERAL_ASSERT_EQ(&(q_int_v->coeffs()[0]), &(q_int_v2->coeffs()[0])); + EXEC_IN_SPACE_END(); + + c_copy = deepCopy(q_int.coeffs()); + } + SPHERAL_ASSERT_EQ(c_copy[0], 0); + SPHERAL_ASSERT_EQ(c_copy[1], 0.5); + SPHERAL_ASSERT_EQ(c_copy[2], 0); + + c_copy.free(); + +} + +GPU_TYPED_TEST(QuadraticInterpolatorTypedTest, Equivalence) +{ + using WORK_EXEC_POLICY = TypeParam; + + Spheral::QuadraticInterpolator q_int; + Spheral::QuadraticInterpolator q_int2; + Spheral::QuadraticInterpolator q_int3; + + auto q_int_v = &q_int; + auto q_int2_v = &q_int2; + auto q_int3_v = &q_int3; + + q_int.initialize(0,4,{0,1,2}); + q_int2.initialize(0,4,{0,1,2}); + q_int3.initialize(0,4,{0,1,3}); + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_FALSE(q_int_v == q_int2_v); + SPHERAL_ASSERT_FALSE(q_int_v == q_int3_v); + EXEC_IN_SPACE_END(); +} + +GPU_TYPED_TEST(QuadraticInterpolatorTypedTest, OperatorParen) +{ + using WORK_EXEC_POLICY = TypeParam; + + Spheral::QuadraticInterpolator q_int; + auto q_int_v = &q_int; + + q_int.initialize(0,4,{0,1,2}); + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + auto& q = *q_int_v; + SPHERAL_ASSERT_EQ(q(1), 0.5); + SPHERAL_ASSERT_EQ(q(2), 1); + SPHERAL_ASSERT_EQ(q(3), 1.5); + SPHERAL_ASSERT_EQ(q(4), 2); + + SPHERAL_ASSERT_EQ(q(1, 0), 0.5); + SPHERAL_ASSERT_EQ(q(2, 0), 1); + SPHERAL_ASSERT_EQ(q(3, 0), 1.5); + SPHERAL_ASSERT_EQ(q(4, 0), 2); + EXEC_IN_SPACE_END(); +} + +GPU_TYPED_TEST(QuadraticInterpolatorTypedTest, Prime) +{ + using WORK_EXEC_POLICY = TypeParam; + + Spheral::QuadraticInterpolator q_int; + auto q_int_v = &q_int; + + q_int.initialize(0,4,{0,1,2}); + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(q_int_v->prime(1), 0.5); + SPHERAL_ASSERT_EQ(q_int_v->prime(2), 0.5); + SPHERAL_ASSERT_EQ(q_int_v->prime(3), 0.5); + SPHERAL_ASSERT_EQ(q_int_v->prime(4), 0.5); + + SPHERAL_ASSERT_EQ(q_int_v->prime(1, 0), 0.5); + SPHERAL_ASSERT_EQ(q_int_v->prime(2, 0), 0.5); + SPHERAL_ASSERT_EQ(q_int_v->prime(3, 0), 0.5); + SPHERAL_ASSERT_EQ(q_int_v->prime(4, 0), 0.5); + EXEC_IN_SPACE_END(); +} + +GPU_TYPED_TEST(QuadraticInterpolatorTypedTest, Prime2) +{ + using WORK_EXEC_POLICY = TypeParam; + + Spheral::QuadraticInterpolator q_int; + auto q_int_v = &q_int; + + q_int.initialize(0,4,{0,1,2}); + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(q_int_v->prime2(1), 0); + SPHERAL_ASSERT_EQ(q_int_v->prime2(2), 0); + SPHERAL_ASSERT_EQ(q_int_v->prime2(3), 0); + SPHERAL_ASSERT_EQ(q_int_v->prime2(4), 0); + + SPHERAL_ASSERT_EQ(q_int_v->prime2(1, 0), 0); + SPHERAL_ASSERT_EQ(q_int_v->prime2(2, 0), 0); + SPHERAL_ASSERT_EQ(q_int_v->prime2(3, 0), 0); + SPHERAL_ASSERT_EQ(q_int_v->prime2(4, 0), 0); + EXEC_IN_SPACE_END(); +} + +GPU_TYPED_TEST(QuadraticInterpolatorTypedTest, LowerBound) +{ + using WORK_EXEC_POLICY = TypeParam; + + Spheral::QuadraticInterpolator q_int; + auto q_int_v = &q_int; + + q_int.initialize(0,4,{0,1,2}); + + EXEC_IN_SPACE_BEGIN(WORK_EXEC_POLICY) + SPHERAL_ASSERT_EQ(q_int_v->lowerBound(1), 0); + SPHERAL_ASSERT_EQ(q_int_v->lowerBound(2), 0); + SPHERAL_ASSERT_EQ(q_int_v->lowerBound(3), 0); + SPHERAL_ASSERT_EQ(q_int_v->lowerBound(4), 0); + EXEC_IN_SPACE_END(); +} + +#endif // __SPHERAL_QUADRATIC_INTERPOLATOR_VIEW_TESTS_HH__ diff --git a/tests/cpp/include/test-basic-exec-policies.hh b/tests/cpp/include/test-basic-exec-policies.hh new file mode 100644 index 000000000..887b9f6ed --- /dev/null +++ b/tests/cpp/include/test-basic-exec-policies.hh @@ -0,0 +1,16 @@ +#ifndef SPHERAL_BASIC_EXEC_POL_HH +#define SPHERAL_BASIC_EXEC_POL_HH + +#include "RAJA/RAJA.hpp" + +using SEQ_EXEC_POLICY = RAJA::seq_exec; +// The list of execution types we want to possibly run these tests over. +using EXEC_TYPES = ::testing::Types< + SEQ_EXEC_POLICY +#ifdef SPHERAL_ENABLE_CUDA + ,RAJA::cuda_exec<512> +#endif +>; + + +#endif // SPHERAL_BASIC_EXEC_POL_HH diff --git a/tests/cpp/include/test-utilities.hh b/tests/cpp/include/test-utilities.hh new file mode 100644 index 000000000..bce661437 --- /dev/null +++ b/tests/cpp/include/test-utilities.hh @@ -0,0 +1,89 @@ +#ifndef SPHERAL_TEST_UTIILITIES_HH +#define SPHERAL_TEST_UTIILITIES_HH + + +#include "gtest/gtest.h" +#include "assert.h" +#include "RAJA/RAJA.hpp" +#include "config.hh" + +using TRS_UINT = RAJA::TypedRangeSegment; +using LOOP_EXEC_POLICY = RAJA::seq_exec; + +#define EXEC_IN_SPACE_BEGIN(POL) \ + RAJA::forall(TRS_UINT(0,1), [=] SPHERAL_HOST_DEVICE (int) { + +#define EXEC_IN_SPACE_END() \ + }); + + +template +inline +SPHERAL_HOST_DEVICE +void SPHERAL_ASSERT_EQ(T const& LHS, U const& RHS) { +#if !defined(SPHERAL_GPU_ACTIVE) + ASSERT_EQ(LHS, RHS); +#else + if (LHS != RHS) { + printf("ERROR @ cuda_assert\n"); + assert(0); + } +#endif +} + +template +SPHERAL_HOST_DEVICE +void SPHERAL_ASSERT_NE(T const& LHS, U const& RHS) { +#if !defined(SPHERAL_GPU_ACTIVE) + ASSERT_NE(LHS, RHS); +#else + if (LHS == RHS) { + printf("ERROR @ cuda_assert\n"); + assert(0); + } +#endif +} + +SPHERAL_HOST_DEVICE +void SPHERAL_ASSERT_TRUE(bool result) { +#if !defined(SPHERAL_GPU_ACTIVE) + ASSERT_TRUE(result); +#else + if (!result) { + printf("ERROR @ cuda_assert\n"); + assert(0); + } +#endif +} + +SPHERAL_HOST_DEVICE +void SPHERAL_ASSERT_FALSE(bool result) { +#if !defined(SPHERAL_GPU_ACTIVE) + ASSERT_FALSE(result); +#else + if (result) { + printf("ERROR @ cuda_assert\n"); + assert(0); + } +#endif +} + + +// Macro used throughout LLNLProjects to get around calling +// HOST_DEVICE lambdas from within the "Testing" class directly +#define GPU_TYPED_TEST(X, Y) \ + template \ + static void gpu_test_##X##Y(); \ + TYPED_TEST(X, Y) { gpu_test_##X##Y(); } \ + template \ + static void gpu_test_##X##Y() + +#define GPU_TYPED_TEST_P(X, Y) \ + template \ + static void gpu_test_##X##Y(TestFixture* gpu_this); \ + TYPED_TEST_P(X, Y) { gpu_test_##X##Y(this); } \ + template \ + static void gpu_test_##X##Y(TestFixture* gpu_this) + + +#endif // SPHERAL_TEST_UTIILITIES_HH