Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Projection-based ROM example #3

Merged
merged 26 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f446135
poisson_prom example initial loading.
dreamer2368 Aug 2, 2023
4b16bdb
utils.Database class from libROM. minor fixes in BasisGenerator.
dreamer2368 Aug 3, 2023
d05d50b
poisson_global_rom example. work in progress.
dreamer2368 Aug 3, 2023
98a1641
poisson_global_rom without timing.
dreamer2368 Aug 3, 2023
04b5512
BasisReader Database::formats fix.
dreamer2368 Aug 3, 2023
edf468d
mfem/Utilities bindings. necessary mfem dependencies are added in CMa…
dreamer2368 Aug 3, 2023
130fdc0
minor fix. PyMFEM parallel version is essential.
dreamer2368 Aug 3, 2023
f8bf1e8
Database.formats fix
dreamer2368 Aug 3, 2023
459af6b
cmake-with-librom: added unit tests.
dreamer2368 Aug 4, 2023
fc6b729
python_utils/cpp_utils: auxiliary functions for c++ side.
dreamer2368 Aug 4, 2023
5de52a8
fix the path to mfem dependencies.
dreamer2368 Aug 4, 2023
af5dfbf
change all input numpy array to be references.
dreamer2368 Aug 4, 2023
2d38e12
cpp_utils::getVectorPointer - get contiguous double pointer from nump…
dreamer2368 Aug 4, 2023
fad8965
binding for serial ComputeCtAB.
dreamer2368 Aug 5, 2023
a730eba
specified namespace CAROM.
dreamer2368 Aug 5, 2023
4be4d41
fixed the binding.
dreamer2368 Aug 5, 2023
fc7ceb0
ComputeCtAB defined in python end.
dreamer2368 Aug 5, 2023
9b20765
minor bug fix
dreamer2368 Aug 5, 2023
ec2b061
add timers.
dreamer2368 Aug 5, 2023
7f36c36
removed mfem bindings and dependencies.
dreamer2368 Aug 6, 2023
7e911fc
python routines are added, with proper __init__.py files.
dreamer2368 Aug 6, 2023
f2d58e8
libROM is now compiled without mfem dependencies. scalapack install i…
dreamer2368 Aug 6, 2023
d91478d
libROM commit update & README.md update.
dreamer2368 Aug 6, 2023
c5bf061
docker back to librom master branch.
dreamer2368 Aug 7, 2023
0aca048
fix __init.py structures.
dreamer2368 Aug 9, 2023
921ad2b
update README.md
dreamer2368 Aug 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,20 @@ jobs:
echo run pyMatrix unit test
pytest test_pyMatrix.py --verbose
mpirun -n 2 pytest test_pyMatrix.py --verbose
echo run pyOptions unit test
pytest test_pyOptions.py --verbose
echo run pySVD unit test
pytest test_pySVD.py --verbose
echo run pyStaticSVD unit test
pytest test_pyStaticSVD.py --verbose
echo run pyIncrementalSVD unit test
pytest test_pyIncrementalSVD.py --verbose
echo run pyBasisGenerator unit test
pytest test_pyBasisGenerator.py --verbose
echo run pyBasisReader unit test
pytest test_pyBasisReader.py --verbose
echo run pyBasisWriter unit test
pytest test_pyBasisWriter.py --verbose
baseline:
runs-on: ubuntu-latest
needs: [docker-base-image]
Expand Down
88 changes: 76 additions & 12 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
cmake_minimum_required(VERSION 3.12)

project(pylibROM)
project(_pylibROM)

set(CMAKE_BUILD_TYPE Debug)
set(PYBIND11_FINDPYTHON ON)

#=================== ScaLAPACK (optional) ==================
option(BUILD_SCALAPACK "Build static ScaLAPACK for libROM" OFF)

if (BUILD_SCALAPACK)
set(WORK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern)
add_custom_command(
OUTPUT SLPK_BUILD
WORKING_DIRECTORY ${WORK_DIR}
COMMAND ${WORK_DIR}/librom_scalapack.sh
COMMENT "Building Static ScaLAPACK..."
)
add_custom_target(RUN_SLPK_BUILD ALL DEPENDS SLPK_BUILD)
endif(BUILD_SCALAPACK)

#=================== libROM ================================

#It is tedious to build libROM. option to not build
set(LIBROM_DIR "" CACHE STRING "absolute path to the pre-installed libROM")
set(BUILD_DEPS OFF)

set(BUILD_LIBROM OFF)
if (NOT LIBROM_DIR)
set(LIBROM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/libROM)
set(LIBROM_SCRIPTS_DIR ${LIBROM_DIR}/scripts)
set(BUILD_DEPS ON)
set(BUILD_LIBROM ON)
endif()

include(ExternalProject)
Expand All @@ -22,7 +37,7 @@ include(CMakePrintHelpers)
cmake_print_variables(LIBROM_DIR)
cmake_print_variables(LIBROM_SCRIPTS_DIR)

if (BUILD_DEPS)
if (BUILD_LIBROM)
# add_custom_command(
# OUTPUT LIBROM_BUILD
# WORKING_DIRECTORY ${LIBROM_DIR}
Expand All @@ -41,7 +56,7 @@ if (BUILD_DEPS)
INSTALL_COMMAND ""
)
message("Building libROM dependency...")
endif(BUILD_DEPS)
endif(BUILD_LIBROM)

#setup external dependency build and link paths for libROM
set(LIBROM_INCLUDE_DIR ${LIBROM_DIR}/lib)
Expand All @@ -50,6 +65,36 @@ link_directories(${LIBROM_DIR}/build/lib) #this hack is the best way for non-cma
#include mpi4py directory
execute_process(COMMAND python3 -c "import mpi4py; print(mpi4py.get_include())" OUTPUT_VARIABLE MPI4PY)

# # MFEM is required.
# # TODO(kevin): We do not bind mfem-related functions until we figure out how to type-cast SWIG Object.
# # Until then, mfem-related functions need to be re-implemented on python-end, using PyMFEM.

# find_library(MFEM mfem
# "$ENV{MFEM_DIR}/lib"
# "$ENV{MFEM_DIR}"
# "${LIBROM_DIR}/dependencies/mfem")
# find_library(HYPRE HYPRE
# "$ENV{HYPRE_DIR}/lib"
# "${LIBROM_DIR}/dependencies/hypre/src/hypre/lib")
# find_library(PARMETIS parmetis
# "$ENV{PARMETIS_DIR}/lib"
# "$ENV{PARMETIS_DIR}/build/lib/libparmetis"
# "${LIBROM_DIR}/dependencies/parmetis-4.0.3/build/lib/libparmetis")
# find_library(METIS metis
# "$ENV{METIS_DIR}/lib"
# "$ENV{PARMETIS_DIR}/build/lib/libmetis"
# "${LIBROM_DIR}/dependencies/parmetis-4.0.3/build/lib/libmetis")
# find_path(MFEM_INCLUDES mfem.hpp
# "$ENV{MFEM_DIR}/include"
# "$ENV{MFEM_DIR}"
# "${LIBROM_DIR}/dependencies/mfem")
# find_path(HYPRE_INCLUDES HYPRE.h
# "$ENV{HYPRE_DIR}/include"
# "${LIBROM_DIR}/dependencies/hypre/src/hypre/include")
# find_path(PARMETIS_INCLUDES metis.h
# "$ENV{PARMETIS_DIR}/metis/include"
# "${LIBROM_DIR}/dependencies/parmetis-4.0.3/metis/include")

#===================== pylibROM =============================


Expand All @@ -58,14 +103,26 @@ set(CMAKE_CXX_STANDARD 14)
find_package(MPI REQUIRED)

set(SOURCE_DIR "bindings/pylibROM")
include_directories( ${SOURCE_DIR}
${LIBROM_INCLUDE_DIR}
${MPI_INCLUDE_PATH}
${MPI4PY})
add_subdirectory("extern/pybind11")
include_directories(
${SOURCE_DIR}
${LIBROM_INCLUDE_DIR}
${MPI_INCLUDE_PATH}
${MPI4PY}
# ${MFEM_INCLUDES}
# ${HYPRE_INCLUDES}
# ${PARMETIS_INCLUDES}
# ${MFEM_C_INCLUDE_DIRS}
)
# link_libraries(
# ${MFEM}
# ${HYPRE}
# ${PARMETIS}
# ${METIS}
# )

add_subdirectory("extern/pybind11")

pybind11_add_module(pylibROM
pybind11_add_module(_pylibROM
bindings/pylibROM/pylibROM.cpp

bindings/pylibROM/linalg/pyMatrix.cpp
Expand All @@ -79,7 +136,14 @@ pybind11_add_module(pylibROM
bindings/pylibROM/linalg/svd/pyIncrementalSVD.cpp
bindings/pylibROM/algo/pyDMD.cpp
bindings/pylibROM/utils/mpi_utils.cpp
bindings/pylibROM/utils/pyDatabase.cpp

# TODO(kevin): We do not bind mfem-related functions until we figure out how to type-cast SWIG Object.
# Until then, mfem-related functions need to be re-implemented on python-end, using PyMFEM.
# bindings/pylibROM/mfem/Utilities.cpp

bindings/pylibROM/python_utils/cpp_utils.hpp
)
message("building pylibROM...")

target_link_libraries(pylibROM PRIVATE ROM)
target_link_libraries(_pylibROM PRIVATE ROM)
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Python Interface for LLNL libROM

1. Pull repository and all sub-module dependencies:
```
git clone --recurse-submodules https://github.com/sullan2/pylibROM.git
git clone --recurse-submodules https://github.com/llnl/pylibROM.git
```

2. Compile and build pylibROM (from top-level pylibROM repo):
Expand All @@ -16,11 +16,15 @@ Python Interface for LLNL libROM
```
pip install ./ --global-option="--librom_dir=/path/to/pre-installed-libROM"
```
If you want to build static ScaLAPACK for libROM,
```
pip install ./ --global-option="--install_scalapack"
```

3. Test python package (from top-level pylibROM repo):
```
cd tests
python3.6 testVector.py
pytest test_pyVector.py
```

### Using PyMFEM
Expand Down
8 changes: 5 additions & 3 deletions bindings/pylibROM/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from . import linalg
from . import algo
from . import utils
# To add pure python routines to this module,
# either define/import the python routine in this file.
# This will combine both c++ bindings/pure python routines into this module.

from _pylibROM import *
7 changes: 6 additions & 1 deletion bindings/pylibROM/algo/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
from .pyDMD import DMD
# To add pure python routines to this module,
# either define/import the python routine in this file.
# This will combine both c++ bindings/pure python routines into this module.

# For other c++ binding modules, change the module name accordingly.
from _pylibROM.algo import *
9 changes: 4 additions & 5 deletions bindings/pylibROM/algo/pyDMD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <pybind11/stl.h>
#include "algo/DMD.h"
#include "linalg/Vector.h"
#include "python_utils/cpp_utils.hpp"

namespace py = pybind11;
using namespace CAROM;
Expand Down Expand Up @@ -53,11 +54,9 @@ void init_DMD(pybind11::module_ &m) {
}), py::arg("dim"), py::arg("dt"), py::arg("alt_output_basis") = false, py::arg("vec") = nullptr)

// .def("setOffset", &PyDMD::setOffset, py::arg("offset_vector"), py::arg("order")) //problem if we want to name the wrapper as DMD. Could get rid of the using namespace directive?
.def("takeSample", [](DMD &self, py::array_t<double> u_in, double t) {
py::buffer_info buf_info = u_in.request();
double* data = static_cast<double*>(buf_info.ptr);
self.takeSample(data, t);
})
.def("takeSample", [](DMD &self, py::array_t<double> &u_in, double t) {
self.takeSample(getVectorPointer(u_in), t);
})
.def("train", py::overload_cast<double, const Matrix*, double>(&DMD::train),
py::arg("energy_fraction"), py::arg("W0") = nullptr, py::arg("linearity_tol") = 0.0)
.def("train", py::overload_cast<int, const Matrix*, double>(&DMD::train),
Expand Down
13 changes: 6 additions & 7 deletions bindings/pylibROM/linalg/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from .pyVector import Vector
from .pyMatrix import Matrix
from .pyBasisWriter import BasisWriter
from .pyBasisReader import BasisReader
from .pyBasisGenerator import BasisGenerator
from .pyOptions import Options
from . import svd
# To add pure python routines to this module,
# either define/import the python routine in this file.
# This will combine both c++ bindings/pure python routines into this module.

# For other c++ binding modules, change the module name accordingly.
from _pylibROM.linalg import *
40 changes: 17 additions & 23 deletions bindings/pylibROM/linalg/pyBasisGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,36 @@
#include <pybind11/operators.h>
#include <pybind11/stl.h>
#include "linalg/BasisGenerator.h"

#include "python_utils/cpp_utils.hpp"

namespace py = pybind11;
using namespace CAROM;

void init_BasisGenerator(pybind11::module_ &m) {
py::enum_<Database::formats>(m, "Formats")
.value("HDF5", Database::formats::HDF5);

py::class_<BasisGenerator>(m, "BasisGenerator")
// .def(py::init<Options, bool, const std::string&, Database::formats>())
.def(py::init<const Options&, bool, const std::string&, Database::formats>(),py::arg("options"),py::arg("incremental"),py::arg("basis_file_name") = "",py::arg("file_format") = static_cast<int>(Database::formats::HDF5))
.def(py::init<const Options&, bool, const std::string&, Database::formats>(),
py::arg("options"),
py::arg("incremental"),
py::arg("basis_file_name") = "",
py::arg("file_format") = Database::formats::HDF5
)
.def("isNextSample", (bool (BasisGenerator::*)(double)) &BasisGenerator::isNextSample)
.def("updateRightSV", (bool (BasisGenerator::*)()) &BasisGenerator::updateRightSV)
.def("takeSample", [](BasisGenerator& self, py::array_t<double> u_in, double time, double dt, bool add_without_increase = false) {
py::buffer_info buf_info = u_in.request();
if (buf_info.ndim != 1)
throw std::runtime_error("Input array must be 1-dimensional");

double* u_in_data = static_cast<double*>(buf_info.ptr);
return self.takeSample(u_in_data, time, dt, add_without_increase);
.def("takeSample", [](BasisGenerator& self, py::array_t<double> &u_in, double time, double dt, bool add_without_increase = false) {
return self.takeSample(getVectorPointer(u_in), time, dt, add_without_increase);
}, py::arg("u_in"), py::arg("time"), py::arg("dt"), py::arg("add_without_increase") = false)
.def("endSamples", &BasisGenerator::endSamples, py::arg("kind") = "basis")
.def("writeSnapshot", (void (BasisGenerator::*)()) &BasisGenerator::writeSnapshot)
.def("loadSamples", &BasisGenerator::loadSamples, py::arg("base_file_name"), py::arg("kind") = "basis", py::arg("cut_off") = 1e9, py::arg("db_format") = static_cast<int>(Database::formats::HDF5))
.def("computeNextSampleTime", [](BasisGenerator& self, py::array_t<double> u_in, py::array_t<double> rhs_in, double time) {
py::buffer_info buf_info_u = u_in.request();
py::buffer_info buf_info_rhs = rhs_in.request();

if (buf_info_u.ndim != 1 || buf_info_rhs.ndim != 1)
throw std::runtime_error("Input arrays must be 1-dimensional");

double* u_in_data = static_cast<double*>(buf_info_u.ptr);
double* rhs_in_data = static_cast<double*>(buf_info_rhs.ptr);

return self.computeNextSampleTime(u_in_data, rhs_in_data, time);
.def("loadSamples", (void (BasisGenerator::*)(const std::string&, const std::string&, int, Database::formats)) &BasisGenerator::loadSamples,
py::arg("base_file_name"),
py::arg("kind") = "basis",
py::arg("cut_off") = static_cast<int>(1e9),
py::arg("db_format") = Database::formats::HDF5
)
.def("computeNextSampleTime", [](BasisGenerator& self, py::array_t<double> &u_in, py::array_t<double> &rhs_in, double time) {
return self.computeNextSampleTime(getVectorPointer(u_in), getVectorPointer(rhs_in), time);
}, py::arg("u_in"), py::arg("rhs_in"), py::arg("time"))

.def("getSpatialBasis", (const Matrix* (BasisGenerator::*)()) &BasisGenerator::getSpatialBasis,py::return_value_policy::reference)
Expand Down
7 changes: 5 additions & 2 deletions bindings/pylibROM/linalg/pyBasisReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ using namespace CAROM;

void init_BasisReader(pybind11::module_ &m) {
py::class_<BasisReader>(m, "BasisReader")
.def(py::init<const std::string&, Database::formats>(),py::arg("base_file_name"),py::arg("file_format") = static_cast<int>(Database::formats::HDF5))
.def(py::init<const std::string&, Database::formats>(),
py::arg("base_file_name"),
py::arg("file_format") = Database::formats::HDF5
)
.def("isNewBasis",(bool (BasisReader::*)(double)) &BasisReader::isNewBasis)
.def("getSpatialBasis",(Matrix* (BasisReader::*)(double)) &BasisReader::getSpatialBasis)
.def("getSpatialBasis",(Matrix* (BasisReader::*)(double,int)) &BasisReader::getSpatialBasis)
Expand All @@ -30,4 +33,4 @@ void init_BasisReader(pybind11::module_ &m) {
.def("getSnapshotMatrix",(Matrix* (BasisReader::*)(double,int)) &BasisReader::getSnapshotMatrix)
.def("getSnapshotMatrix",(Matrix* (BasisReader::*)(double,int,int)) &BasisReader::getSnapshotMatrix)
.def("__del__", [](BasisReader& self) { self.~BasisReader(); }); // Destructor
}
}
2 changes: 1 addition & 1 deletion bindings/pylibROM/linalg/pyMatrix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ void init_matrix(pybind11::module_ &m) {
.def(py::init<>())
.def(py::init<int, int, bool, bool>(),
py::arg("num_rows"), py::arg("num_cols"), py::arg("distributed"), py::arg("randomized") = false)
.def(py::init([](py::array_t<double> mat, bool distributed, bool copy_data = true) {
.def(py::init([](py::array_t<double> &mat, bool distributed, bool copy_data = true) {
py::buffer_info buf_info = mat.request();
int num_rows = buf_info.shape[0];
int num_cols = buf_info.shape[1];
Expand Down
2 changes: 1 addition & 1 deletion bindings/pylibROM/linalg/pyVector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void init_vector(pybind11::module_ &m) {
.def(py::init<int, bool>())

// Constructor
.def(py::init([](py::array_t<double> vec, bool distributed, bool copy_data = true) {
.def(py::init([](py::array_t<double> &vec, bool distributed, bool copy_data = true) {
py::buffer_info buf_info = vec.request();
int dim = buf_info.shape[0];
double* data = static_cast<double*>(buf_info.ptr);
Expand Down
9 changes: 6 additions & 3 deletions bindings/pylibROM/linalg/svd/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from .pySVD import SVD
from .pyStaticSVD import StaticSVD
from .pyIncrementalSVD import IncrementalSVD
# To add pure python routines to this module,
# either define/import the python routine in this file.
# This will combine both c++ bindings/pure python routines into this module.

# For other c++ binding modules, change the module name accordingly.
from _pylibROM.linalg.svd import *
Loading