diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index a657d21825..3389b0c892 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -237,7 +237,7 @@ jobs: cd build ctest --output-on-failure - gcc9_py38_pd_nompi_h5_ad2_libcpp: + gcc9_py38_pd_nompi_h5_ad2_libcpp_julia: runs-on: ubuntu-20.04 if: github.event.pull_request.draft == false steps: @@ -246,17 +246,31 @@ jobs: run: | sudo apt-get update sudo apt-get install g++ libopenmpi-dev libhdf5-openmpi-dev python3 python3-numpy python3-mpi4py python3-pandas -# TODO ADIOS2 + # TODO ADIOS2 + # Install Julia + # 1.6.7 + wget https://julialang-s3.julialang.org/bin/linux/x64/1.7/julia-1.7.3-linux-x86_64.tar.gz + sudo tar -xz -C /usr/local -f julia-1.7.3-linux-x86_64.tar.gz + rm julia-1.7.3-linux-x86_64.tar.gz + # Install cmake-easyinstall + sudo curl -L -o /usr/local/bin/cmake-easyinstall https://git.io/JvLxY + sudo chmod a+x /usr/local/bin/cmake-easyinstall + export CEI_SUDO="sudo" + # Install libcxxwrap-julia + cmake-easyinstall git+https://github.com/JuliaInterop/libcxxwrap-julia.git@v0.9.7 -DJulia_EXECUTABLE=/usr/local/julia-1.7.3/bin/julia - name: Build env: {CXXFLAGS: -Werror, PKG_CONFIG_PATH: /usr/lib/x86_64-linux-gnu/pkgconfig} run: | share/openPMD/download_samples.sh build - cmake -S . -B build \ - -DopenPMD_USE_PYTHON=ON \ - -DopenPMD_USE_MPI=ON \ + cmake -S . -B build \ -DopenPMD_USE_HDF5=ON \ + -DopenPMD_USE_JULIA=ON \ + -DopenPMD_USE_MPI=ON \ + -DopenPMD_USE_PYTHON=ON \ -DopenPMD_USE_INVASIVE_TESTS=ON cmake --build build --parallel 2 + # Install the Julia side of CxxWrap + julia --eval 'using Pkg; Pkg.add("CxxWrap")' cd build ctest --output-on-failure diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index bfc70bf508..da537d26f7 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -24,9 +24,15 @@ jobs: brew update brew install adios2 || true brew install hdf5-mpi || true + brew install julia || true brew install python || true python3 -m pip install -U mpi4py numpy pandas set -e + # Install cmake-easyinstall + sudo curl -L -o /usr/local/bin/cmake-easyinstall https://git.io/JvLxY + sudo chmod a+x /usr/local/bin/cmake-easyinstall + # Install libcxxwrap-julia + cmake-easyinstall git+https://github.com/JuliaInterop/libcxxwrap-julia.git@v0.11.0 - name: Build env: {CXXFLAGS: -Werror, MACOSX_DEPLOYMENT_TARGET: 11.0} # 10.14+ due to std::visit @@ -36,12 +42,15 @@ jobs: run: | share/openPMD/download_samples.sh build cmake -S . -B build \ - -DopenPMD_USE_PYTHON=ON \ - -DopenPMD_USE_MPI=ON \ - -DopenPMD_USE_HDF5=ON \ -DopenPMD_USE_ADIOS2=ON \ + -DopenPMD_USE_HDF5=ON \ + -DopenPMD_USE_JULIA=ON \ + -DopenPMD_USE_MPI=ON \ + -DopenPMD_USE_PYTHON=ON \ -DopenPMD_USE_INVASIVE_TESTS=ON cmake --build build --parallel 3 + # Install the Julia side of CxxWrap + julia --eval 'using Pkg; Pkg.add("CxxWrap")' ctest --test-dir build --verbose appleclang13_py: diff --git a/.readthedocs.yml b/.readthedocs.yml index 2fc118e82d..d72547f396 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -14,4 +14,5 @@ build: tools: python: "3.11" apt_packages: + - graphviz - librsvg2-bin diff --git a/CMakeLists.txt b/CMakeLists.txt index bf9972177f..96db4a5793 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,10 @@ # cmake_minimum_required(VERSION 3.15.0) +# macOS 10.15 is required for certain C++17 features. +# (This variable needs to be set before calling `project`.) +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") + project(openPMD VERSION 0.16.0) # LANGUAGES CXX # the openPMD "markup"/"schema" standard version @@ -143,6 +147,7 @@ endfunction() openpmd_option(MPI "Parallel, Multi-Node I/O for clusters" AUTO) openpmd_option(HDF5 "HDF5 backend (.h5 files)" AUTO) openpmd_option(ADIOS2 "ADIOS2 backend (.bp files)" AUTO) +openpmd_option(JULIA "Enable Julia bindings" OFF) openpmd_option(PYTHON "Enable Python bindings" AUTO) option(openPMD_INSTALL "Add installation targets" ON) @@ -394,6 +399,19 @@ endif() # TODO: Check if ADIOS2 is parallel when openPMD_HAVE_MPI is ON +# External library: libcxxwrap-julia +if(openPMD_USE_JULIA) + find_package(JlCxx 0.8.3 REQUIRED) + set(openPMD_HAVE_JULIA TRUE) +else() + set(openPMD_HAVE_JULIA FALSE) +endif() +if(openPMD_HAVE_JULIA) + get_target_property(JlCxx_location JlCxx::cxxwrap_julia LOCATION) + get_filename_component(JlCxx_location ${JlCxx_location} DIRECTORY) + message(STATUS "Found JlCxx version ${JlCxx_VERSION} at ${JlCxx_location}") +endif() + # external library: pybind11 (optional) set(_PY_DEV_MODULE Development.Module) if(CMAKE_VERSION VERSION_LESS 3.18.0) @@ -599,6 +617,57 @@ else() target_compile_definitions(openPMD PRIVATE openPMD_USE_VERIFY=0) endif() +# Julia bindings +if(openPMD_HAVE_JULIA) + add_library(openPMD.jl SHARED + src/binding/julia/Access.cpp + src/binding/julia/Attributable.cpp + src/binding/julia/Attribute.cpp + src/binding/julia/BaseRecordComponent.cpp + src/binding/julia/ChunkInfo.cpp + src/binding/julia/Container.cpp + src/binding/julia/Dataset.cpp + src/binding/julia/Datatype.cpp + src/binding/julia/Format.cpp + src/binding/julia/Iteration.cpp + src/binding/julia/Mesh.cpp + src/binding/julia/MeshRecordComponent.cpp + src/binding/julia/ReadIterations.cpp + src/binding/julia/RecordComponent.cpp + src/binding/julia/RecordComponent_load_chunk.cpp + src/binding/julia/RecordComponent_make_constant.cpp + src/binding/julia/RecordComponent_store_chunk.cpp + src/binding/julia/Series.cpp + src/binding/julia/UnitDimension.cpp + src/binding/julia/WriteIterations.cpp + src/binding/julia/openPMD.cpp + src/binding/julia/shared_ptr.cpp + src/binding/julia/version.cpp + ) + openpmd_cxx_required(openPMD.jl) + target_link_libraries(openPMD.jl PRIVATE openPMD JlCxx::cxxwrap_julia_stl JlCxx::cxxwrap_julia) + + set_target_properties(openPMD.jl PROPERTIES + COMPILE_PDB_NAME openPMD.jl + ARCHIVE_OUTPUT_DIRECTORY ${openPMD_ARCHIVE_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${openPMD_LIBRARY_OUTPUT_DIRECTORY} + RUNTIME_OUTPUT_DIRECTORY ${openPMD_RUNTIME_OUTPUT_DIRECTORY} + PDB_OUTPUT_DIRECTORY ${openPMD_PDB_OUTPUT_DIRECTORY} + COMPILE_PDB_OUTPUT_DIRECTORY ${openPMD_COMPILE_PDB_OUTPUT_DIRECTORY} + + POSITION_INDEPENDENT_CODE ON + WINDOWS_EXPORT_ALL_SYMBOLS ON + ) + + add_test(NAME CLI.julia_lowlevel + COMMAND julia + ${openPMD_SOURCE_DIR}/test/julia/lowlevel_test.jl + ${openPMD_LIBRARY_OUTPUT_DIRECTORY}/libopenPMD.jl + WORKING_DIRECTORY + ${openPMD_RUNTIME_OUTPUT_DIRECTORY} + ) +endif() + # python bindings if(openPMD_HAVE_PYTHON) add_library(openPMD.py MODULE @@ -1061,11 +1130,18 @@ if(openPMD_INSTALL) endforeach() endif() + if(openPMD_HAVE_JULIA) + list(APPEND openPMD_INSTALL_TARGET_NAMES openPMD.jl) + endif() + if(openPMD_INSTALL_RPATH) set(openPMD_INSTALL_RPATH_TARGET_NAMES ${openPMD_INSTALL_TARGET_NAMES}) if(openPMD_HAVE_PYTHON) list(APPEND openPMD_INSTALL_RPATH_TARGET_NAMES openPMD.py) endif() + if(openPMD_HAVE_JULIA) + list(APPEND openPMD_INSTALL_RPATH_TARGET_NAMES openPMD.jl) + endif() if(NOT DEFINED CMAKE_INSTALL_RPATH) if(APPLE) set_target_properties(${openPMD_INSTALL_RPATH_TARGET_NAMES} PROPERTIES diff --git a/NEWS.rst b/NEWS.rst index db59c2ab0f..5eab96366e 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -13,6 +13,8 @@ Please transition to ADIOS2. For reading legacy ADIOS1 BP3 files, either use an older version of openPMD-api or the BP3 backend in ADIOS2. Note that ADIOS2 does not support compression in BP3 files. +Julia 1.7 to 1.9 are supported. The development version of Julia +(future 1.10) is supported as well. Julia 1.6 is too old. pybind11 2.11.1 is now the minimally supported version for Python support. diff --git a/README.md b/README.md index ace6eb47a4..3cb41b6b99 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,12 @@ while those can be built either with or without: * MPI 2.1+, e.g. OpenMPI 1.6.5+ or MPICH2 Optional language bindings: +* Julia: + * Most Julia users will not build `openPMD_api` from source. The + Julia package `openPMD.jl` will automatically download a suitable + `openPMD_api` binary. + * Julia 1.7 - 1.10 + * [libcxxwrap_julia](https://github.com/JuliaInterop/libcxxwrap-julia) 0.8.3 - 0.9.7 * Python: * Python 3.8 - 3.11 * pybind11 2.11.1+ diff --git a/docs/Doxyfile b/docs/Doxyfile index d1835b60ed..c0ca578c88 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -36,3 +36,4 @@ PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS \ WARN_IF_UNDOCUMENTED = NO WARN_NO_PARAMDOC = NO # the ideal CI enforcing world: WARN_AS_ERROR = YES +WARN_AS_ERROR = YES diff --git a/docs/source/install/install.rst b/docs/source/install/install.rst index a0268a2a99..4d4d21e684 100644 --- a/docs/source/install/install.rst +++ b/docs/source/install/install.rst @@ -123,6 +123,18 @@ Additional CMake options can be passed via individual environment variables, whi .. image:: cmake.svg +Using the Julia Package +----------------------- + +A package for openPMD-api is available via the Julia Package Registry: + +.. code-block:: julia + + # We need Julia 1.7 or newer + using("Pkg") + Pkg.add("openPMD") + + From Source with CMake ---------------------- @@ -147,7 +159,7 @@ Linux & OSX # -DCMAKE_INSTALL_PREFIX=$HOME/somepath # for options append: # -DopenPMD_USE_...=... - # e.g. for python support add: + # e.g. for Python support add: # -DopenPMD_USE_PYTHON=ON -DPython_EXECUTABLE=$(which python3) cmake ../openPMD-api diff --git a/include/openPMD/Series.hpp b/include/openPMD/Series.hpp index 1d99b54a84..a7db5403eb 100644 --- a/include/openPMD/Series.hpp +++ b/include/openPMD/Series.hpp @@ -55,7 +55,6 @@ namespace openPMD class ReadIterations; class SeriesIterator; class Series; -class Series; namespace internal { diff --git a/src/binding/julia/Access.cpp b/src/binding/julia/Access.cpp new file mode 100644 index 0000000000..b317401e8d --- /dev/null +++ b/src/binding/julia/Access.cpp @@ -0,0 +1,17 @@ +/* Bindings for IO/Access + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +void define_julia_Access(jlcxx::Module &mod) +{ + mod.add_bits("Access", jlcxx::julia_type("CppEnum")); + jlcxx::stl::apply_stl(mod); + + mod.set_const("ACCESS_READ_ONLY", Access::READ_ONLY); + mod.set_const("ACCESS_READ_WRITE", Access::READ_WRITE); + mod.set_const("ACCESS_CREATE", Access::CREATE); +} diff --git a/src/binding/julia/Attributable.cpp b/src/binding/julia/Attributable.cpp new file mode 100644 index 0000000000..50a3609ac5 --- /dev/null +++ b/src/binding/julia/Attributable.cpp @@ -0,0 +1,39 @@ +/* Bindings for Attributable + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +namespace +{ +struct method_set_attribute +{ + template + void call(jlcxx::TypeWrapper type) const + { + type.method( + "cxx_set_attribute_" + datatypeToString(determineDatatype()) + + "!", + &Attributable::setAttribute); + } +}; +} // namespace + +void define_julia_Attributable(jlcxx::Module &mod) +{ + auto type = mod.add_type("CXX_Attributable"); + + forallJuliaTypes(method_set_attribute(), type); + + type.method("cxx_get_attribute", &Attributable::getAttribute); + type.method("cxx_delete_attribute!", &Attributable::deleteAttribute); + type.method("cxx_attributes", &Attributable::attributes); + type.method("cxx_num_attributes", &Attributable::numAttributes); + type.method("cxx_contains_attribute", &Attributable::containsAttribute); + type.method("cxx_comment", &Attributable::comment); + type.method("cxx_set_comment!", &Attributable::setComment); + type.method( + "cxx_series_flush", [](Attributable &attr) { attr.seriesFlush(); }); +} diff --git a/src/binding/julia/Attribute.cpp b/src/binding/julia/Attribute.cpp new file mode 100644 index 0000000000..9c15771bbc --- /dev/null +++ b/src/binding/julia/Attribute.cpp @@ -0,0 +1,30 @@ +/* Bindings for Attribute + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +namespace +{ +struct method_get +{ + template + void call(jlcxx::TypeWrapper type) const + { + type.method( + "cxx_get_" + datatypeToString(determineDatatype()), + &Attribute::get); + } +}; +} // namespace + +void define_julia_Attribute(jlcxx::Module &mod) +{ + auto type = mod.add_type("CXX_Attribute"); + + type.method("cxx_dtype", [](const Attribute &attr) { return attr.dtype; }); + + forallJuliaTypes(method_get(), type); +} diff --git a/src/binding/julia/BaseRecordComponent.cpp b/src/binding/julia/BaseRecordComponent.cpp new file mode 100644 index 0000000000..4ffd41a277 --- /dev/null +++ b/src/binding/julia/BaseRecordComponent.cpp @@ -0,0 +1,29 @@ +/* Bindings for BaseRecordComponent + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +// Define supertype relationships +namespace jlcxx +{ +template <> +struct SuperType +{ + using type = Attributable; +}; +} // namespace jlcxx + +void define_julia_BaseRecordComponent(jlcxx::Module &mod) +{ + auto type = mod.add_type( + "CXX_BaseRecordComponent", jlcxx::julia_base_type()); + + type.method("cxx_unit_SI", &BaseRecordComponent::unitSI); + type.method("cxx_reset_datatype!", &BaseRecordComponent::resetDatatype); + type.method("cxx_get_datatype", &BaseRecordComponent::getDatatype); + type.method("cxx_isconstant", &BaseRecordComponent::constant); + type.method("cxx_available_chunks", &BaseRecordComponent::availableChunks); +} diff --git a/src/binding/julia/ChunkInfo.cpp b/src/binding/julia/ChunkInfo.cpp new file mode 100644 index 0000000000..2b396652c7 --- /dev/null +++ b/src/binding/julia/ChunkInfo.cpp @@ -0,0 +1,35 @@ +/* Bindings for ChunkInfo + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +// Define supertype relationships +namespace jlcxx +{ +template <> +struct SuperType +{ + using type = ChunkInfo; +}; +} // namespace jlcxx + +void define_julia_ChunkInfo(jlcxx::Module &mod) +{ + auto chunkInfo = mod.add_type("CXX_ChunkInfo"); + chunkInfo.constructor<>(); + chunkInfo.constructor(); + chunkInfo.method("cxx_offset", [](const ChunkInfo &chunkInfo_) { + return chunkInfo_.offset; + }); + chunkInfo.method("cxx_extent", [](const ChunkInfo &chunkInfo_) { + return chunkInfo_.extent; + }); + + auto writtenChunkInfo = mod.add_type( + "CXX_WrittenChunkInfo", jlcxx::julia_base_type()); + writtenChunkInfo.constructor<>(); + writtenChunkInfo.constructor(); +} diff --git a/src/binding/julia/Container.cpp b/src/binding/julia/Container.cpp new file mode 100644 index 0000000000..66e8729319 --- /dev/null +++ b/src/binding/julia/Container.cpp @@ -0,0 +1,9 @@ +/* Bindings for Container + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "Container.hpp" + +std::unique_ptr julia_Container_type; diff --git a/src/binding/julia/Container.hpp b/src/binding/julia/Container.hpp new file mode 100644 index 0000000000..70db8749b4 --- /dev/null +++ b/src/binding/julia/Container.hpp @@ -0,0 +1,95 @@ +/* Bindings for Container + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#ifndef CONTAINER_HPP +#define CONTAINER_HPP + +// Container + +#include "defs.hpp" + +#include +#include + +// Define supertype relationships +namespace jlcxx +{ +template +struct SuperType> +{ + using type = Attributable; +}; +} // namespace jlcxx + +using julia_Container_type_t = + jlcxx::TypeWrapper, jlcxx::TypeVar<2>>>; +// TODO: use std::optional instead of std::unique_ptr +extern std::unique_ptr julia_Container_type; + +template +void define_julia_Container(jlcxx::Module &mod) +{ + if (!julia_Container_type) + julia_Container_type = std::make_unique( + mod.add_type< + jlcxx::Parametric, jlcxx::TypeVar<2>>>( + "CXX_Container", jlcxx::julia_base_type())); + + julia_Container_type->apply>([](auto type) { + using ContainerT = typename decltype(type)::type; + using key_type = typename ContainerT::key_type; + using mapped_type = typename ContainerT::mapped_type; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + type.template constructor(); + + type.method("cxx_empty", &ContainerT::empty); + type.method("cxx_length", &ContainerT::size); + type.method("cxx_empty!", &ContainerT::clear); + // type.method("cxx_getindex", + // static_cast( + // &ContainerT::at)); + type.method( + "cxx_getindex", + [](ContainerT &cont, const key_type &key) -> mapped_type & { + return cont[key]; + }); + type.method( + "cxx_setindex!", + [](ContainerT &cont, + const mapped_type &value, + const key_type &key) { return cont[key] = value; }); + type.method("cxx_count", &ContainerT::count); + type.method("cxx_contains", &ContainerT::contains); + type.method( + "cxx_delete!", overload_cast(&ContainerT::erase)); + type.method("cxx_keys", [](const ContainerT &cont) { + std::vector res; + res.reserve(cont.size()); + for (auto iter = cont.begin(); iter != cont.end(); ++iter) + res.push_back(iter->first); + return res; + }); + // type.method("cxx_values", [](const ContainerT &cont) { + // std::vector res; + // res.reserve(cont.size()); + // for (auto iter = cont.begin(); iter != cont.end(); ++iter) + // res.push_back(&iter->second); + // return res; + // }); + // type.method("cxx_collect", [](const ContainerT &cont) { + // std::vector> res; + // res.reserve(cont.size()); + // for (auto iter = cont.begin(); iter != cont.end(); ++iter) + // res.emplace_back(iter->first, &iter->second); + // return res; + // }); + }); +} + +#endif // #ifndef CONTAINER_HPP diff --git a/src/binding/julia/Dataset.cpp b/src/binding/julia/Dataset.cpp new file mode 100644 index 0000000000..14fbbd20b6 --- /dev/null +++ b/src/binding/julia/Dataset.cpp @@ -0,0 +1,22 @@ +/* Bindings for Datset + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +void define_julia_Dataset(jlcxx::Module &mod) +{ + auto type = mod.add_type("Dataset"); + + type.constructor(); + type.constructor(); + type.constructor(); + + type.method("cxx_extend!", &Dataset::extend); + type.method("cxx_extent", [](const Dataset &d) { return d.extent; }); + type.method("cxx_dtype", [](const Dataset &d) { return d.dtype; }); + type.method("cxx_rank", [](const Dataset &d) { return d.rank; }); + type.method("options", [](const Dataset &d) { return d.options; }); +} diff --git a/src/binding/julia/Datatype.cpp b/src/binding/julia/Datatype.cpp new file mode 100644 index 0000000000..27bd420e4d --- /dev/null +++ b/src/binding/julia/Datatype.cpp @@ -0,0 +1,56 @@ +/* Bindings for Datatype + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +namespace +{ +struct set_const_Datatype +{ + template + void call(jlcxx::Module &mod) const + { + Datatype const dt = determineDatatype(); + // The second argument to `set_const` must be a value and not a + // reference + mod.set_const(datatypeToString(dt), Datatype(dt)); + } +}; +} // namespace + +void define_julia_Datatype(jlcxx::Module &mod) +{ + mod.add_bits("Datatype", jlcxx::julia_type("CppEnum")); + jlcxx::stl::apply_stl(mod); + + forallJuliaTypes(set_const_Datatype(), mod); + mod.set_const("UNDEFINED", Datatype::UNDEFINED); + + // mod.method("openpmd_datatypes", openPMD_datatypes); + // mod.method("determine_datatype", determineDatatype); + mod.method("cxx_to_bytes", toBytes); + mod.method("cxx_to_bits", toBits); + mod.method("cxx_is_vector", isVector); + mod.method( + "cxx_is_floating_point", + static_cast(isFloatingPoint)); + mod.method( + "cxx_is_complex_floating_point", + static_cast(isComplexFloatingPoint)); + mod.method( + "cxx_is_integer", (std::tuple(*)(Datatype))isInteger); + // isSameFloatingPoint + // isSameComplexFloatingPoint + // isSameInteger + mod.method("cxx_is_same", isSame); + mod.method("cxx_basic_datatype", basicDatatype); + mod.method("cxx_to_vector_type", toVectorType); + mod.method("cxx_datatype_to_string", datatypeToString); + mod.method("cxx_string_to_datatype", stringToDatatype); + mod.method("cxx_warn_wrong_datatype", warnWrongDtype); + // mod.method("==", operator==); + // mod.method("!=", operator!=); +} diff --git a/src/binding/julia/Format.cpp b/src/binding/julia/Format.cpp new file mode 100644 index 0000000000..083cc52ff7 --- /dev/null +++ b/src/binding/julia/Format.cpp @@ -0,0 +1,24 @@ +/* Bindings for IO/Format + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +void define_julia_Format(jlcxx::Module &mod) +{ + mod.add_bits("Format", jlcxx::julia_type("CppEnum")); + jlcxx::stl::apply_stl(mod); + + mod.set_const("FORMAT_HDF5", Format::HDF5); + mod.set_const("FORMAT_ADIOS2_BP", Format::ADIOS2_BP); + mod.set_const("FORMAT_ADIOS2_BP4", Format::ADIOS2_BP4); + mod.set_const("FORMAT_ADIOS2_BP5", Format::ADIOS2_BP5); + mod.set_const("FORMAT_ADIOS2_SST", Format::ADIOS2_SST); + mod.set_const("FORMAT_ADIOS2_SSC", Format::ADIOS2_SSC); + mod.set_const("FORMAT_JSON", Format::JSON); + mod.set_const("FORMAT_DUMMY", Format::DUMMY); + mod.method("determine_format", determineFormat); + mod.method("suffix", suffix); +} diff --git a/src/binding/julia/Iteration.cpp b/src/binding/julia/Iteration.cpp new file mode 100644 index 0000000000..28a8d858c3 --- /dev/null +++ b/src/binding/julia/Iteration.cpp @@ -0,0 +1,39 @@ +/* Bindings for Iteration + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +#include + +// Define supertype relationships +namespace jlcxx +{ +template <> +struct SuperType +{ + using type = Attributable; +}; +} // namespace jlcxx + +void define_julia_Iteration(jlcxx::Module &mod) +{ + auto type = mod.add_type( + "CXX_Iteration", jlcxx::julia_base_type()); + + type.method("cxx_time", &Iteration::time); + type.method("cxx_set_time!", &Iteration::setTime); + type.method("cxx_dt", &Iteration::dt); + type.method("cxx_set_dt!", &Iteration::setDt); + type.method("cxx_time_unit_SI", &Iteration::timeUnitSI); + type.method("cxx_set_time_unit_SI!", &Iteration::setTimeUnitSI); + type.method("cxx_close", overload_cast(&Iteration::close)); + type.method("cxx_open", &Iteration::open); + type.method("cxx_closed", &Iteration::closed); + type.method("cxx_meshes", [](Iteration &iter) -> Container & { + return iter.meshes; + }); + // TODO: particles +} diff --git a/src/binding/julia/Mesh.cpp b/src/binding/julia/Mesh.cpp new file mode 100644 index 0000000000..7fac56776b --- /dev/null +++ b/src/binding/julia/Mesh.cpp @@ -0,0 +1,82 @@ +/* Bindings for Mesh + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +// Define supertype relationships +namespace jlcxx +{ +template <> +struct SuperType +{ + using type = Container; +}; +} // namespace jlcxx + +void define_julia_Mesh(jlcxx::Module &mod) +{ + + // Mesh::Geometry + mod.add_bits("Geometry", jlcxx::julia_type("CppEnum")); + jlcxx::stl::apply_stl(mod); + + mod.set_const("GEOMETRY_cartesian", Mesh::Geometry::cartesian); + mod.set_const("GEOMETRY_theta_mode", Mesh::Geometry::thetaMode); + mod.set_const("GEOMETRY_cylindrical", Mesh::Geometry::cylindrical); + mod.set_const("GEOMETRY_spherical", Mesh::Geometry::spherical); + mod.set_const("GEOMETRY_other", Mesh::Geometry::other); + + // Mesh::DataOrder + mod.add_bits("DataOrder", jlcxx::julia_type("CppEnum")); + jlcxx::stl::apply_stl(mod); + + mod.set_const("DATAORDER_C", Mesh::DataOrder::C); + mod.set_const("DATAORDER_F", Mesh::DataOrder::F); + + // Mesh + auto type = mod.add_type( + "CXX_Mesh", + // We don't wrap BaseRecord for simplicity. We thus need to declare + // Container as our supertype. + jlcxx::julia_base_type>()); + + // These two functions come from our superclass + // BaseRecord. We declare them as if they were our own. + type.method("cxx_unit_dimension", &Mesh::unitDimension); + type.method("cxx_isscalar", &Mesh::scalar); + + type.method("cxx_geometry", &Mesh::geometry); + type.method( + "cxx_set_geometry!", overload_cast(&Mesh::setGeometry)); + type.method("cxx_geometry_parameters", &Mesh::geometryParameters); + type.method("cxx_set_geometry_parameters!", &Mesh::setGeometryParameters); + type.method("cxx_data_order", &Mesh::dataOrder); + type.method("cxx_set_data_order!", &Mesh::setDataOrder); + type.method("cxx_axis_labels", &Mesh::axisLabels); + type.method("cxx_set_axis_labels!", &Mesh::setAxisLabels); + type.method("cxx_grid_spacing", &Mesh::gridSpacing); + type.method("cxx_set_grid_spacing!", &Mesh::setGridSpacing); + type.method("cxx_grid_global_offset", &Mesh::gridGlobalOffset); + type.method("cxx_set_grid_global_offset!", &Mesh::setGridGlobalOffset); + type.method("cxx_grid_unit_SI", &Mesh::gridUnitSI); + type.method("cxx_set_grid_unit_SI!", &Mesh::setGridUnitSI); + type.method( + "cxx_set_unit_dimension!", + [](Mesh &mesh, const array_double_7 &unitDimension) { + return mesh.setUnitDimension(std::map{ + {UnitDimension::L, unitDimension[uint8_t(UnitDimension::L)]}, + {UnitDimension::M, unitDimension[uint8_t(UnitDimension::M)]}, + {UnitDimension::T, unitDimension[uint8_t(UnitDimension::T)]}, + {UnitDimension::I, unitDimension[uint8_t(UnitDimension::I)]}, + {UnitDimension::theta, + unitDimension[uint8_t(UnitDimension::theta)]}, + {UnitDimension::N, unitDimension[uint8_t(UnitDimension::N)]}, + {UnitDimension::J, unitDimension[uint8_t(UnitDimension::J)]}, + }); + }); + type.method("cxx_time_offset", &Mesh::timeOffset); + type.method("cxx_set_time_offset!", &Mesh::setTimeOffset); +} diff --git a/src/binding/julia/MeshRecordComponent.cpp b/src/binding/julia/MeshRecordComponent.cpp new file mode 100644 index 0000000000..e23260caf4 --- /dev/null +++ b/src/binding/julia/MeshRecordComponent.cpp @@ -0,0 +1,41 @@ +/* Bindings for MeshRecordComponent + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +// Define supertype relationships +namespace jlcxx +{ +template <> +struct SuperType +{ + using type = RecordComponent; +}; +} // namespace jlcxx + +namespace +{ +struct method_make_constant +{ + template + void call(jlcxx::TypeWrapper type) const + { + type.method( + "cxx_make_constant_" + datatypeToString(determineDatatype()), + &MeshRecordComponent::makeConstant); + } +}; +} // namespace + +void define_julia_MeshRecordComponent(jlcxx::Module &mod) +{ + auto type = mod.add_type( + "CXX_MeshRecordComponent", jlcxx::julia_base_type()); + + type.method("cxx_position", &MeshRecordComponent::position); + type.method("cxx_set_position!", &MeshRecordComponent::setPosition); + forallJuliaTypes(method_make_constant(), type); +} diff --git a/src/binding/julia/ReadIterations.cpp b/src/binding/julia/ReadIterations.cpp new file mode 100644 index 0000000000..637b0654ac --- /dev/null +++ b/src/binding/julia/ReadIterations.cpp @@ -0,0 +1,16 @@ +/* Bindings for ReadIterations + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +#include + +void define_julia_ReadIterations(jlcxx::Module & /*mod*/) +{ + // TODO auto type = mod.add_type("ReadIterations"); + // TODO: SeriesIterator + // TODO: begin, end +} diff --git a/src/binding/julia/RecordComponent.cpp b/src/binding/julia/RecordComponent.cpp new file mode 100644 index 0000000000..f67fcd5bf0 --- /dev/null +++ b/src/binding/julia/RecordComponent.cpp @@ -0,0 +1,52 @@ +/* Bindings for RecordComponent + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +#include +#include +#include +#include + +// Define supertype relationships +namespace jlcxx +{ +template <> +struct SuperType +{ + using type = BaseRecordComponent; +}; +} // namespace jlcxx + +void define_julia_RecordComponent(jlcxx::Module &mod) +{ + + // RecordComponent::Allocation + mod.add_bits( + "Allocation", jlcxx::julia_type("CppEnum")); + jlcxx::stl::apply_stl(mod); + + mod.set_const("ALLOCATION_USER", RecordComponent::Allocation::USER); + mod.set_const("ALLOCATION_API", RecordComponent::Allocation::API); + mod.set_const("ALLOCATION_AUTO", RecordComponent::Allocation::AUTO); + + auto type = mod.add_type( + "CXX_RecordComponent", jlcxx::julia_base_type()); + + type.method("cxx_set_unit_SI!", &RecordComponent::setUnitSI); + type.method("cxx_reset_dataset!", &RecordComponent::resetDataset); + type.method("cxx_get_dimensionality", &RecordComponent::getDimensionality); + type.method("cxx_get_extent", &RecordComponent::getExtent); + define_julia_RecordComponent_make_constant(mod, type); + type.method( + "cxx_make_empty", + static_cast( + &RecordComponent::makeEmpty)); + type.method("cxx_isempty", &RecordComponent::empty); + define_julia_RecordComponent_load_chunk(mod, type); + define_julia_RecordComponent_store_chunk(mod, type); + type.method("cxx_SCALAR", []() { return RecordComponent::SCALAR; }); +} diff --git a/src/binding/julia/RecordComponent_load_chunk.cpp b/src/binding/julia/RecordComponent_load_chunk.cpp new file mode 100644 index 0000000000..116eda00a2 --- /dev/null +++ b/src/binding/julia/RecordComponent_load_chunk.cpp @@ -0,0 +1,28 @@ +/* Bindings for RecordComponent::load_chunk + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +namespace +{ +struct method_load_chunk +{ + template + void call(jlcxx::TypeWrapper &type) const + { + type.method( + "cxx_load_chunk_" + datatypeToString(determineDatatype()), + overload_cast, Offset, Extent>( + &RecordComponent::loadChunk)); + } +}; +} // namespace + +void define_julia_RecordComponent_load_chunk( + jlcxx::Module & /*mod*/, jlcxx::TypeWrapper &type) +{ + forallScalarJuliaTypes(method_load_chunk(), type); +} diff --git a/src/binding/julia/RecordComponent_make_constant.cpp b/src/binding/julia/RecordComponent_make_constant.cpp new file mode 100644 index 0000000000..622f3097cf --- /dev/null +++ b/src/binding/julia/RecordComponent_make_constant.cpp @@ -0,0 +1,27 @@ +/* Bindings for RecordComponent::make_constant + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +namespace +{ +struct method_make_constant +{ + template + void call(jlcxx::TypeWrapper &type) const + { + type.method( + "cxx_make_constant_" + datatypeToString(determineDatatype()), + &RecordComponent::makeConstant); + } +}; +} // namespace + +void define_julia_RecordComponent_make_constant( + jlcxx::Module & /*mod*/, jlcxx::TypeWrapper &type) +{ + forallScalarJuliaTypes(method_make_constant(), type); +} diff --git a/src/binding/julia/RecordComponent_store_chunk.cpp b/src/binding/julia/RecordComponent_store_chunk.cpp new file mode 100644 index 0000000000..355c42045a --- /dev/null +++ b/src/binding/julia/RecordComponent_store_chunk.cpp @@ -0,0 +1,28 @@ +/* Bindings for RecordComponent::store_chunk + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +namespace +{ +struct method_store_chunk +{ + template + void call(jlcxx::TypeWrapper &type) const + { + type.method( + "cxx_store_chunk_" + datatypeToString(determineDatatype()), + overload_cast, Offset, Extent>( + &RecordComponent::storeChunk)); + } +}; +} // namespace + +void define_julia_RecordComponent_store_chunk( + jlcxx::Module & /*mod*/, jlcxx::TypeWrapper &type) +{ + forallScalarJuliaTypes(method_store_chunk(), type); +} diff --git a/src/binding/julia/Series.cpp b/src/binding/julia/Series.cpp new file mode 100644 index 0000000000..6d0524f447 --- /dev/null +++ b/src/binding/julia/Series.cpp @@ -0,0 +1,119 @@ +/* Bindings for Series + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +#include + +#if openPMD_HAVE_MPI +#include + +namespace jlcxx +{ +template <> +struct IsMirroredType : std::false_type +{}; +} // namespace jlcxx +#endif + +// Define supertype relationships +namespace jlcxx +{ +template <> +struct SuperType +{ + using type = Attributable; +}; +} // namespace jlcxx + +void define_julia_Series(jlcxx::Module &mod) +{ + // Series + + auto type = mod.add_type( + "CXX_Series", jlcxx::julia_base_type()); + + type.constructor<>(); +#if openPMD_HAVE_MPI + type.method( + "cxx_Series", + [](const std::string &filepath, + Access at, + sized_uint_t ucomm, + const std::string &options) { + MPI_Comm comm; + static_assert(sizeof ucomm == sizeof comm); + memcpy(&comm, &ucomm, sizeof comm); + return Series(filepath, at, comm, options); + }); + type.method( + "cxx_Series", + [](const std::string &filepath, + Access at, + sized_uint_t ucomm) { + MPI_Comm comm; + static_assert(sizeof ucomm == sizeof comm); + memcpy(&comm, &ucomm, sizeof comm); + return Series(filepath, at, comm); + }); +#endif + type.constructor(); + type.constructor(); + + type.method( + "cxx_isvalid", [](const Series &series) { return bool(series); }); + + type.method("cxx_openPMD_version", &Series::openPMD); + type.method("cxx_set_openPMD_version!", &Series::setOpenPMD); + type.method("cxx_openPMD_extension", &Series::openPMDextension); + type.method("cxx_set_openPMD_extension!", &Series::setOpenPMDextension); + type.method("cxx_base_path", &Series::basePath); + type.method("cxx_set_base_path!", &Series::setBasePath); + type.method("cxx_meshes_path", &Series::meshesPath); + type.method("cxx_set_meshes_path!", &Series::setMeshesPath); + type.method("cxx_particles_path", &Series::particlesPath); + type.method("cxx_set_particles_path!", &Series::setParticlesPath); + type.method("cxx_author", &Series::author); + type.method("cxx_set_author!", &Series::setAuthor); + type.method("cxx_software", &Series::software); + type.method( + "cxx_set_software!", + overload_cast( + &Series::setSoftware)); + type.method( + "cxx_set_software!", + [](Series &series, const std::string &newName) -> Series & { + return series.setSoftware(newName); + }); + type.method("cxx_software_version", &Series::softwareVersion); + // type.method("set_software_version!", + // &Series::setSoftwareVersion); + type.method("cxx_date", &Series::date); + type.method("cxx_set_date!", &Series::setDate); + type.method("cxx_software_dependencies", &Series::softwareDependencies); + type.method( + "cxx_set_software_dependencies!", &Series::setSoftwareDependencies); + type.method("cxx_machine", &Series::machine); + type.method("cxx_set_machine!", &Series::setMachine); + // TODO: type.method("iteration_encoding", + // &Series::iterationEncoding); + // TODO: type.method("set_iteration_encoding!", + // &Series::setIterationEncoding); + type.method("cxx_iteration_format", &Series::iterationFormat); + type.method("cxx_set_iteration_format!", &Series::setIterationFormat); + type.method("cxx_name", &Series::name); + type.method("cxx_set_name!", &Series::setName); + type.method("cxx_backend", &Series::backend); + type.method("cxx_flush", &Series::flush); + + type.method( + "cxx_iterations", + [](Series &series) -> Container & { + return series.iterations; + }); + // TODO type.method("read_iterations", &Series::readIterations); + type.method("cxx_write_iterations", &Series::writeIterations); +} diff --git a/src/binding/julia/UnitDimension.cpp b/src/binding/julia/UnitDimension.cpp new file mode 100644 index 0000000000..57fd1e7c44 --- /dev/null +++ b/src/binding/julia/UnitDimension.cpp @@ -0,0 +1,21 @@ +/* Bindings for IO/UnitDimension + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +void define_julia_UnitDimension(jlcxx::Module &mod) +{ + mod.add_bits("UnitDimension", jlcxx::julia_type("CppEnum")); + jlcxx::stl::apply_stl(mod); + + mod.set_const("UNITDIMENSION_L", UnitDimension::L); + mod.set_const("UNITDIMENSION_M", UnitDimension::M); + mod.set_const("UNITDIMENSION_T", UnitDimension::T); + mod.set_const("UNITDIMENSION_I", UnitDimension::I); + mod.set_const("UNITDIMENSION_θ", UnitDimension::theta); + mod.set_const("UNITDIMENSION_N", UnitDimension::N); + mod.set_const("UNITDIMENSION_J", UnitDimension::J); +} diff --git a/src/binding/julia/WriteIterations.cpp b/src/binding/julia/WriteIterations.cpp new file mode 100644 index 0000000000..03da048198 --- /dev/null +++ b/src/binding/julia/WriteIterations.cpp @@ -0,0 +1,21 @@ +/* Bindings for WriteIterations + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +#include + +void define_julia_WriteIterations(jlcxx::Module &mod) +{ + using key_type = Series::IterationIndex_t; + // using mapped_type = typename iterations_t::mapped_type; + + auto type = mod.add_type("WriteIterations"); + type.method( + "getindex1!", [](WriteIterations &w, const key_type &k) -> Iteration & { + return w[k]; + }); +} diff --git a/src/binding/julia/defs.hpp b/src/binding/julia/defs.hpp new file mode 100644 index 0000000000..995224d062 --- /dev/null +++ b/src/binding/julia/defs.hpp @@ -0,0 +1,342 @@ +/* General definitions + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#ifndef DEFS_HPP +#define DEFS_HPP + +#include "openPMD/openPMD.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace openPMD; + +template +struct sized_uint; +template <> +struct sized_uint<1> +{ + using type = std::uint8_t; +}; +template <> +struct sized_uint<2> +{ + using type = std::uint16_t; +}; +template <> +struct sized_uint<4> +{ + using type = std::uint32_t; +}; +template <> +struct sized_uint<8> +{ + using type = std::uint64_t; +}; +template +using sized_uint_t = typename sized_uint::type; + +using array_double_7 = std::array; + +// From pybind11, see +// share/openPMD/thirdParty/pybind11/include/pybind11/detail/common.h +// in the source tree +struct non_const_tag +{}; +struct const_tag +{}; + +namespace detail +{ +template +struct overload_cast_impl +{ + template + constexpr auto operator()(Return (*pf)(Args...)) const noexcept + -> decltype(pf) + { + return pf; + } + + template + constexpr auto + operator()(Return (Class::*pmf)(Args...), non_const_tag = {}) const noexcept + -> decltype(pmf) + { + return pmf; + } + + template + constexpr auto + operator()(Return (Class::*pmf)(Args...) const, const_tag) const noexcept + -> decltype(pmf) + { + return pmf; + } +}; +} // namespace detail + +template +static constexpr ::detail::overload_cast_impl overload_cast{}; +constexpr const_tag const const_; + +/** + * Generalizes the repeated application of a function template for all + * scalar openPMD datatypes. + * + * Will call the function template found at Action::template call(), + * instantiating T with every scalar openPMD datatype as found + * in the Datatype enum. + * + * @tparam Action The struct containing the function template. + * @tparam Args The function template's argument types. + * @param action The function template's arguments. + * @param args The function template's arguments. + */ +template +void forallScalarJuliaTypes(const Action &action, Args &...args) +{ + // Do NOT call `std::forward(args)...` here. + // We must avoid move semantics because we apply repeatedly. + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + // We disable `long double` since Julia does not support this type + // action. call(args...); + action.template call>(args...); + action.template call>(args...); + // action. call>(args...); + action.template call(args...); + action.template call(args...); +} +/** + * Generalizes the repeated application of a function template for all + * openPMD datatypes. + * + * Will call the function template found at Action::template call(), + * instantiating T with every scalar datatype as found in the Datatype enum. + * + * @tparam Action The struct containing the function template. + * @tparam Args The function template's argument types. + * @param action The function template's arguments. + * @param args The function template's arguments. + */ +template +void forallJuliaTypes(const Action &action, Args &...args) +{ + // Do NOT call `std::forward(args)...` here. + // We must avoid move semantics because we apply repeatedly. + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + action.template call(args...); + // We disable `long double` since Julia does not support this type + // action.template call (args...); + action.template call>(args...); + action.template call>(args...); + // action.template call >(args...); + action.template call(args...); + action.template call>(args...); + action.template call>(args...); + action.template call>(args...); + action.template call>(args...); + action.template call>(args...); + action.template call>(args...); + action.template call>(args...); + action.template call>(args...); + action.template call>(args...); + action.template call>(args...); + action.template call>(args...); + action.template call>(args...); + action.template call>(args...); + // action.template call >(args...); + action.template call>>(args...); + action.template call>>(args...); + // action.template call >>(args...); + action.template call>(args...); + action.template call>(args...); + action.template call(args...); +} + +namespace +{ +template +std::vector> map_to_vector_pair(const std::map &m) +{ + std::vector> vp; + vp.reserve(m.size()); + for (const auto &p : m) + vp.push_back(p); + return vp; +} + +template +std::vector> map_to_vector_tuple(const std::map &m) +{ + std::vector> vp; + vp.reserve(m.size()); + for (const auto &p : m) + vp.emplace_back(p.first, p.second); + return vp; +} + +template +std::shared_ptr create_aliasing_shared_ptr(T *ptr) +{ + auto null_deleter = [](T *) {}; + return std::shared_ptr(ptr, null_deleter); +} + +template +std::shared_ptr capture_vector_as_buffer(std::vector &vec) +{ + if constexpr (std::is_same_v) + { + // We cannot handle std::vector because it is special + std::abort(); + } + else + { + auto deleter = [](T *) { /* do not delete anything */ }; + std::shared_ptr ptr(vec.data(), std::move(deleter)); + return ptr; + } +} + +template +std::shared_ptr capture_vector(std::vector vec) +{ + if constexpr (std::is_same_v) + { + // Copy the vector, because std::vector is special + T *dataptr = new T[vec.size()]; + std::shared_ptr ptr(dataptr, std::default_delete()); + std::copy(vec.begin(), vec.end(), dataptr); + return ptr; + } + else + { + // Capture the vector + T *dataptr = vec.data(); + auto deleter = [vec = std::move(vec)](T *) { + // We moved the vector into the anonymous function, and thus it will + // be destructed when the anonymous function is destructed. There is + // no need to call a destructor manually. + }; + std::shared_ptr ptr(dataptr, std::move(deleter)); + return ptr; + } +} + +template +void add_array_type(jlcxx::Module &mod, const std::string &name) +{ + mod.add_type>(name) + .template constructor<>() + .template constructor &>() + .method("size1", &std::array::size) + .method("getindex1", [](const std::array &a, std::size_t n) { + return a[n]; + }); + jlcxx::stl::apply_stl>(mod); +} + +template +void map_array_type(jlcxx::Module &mod, const std::string &name) +{ + mod.map_type>(name); + mod.method("size1", [](const std::array &a) { return a.size(); }); + mod.method("getindex1", [](const std::array &a, std::size_t n) { + return a[n]; + }); + jlcxx::stl::apply_stl>(mod); +} + +template +void add_pair_type(jlcxx::Module &mod, const std::string &name) +{ + mod.add_type>(name) + .template constructor<>() + .template constructor &>() + .method("first", [](const std::pair &p) { return p.first; }) + .method("second", [](const std::pair &p) { return p.second; }); + jlcxx::stl::apply_stl>(mod); +} + +} // namespace + +namespace jlcxx +{ +template <> +struct IsMirroredType : std::false_type +{}; +} // namespace jlcxx + +// We use one function per header file +void define_julia_Access(jlcxx::Module &mod); +void define_julia_Attributable(jlcxx::Module &mod); +void define_julia_Attribute(jlcxx::Module &mod); +void define_julia_BaseRecordComponent(jlcxx::Module &mod); +void define_julia_ChunkInfo(jlcxx::Module &mod); +template +void define_julia_Container(jlcxx::Module &mod); +void define_julia_Dataset(jlcxx::Module &mod); +void define_julia_Datatype(jlcxx::Module &mod); +void define_julia_Format(jlcxx::Module &mod); +void define_julia_Iteration(jlcxx::Module &mod); +void define_julia_Mesh(jlcxx::Module &mod); +void define_julia_MeshRecordComponent(jlcxx::Module &mod); +void define_julia_RecordComponent(jlcxx::Module &mod); +void define_julia_RecordComponent_load_chunk( + jlcxx::Module &mod, jlcxx::TypeWrapper &type); +void define_julia_RecordComponent_make_constant( + jlcxx::Module &mod, jlcxx::TypeWrapper &type); +void define_julia_RecordComponent_store_chunk( + jlcxx::Module &mod, jlcxx::TypeWrapper &type); +void define_julia_Series(jlcxx::Module &mod); +void define_julia_UnitDimension(jlcxx::Module &mod); +void define_julia_ReadIterations(jlcxx::Module &mod); +void define_julia_WriteIterations(jlcxx::Module &mod); +void define_julia_shared_ptr(jlcxx::Module &mod); +void define_julia_version(jlcxx::Module &mod); + +#endif // #ifndef DEFS_HPP diff --git a/src/binding/julia/openPMD.cpp b/src/binding/julia/openPMD.cpp new file mode 100644 index 0000000000..d90e432847 --- /dev/null +++ b/src/binding/julia/openPMD.cpp @@ -0,0 +1,60 @@ +/* Main file for Julia bindings + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +#include "Container.hpp" + +#include + +//////////////////////////////////////////////////////////////////////////////// + +JLCXX_MODULE define_julia_module(jlcxx::Module &mod) +{ + add_array_type(mod, "array_double_7"); + add_pair_type(mod, "pair_string_bool"); + + define_julia_shared_ptr(mod); + + // The order of these calls matters. Julia types need to be defined before + // they are used. + + // Stand-alone classes + define_julia_Access(mod); + define_julia_ChunkInfo(mod); + define_julia_Datatype(mod); + define_julia_Format(mod); + define_julia_UnitDimension(mod); + // All classes below need at least Datatype + + define_julia_Attribute(mod); + define_julia_Attributable(mod); + define_julia_Dataset(mod); + + define_julia_BaseRecordComponent(mod); // needs: Attributable + define_julia_RecordComponent(mod); // needs: BaseRecordComponent + define_julia_MeshRecordComponent(mod); // needs: RecordComponent + + define_julia_Container( + mod); // needs: Attributable, MeshRecordComponent + + define_julia_Mesh(mod); // needs: Container + + define_julia_Container(mod); // needs: Attributable + + define_julia_Iteration(mod); // needs: Attributable, Container + + define_julia_Container( + mod); // needs: Attributable + + define_julia_WriteIterations(mod); // needs: Iteration + + // The main class + define_julia_Series(mod); + + // Handle metadata + define_julia_version(mod); +} diff --git a/src/binding/julia/shared_ptr.cpp b/src/binding/julia/shared_ptr.cpp new file mode 100644 index 0000000000..a2a2d240af --- /dev/null +++ b/src/binding/julia/shared_ptr.cpp @@ -0,0 +1,29 @@ +/* Bindings for std::shared_ptr + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" +#include "openPMD/Datatype.hpp" +#include + +namespace +{ +struct method_create_aliasing_shared_ptr +{ + template + void call(jlcxx::Module &mod) const + { + mod.method( + "create_aliasing_shared_ptr_" + + datatypeToString(determineDatatype()), + &create_aliasing_shared_ptr); + } +}; +} // namespace + +void define_julia_shared_ptr(jlcxx::Module &mod) +{ + forallScalarJuliaTypes(method_create_aliasing_shared_ptr(), mod); +} diff --git a/src/binding/julia/version.cpp b/src/binding/julia/version.cpp new file mode 100644 index 0000000000..3ac339e40d --- /dev/null +++ b/src/binding/julia/version.cpp @@ -0,0 +1,17 @@ +/* Bindings for version + * + * File authors: Erik Schnetter + * License: LGPL-3.0-or-later + */ + +#include "defs.hpp" + +void define_julia_version(jlcxx::Module &mod) +{ + mod.method("get_version", getVersion); + mod.method("get_standard", getStandard); + mod.method("get_standard_minimum", getStandardMinimum); + mod.method( + "cxx_get_variants", []() { return map_to_vector_pair(getVariants()); }); + mod.method("get_file_extensions", getFileExtensions); +} diff --git a/src/binding/python/Series.cpp b/src/binding/python/Series.cpp index 241e14d69f..9b2af8fe99 100644 --- a/src/binding/python/Series.cpp +++ b/src/binding/python/Series.cpp @@ -18,14 +18,18 @@ * and the GNU Lesser General Public License along with openPMD-api. * If not, see . */ -#include "openPMD/Series.hpp" + +#include +#include +#include +#include + #include "openPMD/IO/Access.hpp" #include "openPMD/IterationEncoding.hpp" +#include "openPMD/Series.hpp" #include "openPMD/auxiliary/JSON.hpp" #include "openPMD/config.hpp" -#include "openPMD/binding/python/Common.hpp" - #if openPMD_HAVE_MPI // re-implemented signatures: // include @@ -35,6 +39,9 @@ #include #include +namespace py = pybind11; +using namespace openPMD; + #if openPMD_HAVE_MPI /** mpi4py communicator wrapper * diff --git a/test/julia/lowlevel_test.jl b/test/julia/lowlevel_test.jl new file mode 100644 index 0000000000..65348a0d39 --- /dev/null +++ b/test/julia/lowlevel_test.jl @@ -0,0 +1,37 @@ +import Base + +# pass as first argument the path to libopenPMD.jl.so but without the ".so" suffix + +module openPMD +using CxxWrap +dlext = Sys.isapple() ? "dylib" : "so" +libname = "$(ARGS[1]).$dlext" +@wrapmodule libname + +function __init__() + @initcxx +end +end + +s = openPMD.CXX_Series( + "../samples/lowlevel_julia_test.json", openPMD.ACCESS_CREATE) + +function Base.getindex( + cont::Cont, + index, +) where +{ + A, + B, + Cont<:openPMD.CXX_Container{A,B} +} + return openPMD.cxx_getindex(cont, index) +end + +iteration = openPMD.cxx_iterations(s)[100] +mesh = openPMD.cxx_meshes(iteration) +# empty brackets to dereference CxxRef +# automatic dereferencing works only for Int types since CxxRwap.jl defines +# Base.getindex(x::CxxBaseRef, i::Int) = Base.getindex(x[], i) +mesh[]["E"][]["x"] +openPMD.cxx_flush(s, "{}")