diff --git a/.github/workflows/python-api-test.yml b/.github/workflows/python-api-test.yml new file mode 100644 index 000000000..2d2d645b0 --- /dev/null +++ b/.github/workflows/python-api-test.yml @@ -0,0 +1,103 @@ +name: Build and Test PUMI Python Interface + +on: + push: + branches: [develop] + pull_request: + branches: [develop] + +jobs: + build-and-test: + runs-on: ubuntu-latest + + env: + PYTHONPATH: ${{ github.workspace }}/build/python_wrappers + LD_LIBRARY_PATH: ${{ github.workspace }}/libs/install/lib:${LD_LIBRARY_PATH} + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y cmake mpich build-essential libbz2-dev + pip3 install mpi4py + + - name: Build SWIG + run: | + git clone https://github.com/swig/swig.git + cd swig + ./autogen.sh + ./configure --prefix=${{ github.workspace }}/libs/install + make + make install + + - name: Build GKlib + run: | + git clone https://github.com/KarypisLab/GKlib.git + cd GKlib + make config prefix=${{ github.workspace }}/libs/install + make install + + - name: Build METIS + run: | + git clone https://github.com/KarypisLab/METIS.git + cd METIS + make config prefix=${{ github.workspace }}/libs/install + make install + + - name: Build ParMETIS + run: | + git clone https://github.com/KarypisLab/ParMETIS.git + cd ParMETIS + make config prefix=${{ github.workspace }}/libs/install + make install + + - name: Build Zoltan from Trilinos (minimal) + run: | + git clone https://github.com/trilinos/Trilinos.git + mkdir build-zoltan + cd build-zoltan + cmake ../Trilinos \ + -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/libs/install \ + -DTPL_ENABLE_MPI:BOOL=ON \ + -DCMAKE_C_FLAGS="-O3 -fPIC" \ + -DCMAKE_CXX_FLAGS="-O3 -fPIC" \ + -DTrilinos_ENABLE_ALL_PACKAGES:BOOL=OFF \ + -DTrilinos_ENABLE_Zoltan:BOOL=ON \ + -DZoltan_ENABLE_EXAMPLES:BOOL=OFF \ + -DZoltan_ENABLE_TESTS:BOOL=OFF \ + -DZoltan_ENABLE_ParMETIS:BOOL=ON \ + -DParMETIS_INCLUDE_DIRS=${{ github.workspace }}/libs/install \ + -DParMETIS_LIBRARY_DIRS=${{ github.workspace }}/libs/install + make + make install + + - name: Configure PUMI + run: | + cmake -S . -B build \ + -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build/install \ + -DCMAKE_C_COMPILER=mpicc \ + -DCMAKE_CXX_COMPILER=mpicxx \ + -DSCOREC_CXX_OPTIMIZE=ON \ + -DMDS_ID_TYPE=int \ + -DPCU_COMPRESS=ON \ + -DENABLE_ZOLTAN=ON \ + -DZOLTAN_PREFIX=${{ github.workspace }}/libs/install \ + -DPUMI_PYTHON_INTERFACE=ON \ + -DBUILD_SHARED_LIBS=ON \ + -DBUILD_EXES=OFF \ + -DIS_TESTING=OFF \ + -DENABLE_OMEGA_H=OFF \ + -DPARMETIS_PREFIX=${{ github.workspace }}/libs/install \ + -DENABLE_SIMMETRIX=OFF + + - name: Build and install + run: | + cmake --build build -j 4 --target install + + - name: Run Python test + working-directory: ${{github.workspace}}/python_wrappers + run: | + python3 test_pyCore.py -g input/cube.dmg -m input/cube.smb diff --git a/pcu/PCU.h b/pcu/PCU.h index 7b588758c..ae8e18b15 100644 --- a/pcu/PCU.h +++ b/pcu/PCU.h @@ -110,8 +110,15 @@ class PCU { int Received(size_t *size) noexcept; void *Extract(size_t size) noexcept; + /* + * Debug print function with printf-style formatting + * This function is excluded from SWIG parsing due to complex macro usage + * with variadic argument. Also, such functionalities is not needed for the Python API. + */ + #ifndef SWIG void DebugPrint(const char* format, ...) noexcept PCU_FORMAT_ATTRIBUTE(2, 3) void DebugPrint(const char* format, va_list args) noexcept; + #endif // SWIG /* Debug functions */ void DebugOpen() noexcept; @@ -141,7 +148,12 @@ void Init(int *argc, char ***argv); */ void Finalize(); -/* explicit instantiations of template functions */ +/* + * Explicit instantiations of template functions, + * ignored by SWIG to avoid difficulties when parsing macros involving + * template functions. Templates are initialized manually in the interface file. + */ +#ifndef SWIG #define PCU_EXPL_INST_DECL(T) \ extern template void PCU::Add(T * p, size_t n) noexcept; \ extern template T PCU::Add(T p) noexcept; \ @@ -158,6 +170,7 @@ PCU_EXPL_INST_DECL(size_t) PCU_EXPL_INST_DECL(long) PCU_EXPL_INST_DECL(double) #undef PCU_EXPL_INST_DECL +#endif // SWIG } // namespace pcu diff --git a/python_wrappers/apf.i b/python_wrappers/apf.i index 283b32005..c6a500402 100644 --- a/python_wrappers/apf.i +++ b/python_wrappers/apf.i @@ -2,6 +2,8 @@ %{ #include #include +#include +#include #include #include #include @@ -40,14 +42,35 @@ /* PCU RELATED WRAPPERS */ /* ==== FROM PCU.h ====*/ -MPI_Comm PCU_Get_Comm(void); -int PCU_Comm_Init(void); -int PCU_Comm_Free(void); +int PCU_Comm_Init(PCU_t* h); +int PCU_Comm_Free(PCU_t* h); -int PCU_Comm_Self(void); -int PCU_Comm_Peers(void); +int PCU_Comm_Self(PCU_t h); +int PCU_Comm_Peers(PCU_t h); double PCU_Time(void); -bool PCU_Comm_Initialized(void); +bool PCU_Comm_Initialized(PCU_t h); +%include +%include +%template(Add_int) pcu::PCU::Add; +%template(Min_int) pcu::PCU::Min; +%template(Max_int) pcu::PCU::Max; +%template(Exscan_int) pcu::PCU::Exscan; +%template(ALLgather_int) pcu::PCU::Allgather; +%template(Add_size_t) pcu::PCU::Add; +%template(Min_size_t) pcu::PCU::Min; +%template(Max_size_t) pcu::PCU::Max; +%template(Exscan_size_t) pcu::PCU::Exscan; +%template(ALLgather_size_t) pcu::PCU::Allgather; +%template(Add_long) pcu::PCU::Add; +%template(Min_long) pcu::PCU::Min; +%template(Max_long) pcu::PCU::Max; +%template(Exscan_long) pcu::PCU::Exscan; +%template(ALLgather_long) pcu::PCU::Allgather; +%template(Add_double) pcu::PCU::Add; +%template(Min_double) pcu::PCU::Min; +%template(Max_double) pcu::PCU::Max; +%template(Exscan_double) pcu::PCU::Exscan; +%template(ALLgather_double) pcu::PCU::Allgather; /* ==== FROM pcu_util.h ====*/ void PCU_Assert_Fail(const char* msg); @@ -221,7 +244,7 @@ void lion_set_verbosity(int lvl); local_min = val; } self->end(it); - PCU_Min_Doubles(&local_min, 1); + self->getPCU()->Min(&local_min, 1); return local_min; } double getMaxOfScalarField(apf::Field* field) @@ -238,7 +261,7 @@ void lion_set_verbosity(int lvl); local_max = val; } self->end(it); - PCU_Max_Doubles(&local_max, 1); + self->getPCU()->Max(&local_max, 1); return local_max; } bool isBoundingModelRegion(int rtag, int dim, int tag) @@ -262,6 +285,7 @@ void lion_set_verbosity(int lvl); #define __attribute__(x) %ignore apf::fail; +%ignore apf::writeCGNS; %include %include %include @@ -269,9 +293,9 @@ void lion_set_verbosity(int lvl); namespace apf { - apf::Mesh2* makeEmptyMdsMesh(gmi_model* model, int dim, bool isMatched); - apf::Mesh2* loadMdsMesh(const char* modelfile, const char* meshfile); - apf::Mesh2* loadMdsMesh(gmi_model* model, const char* meshfile); + apf::Mesh2* makeEmptyMdsMesh(gmi_model* model, int dim, bool isMatched, pcu::PCU *PCUObj); + apf::Mesh2* loadMdsMesh(const char* modelfile, const char* meshfile, pcu::PCU *PCUObj); + apf::Mesh2* loadMdsMesh(gmi_model* model, const char* meshfile, pcu::PCU *PCUObj); void writeASCIIVtkFiles(const char* prefix, apf::Mesh2* m); /* void writeVtkFiles(const char* prefix, apf::Mesh* m, int cellDim = -1); */ /* void writeVtkFiles(const char* prefix, apf::Mesh* m, */ diff --git a/python_wrappers/input/sphere.smd b/python_wrappers/input/sphere.smd new file mode 100644 index 000000000..71226d363 --- /dev/null +++ b/python_wrappers/input/sphere.smd @@ -0,0 +1,110 @@ +smi 5 0 + smd 33 +simple 33 1 +* +-1 +-0.00050000000000000001 -0.00050000000000000001 -0.00050000000000000001 +0.00050000000000000001 0.00050000000000000001 0.00029999999999999997 +1 1007 9 0 +0 0 0 0 +-0.00050000000000000001 -0.00050000000000000001 -0.00050000000000000001 +0.00050000000000000001 0.00050000000000000001 0.00029999999999999997 +_sim_empty +-1 +0 0 0 0 + +0 +1 2 1 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +2 +1 5 65 -1 -1 +2 5 65 -1 -1 +65 1 9 0 210 0 2 -1 -1 +1 2 +_sim_empty +-1 +1 3.1415926535897931 -0.00040000000000000002 4.8985871965894131e-20 0.00029999999999999997 +1e-08 +1 3 +0 6.2831853071795862 +0 0 0.00029999999999999997 +0.00040000000000000002 6.2831853071795862 +0 0 1 +1 0 0 +1 6 11 1 0 2 +1 65 1 0 2 6 11 0 0 1 +1 65 2 1 11 2 9 0 203 0 +1 1 +0 + + 1 2 +0 + +0 +_sim_empty +-1 +0 0 0 0 0 0 +0 +1 3 +0 0 0 +0 1 0 +1 -0 0 +0 0 -1 +0.00050000000000000001 0 0 6.2831853071795862 +3 6 100 1 0 4 +1 65 1 1 4 6 100 0 0 3 +1 65 2 0 100 2 9 0 203 0 +1 3 +0 + + 1 4 +0 + +0 +_sim_empty +-1 +0 0 0 0 0 0 +0 +1 0 +0 0 0.00029999999999999997 +1 0 0 +0 1 0 +1 1 1 +1 8 1 2 +11 0 100 0 1 3 9 0 205 0 1 +1 +0 +0 +_sim_empty +-1 +0 -1 +3 8 2 2 +11 1 100 1 2 3 8 0 303 0 1 +3 +0 +0 +_sim_empty +-1 +0 +0 -1 +0 -1 +0 -1 +0 -1 +0 -1 + auxmm 3 +0 +0 + +2 +smd 8 +auxmm 1160 + +
+4 +71 file:///lore/yus9/scorec_software/core/python_wrappers/input/sphere.smd24 model.nonmanifold.simple6 # 1 0 0 +0 +0 +
+1179 1215 diff --git a/python_wrappers/test_pyCore.py b/python_wrappers/test_pyCore.py index 8b300abb6..4e1a20908 100644 --- a/python_wrappers/test_pyCore.py +++ b/python_wrappers/test_pyCore.py @@ -23,13 +23,13 @@ def main(argv): print('Mesh file is "', mesh) # PCU initialization - pyCore.PCU_Comm_Init() + PCUObj = pyCore.PCU(MPI.COMM_WORLD) # gmi initialization pyCore.gmi_register_mesh() # load the mesh and model and write the initial mesh to vtk - mesh = pyCore.loadMdsMesh(model, mesh) + mesh = pyCore.loadMdsMesh(model, mesh, PCUObj) pyCore.writeASCIIVtkFiles('before', mesh); # setup uniform refiner and call mesh adapt @@ -40,7 +40,7 @@ def main(argv): pyCore.writeASCIIVtkFiles('after', mesh); # gmi finalization - pyCore.PCU_Comm_Free() + del PCUObj if __name__ == "__main__": main(sys.argv[1:]) diff --git a/python_wrappers/test_pyCore_with_simx.py b/python_wrappers/test_pyCore_with_simx.py index b2314e8bf..c6003bbf0 100644 --- a/python_wrappers/test_pyCore_with_simx.py +++ b/python_wrappers/test_pyCore_with_simx.py @@ -25,7 +25,7 @@ def main(argv): pyCore.lion_set_verbosity(1) # PCU initialization - pyCore.PCU_Comm_Init() + PCUObj = pyCore.PCU(MPI.COMM_WORLD) # SIMX initialization pyCore.start_sim('simlog.txt') @@ -38,7 +38,7 @@ def main(argv): pyCore.gmi_register_sim() # load the mesh and model and write the initial mesh to vtk - mesh = pyCore.loadMdsMesh(model, mesh) + mesh = pyCore.loadMdsMesh(model, mesh, PCUObj) print("num verts in the mesh is ", mesh.count(0)) it = mesh.begin(0) @@ -85,7 +85,7 @@ def main(argv): pyCore.stop_sim() # gmi finalization - pyCore.PCU_Comm_Free() + del PCUObj if __name__ == "__main__": main(sys.argv[1:])