Skip to content

Commit

Permalink
Merge pull request #60 from DARMA-tasking/16-create-lbaf-viz-interface
Browse files Browse the repository at this point in the history
#16: Create lbaf-viz interface
  • Loading branch information
pierrepebay authored May 8, 2024
2 parents 4ea6a17 + 0a66707 commit 97933f4
Show file tree
Hide file tree
Showing 49 changed files with 1,379 additions and 378 deletions.
47 changes: 47 additions & 0 deletions .github/workflows/build-and-test-bindings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Build and Test Bindings Ubuntu 22.04 gcc 11 x64

# Trigger the workflow on push or pull request
on:
# push:
# branches:
# - master
# pull_request:
# types: [opened, reopened, synchronize, converted_to_draft, ready_for_review]

jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
env:
OUTPUT_DIR: '/tmp/out'
name: vt-tv bindings build and test
steps:
- uses: actions/checkout@v3

- name: CI Variables
id: vars
run: echo "DOCKER_TAG=$(echo ${{ github.ref }} | cut -d'/' -f3- | sed 's/[^a-z0-9_-]/__/gi')" >> $GITHUB_ENV

- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2

- name: Inspect Builder
run: |
echo "Name: ${{ steps.buildx.outputs.name }}"
echo "Endpoint: ${{ steps.buildx.outputs.endpoint }}"
echo "Status: ${{ steps.buildx.outputs.status }}"
echo "Flags: ${{ steps.buildx.outputs.flags }}"
echo "Platforms: ${{ steps.buildx.outputs.platforms }}"
echo "DOCKER_TAG: ${{ env.DOCKER_TAG }}"
- name: Build the Docker Image; build and test vt-tv bindings
id: docker_build
uses: docker/build-push-action@v3
with:
push: false
tags: ${{ env.DOCKER_TAG }}
context: .
file: ./ci/build-and-test-bindings.dockerfile
outputs: type=local,dest=${{ env.OUTPUT_DIR }}
57 changes: 15 additions & 42 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,41 +19,45 @@ set(VT_TV_LIBRARY_NS vt::lib::vt-tv)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# add -fPIC to all targets
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17 CACHE STRING "The C++ standard to use")
endif()
message(STATUS "CMAKE_CXX_STANDARD: ${CMAKE_CXX_STANDARD}")

option(vt_tv_python_bindings_enabled "Build vt-tv with Python bindings" ON)
option(vt_tv_openmp_enabled "Build vt-tv with openMP support" ON)
option(VT_TV_PYTHON_BINDINGS_ENABLED "Build vt-tv with Python bindings" ON)
option(VT_TV_OPENMP_ENABLED "Build vt-tv with openMP support" ON)
set(VT_TV_N_THREADS "2" CACHE STRING "Number of OpenMP threads to use")

include(cmake/load_packages.cmake)

if(APPLE)
add_compile_options(-ffat-lto-objects)
endif()

if(openmp_enabled)
set(VT_TV_NUM_THREADS "2" CACHE STRING "Number of threads to use")
add_definitions(-DVT_TV_NUM_THREADS=${VT_TV_NUM_THREADS})
endif()
add_definitions(-DVT_TV_N_THREADS=${VT_TV_N_THREADS})
add_definitions(-DVT_TV_OPENMP_ENABLED=${VT_TV_OPENMP_ENABLED})

add_custom_target(vt_tv_examples)
add_custom_target(vt_tv_tests)
add_custom_target(vt_tv_apps)

set(PROJECT_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
set(PROJECT_BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(PROJECT_LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib)
set(PROJECT_EXAMPLE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/examples)
set(PROJECT_APP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/apps)
set(PROJECT_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
set(PROJECT_BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(PROJECT_LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib)
set(PROJECT_EXAMPLE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/examples)
set(PROJECT_APP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/apps)
set(PROJECT_BINDINGS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/bindings)

include(CTest)

add_subdirectory(src)
add_subdirectory(examples)
add_subdirectory(tests)
add_subdirectory(apps)
add_subdirectory(bindings)

configure_file(
cmake/vtTVConfig.cmake.in
Expand All @@ -70,34 +74,3 @@ install(
DESTINATION cmake
COMPONENT lib
)

if (vt_tv_python_bindings_enabled)
# Build the core parts of nanobind once
nanobind_build_library(nanobind SHARED)

# Compile an extension library
add_library(my_ext MODULE ${CMAKE_CURRENT_SOURCE_DIR}/circle.cc)

# .. and link it against the nanobind parts
message(STATUS "vtk libraries: ${VTK_LIBRARIES}")
target_link_libraries(my_ext PUBLIC ${VTK_LIBRARIES})
target_link_libraries(my_ext PRIVATE nanobind)

# .. enable size optimizations
nanobind_opt_size(my_ext)

# .. enable link time optimization
nanobind_lto(my_ext)

# .. disable the stack protector
nanobind_disable_stack_protector(my_ext)

# .. set the Python extension suffix
nanobind_extension(my_ext)

# .. set important compilation flags
nanobind_compile_options(my_ext)

# .. set important linker flags
nanobind_link_options(my_ext)
endif()
41 changes: 40 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,43 @@ using Paraview. Additionally, the task visualizer can produce PNGs
directly using a VTK workflow to render a visualization of ranks and
tasks over phases.

![Example Output PNG](./docs/example-output-image.png)
![Example Output PNG](./docs/example-output-image.png)

## Building the Python bindings

### Requirements

In order to build the python bindings, make sure you have a Python <ins>`3.8` or `3.9`</ins> environment, with the `nanobind` package installed. You can install `nanobind` with `pip`:

```bash
pip install nanobind
```

You must have a C++ compiler that supports C++17, and `cmake` >= 3.17.

Finally, you must have a (<ins>C++</ins>) [VTK](https://vtk.org/) build available on your system. We recommend building from source, and the currently tested version is `9.3.0`. You can find instructions for building VTK [here](https://gitlab.kitware.com/vtk/vtk/-/blob/master/Documentation/docs/build_instructions/build.md).

### Building

To build the python bindings, you must specify in the `VTTV_VTK_DIR` environment variable the path to the VTK build directory:

```bash
export VTTV_VTK_DIR=/path/to/vtk/build
```


Then, to install python-environment-wide the binded `vt-tv` python module, run:

```bash
pip install <path/to/vt-tv/source>
```
**Optional**

To specify the number of parallel jobs to use during the build, you can set the `VTTV_J` environment variable:

```bash
export VTTV_J=8
```

> [!NOTE]
> Behind the scenes, the usual `cmake` and `make` commands are run. Depending on your system, this can cause the install process to be lengthy as it will be compiling the entire `vt-tv` library.
6 changes: 6 additions & 0 deletions apps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,10 @@ foreach(APP_FULL ${PROJECT_APPS})
PUBLIC
${VT_TV_LIBRARY_NS}
)

vtk_module_autoinit(
TARGETS ${APP}
MODULES ${VTK_LIBRARIES}
)

endforeach()
3 changes: 3 additions & 0 deletions bindings/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
if(VT_TV_PYTHON_BINDINGS_ENABLED)
add_subdirectory(python)
endif()
21 changes: 21 additions & 0 deletions bindings/python/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

# Source files for the Python bindings
file(
GLOB
PYTHON_BINDING_SRCS
"${CMAKE_CURRENT_SOURCE_DIR}/*.cc"
)

# Build the core parts of nanobind once
nanobind_build_library(nanobind SHARED)

# Create the Python bindings for the module
nanobind_add_module(vttv ${PYTHON_BINDING_SRCS} tv.cc)

# .. Link it to necessary libraries
target_link_libraries(vttv PUBLIC ${VT_TV_LIBRARY_NS} ${JSON_LIBRARY} ${FMT_LIBRARY})

vtk_module_autoinit(
TARGETS vttv
MODULES ${VTK_LIBRARIES}
)
140 changes: 140 additions & 0 deletions bindings/python/tv.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#include "tv.h"

namespace vt::tv::bindings::python {

void tvFromJson(const std::vector<std::string>& input_json_per_rank_list, const std::string& input_yaml_params_str, uint64_t num_ranks) {
std::string startup_logo = std::string(" __ __\n")
+ std::string(" _ __/ /_ / /__ __\n")
+ std::string("| | / / __/ _____ / __/ | / /\n")
+ std::string("| |/ / / /____/ / /_ | |/ /\n")
+ std::string("|___/\\__/ \\__/ |___/\n");
fmt::print("==============================\n");
fmt::print(startup_logo);
fmt::print("==============================\n");

// parse the input yaml parameters
try {
// Load the configuration from serialized YAML
YAML::Node viz_config = YAML::Load(input_yaml_params_str);


std::array<std::string, 3> qoi_request = {
viz_config["rank_qoi"].as<std::string>(),
"",
viz_config["object_qoi"].as<std::string>()
};

bool save_meshes = viz_config["save_meshes"].as<bool>();
bool save_pngs = true; // lbaf always saves pngs
bool continuous_object_qoi = viz_config["force_continuous_object_qoi"].as<bool>();

std::array<uint64_t, 3> grid_size = {
viz_config["x_ranks"].as<uint64_t>(),
viz_config["y_ranks"].as<uint64_t>(),
viz_config["z_ranks"].as<uint64_t>()
};

double object_jitter = viz_config["object_jitter"].as<double>();

std::string output_dir = viz_config["output_visualization_dir"].as<std::string>();
std::filesystem::path output_path(output_dir);

// Throw an error if the output directory does not exist or is not absolute
if (!std::filesystem::exists(output_path)) {
throw std::runtime_error("Visualization output directory does not exist.");
}
if (!output_path.is_absolute()) {
throw std::runtime_error("Visualization output directory must be absolute.");
}

// append / to avoid problems with file stems
if (!output_dir.empty() && output_dir.back() != '/') {
output_dir += '/';
}

std::string output_file_stem = viz_config["output_visualization_file_stem"].as<std::string>();

uint64_t win_size = 2000;
if (viz_config["window_size"]) {
win_size = viz_config["window_size"].as<uint64_t>();
}

// Use automatic font size if not defined by user
// 0.025 is the factor of the window size determined to be ideal for the font size
uint64_t font_size = 0.025 * win_size;
if (viz_config["font_size"]) {
font_size = viz_config["font_size"].as<uint64_t>();
}

// print all saved configuration parameters
fmt::print("Input Configuration Parameters:\n");
fmt::print(" x_ranks: {}\n", grid_size[0]);
fmt::print(" y_ranks: {}\n", grid_size[1]);
fmt::print(" z_ranks: {}\n", grid_size[2]);
fmt::print(" object_jitter: {}\n", object_jitter);
fmt::print(" rank_qoi: {}\n", qoi_request[0]);
fmt::print(" object_qoi: {}\n", qoi_request[2]);
fmt::print(" save_meshes: {}\n", save_meshes);
fmt::print(" save_pngs: {}\n", save_pngs);
fmt::print(" force_continuous_object_qoi: {}\n", continuous_object_qoi);
fmt::print(" output_visualization_dir: {}\n", output_dir);
fmt::print(" output_visualization_file_stem: {}\n", output_file_stem);
fmt::print(" window_size: {}\n", win_size);
fmt::print(" font_size: {}\n", font_size);

using json = nlohmann::json;

assert(input_json_per_rank_list.size() == num_ranks && "Must have the same number of json files as ranks");

// Initialize the info object, that will hold data for all ranks for all phases
std::unique_ptr<Info> info = std::make_unique<Info>();

#ifdef VT_TV_N_THREADS
const int threads = VT_TV_N_THREADS;
#else
const int threads = 2;
#endif
#ifdef VT_TV_OPENMP_ENABLED
#if VT_TV_OPENMP_ENABLED
omp_set_num_threads(threads);
// print number of threads
fmt::print("vt-tv: Using {} threads\n", threads);
# pragma omp parallel for
#endif
#endif
for (int64_t rank_id = 0; rank_id < num_ranks; rank_id++) {
fmt::print("Reading file for rank {}\n", rank_id);
std::string rank_json_str = input_json_per_rank_list[rank_id];
utility::JSONReader reader{static_cast<NodeType>(rank_id)};
reader.readString(rank_json_str);
auto tmpInfo = reader.parse();
#ifdef VT_TV_OPENMP_ENABLED
#if VT_TV_OPENMP_ENABLED
#pragma omp critical
#endif
#endif
{
info->addInfo(tmpInfo->getObjectInfo(), tmpInfo->getRank(rank_id));
}
}
// Instantiate render
Render render(
qoi_request, continuous_object_qoi, *info, grid_size, object_jitter,
output_dir, output_file_stem, 1.0, save_meshes, save_pngs, std::numeric_limits<PhaseType>::max()
);
render.generate(font_size, win_size);
} catch (std::exception const& e) {
std::cout << "vt-tv: Error reading the configuration file: " << e.what() << std::endl;
}

fmt::print("vt-tv: Done.\n");
}

namespace nb = nanobind;
using namespace nb::literals;

NB_MODULE(vttv, m) {
m.def("tvFromJson", &tvFromJson);
}

} /* end namespace vt::tv::bindings::python */
Loading

0 comments on commit 97933f4

Please sign in to comment.