From 92e94fbef8a7a8ade03aab4f33d58211ff543c26 Mon Sep 17 00:00:00 2001 From: Sebastian Beyer Date: Mon, 10 Jun 2024 22:53:23 +0300 Subject: [PATCH 1/4] Remove PARMS solver This removes the most obvious traces and builds, no real test run done yet --- dwarf/dwarf_ice/dwarf_ini/CMakeLists.txt | 7 +- dwarf/dwarf_tracer/dwarf_ini/CMakeLists.txt | 7 +- lib/parms/CMakeLists.txt | 49 - lib/parms/COPYRIGHT | 28 - lib/parms/LGPL | 504 - lib/parms/Makefile | 74 - lib/parms/Makefile_ollie | 73 - lib/parms/README | 68 - lib/parms/README_INSTALL | 86 - lib/parms/conf/makefile.altix | 54 - lib/parms/conf/makefile.bigben | 49 - lib/parms/conf/makefile.blade_ompi_intel | 58 - lib/parms/conf/makefile.calhoun_ompi_gnu | 57 - lib/parms/conf/makefile.calhoun_ompi_intel | 58 - lib/parms/conf/makefile.itasca_ompi_intel | 58 - lib/parms/conf/makefile.laptop | 49 - lib/parms/conf/makefile.linux.gcc | 49 - lib/parms/conf/makefile.linux.gcc3 | 49 - lib/parms/conf/makefile.linux.icc | 53 - lib/parms/conf/makefile.linux.pathscale | 53 - lib/parms/conf/makefile.linux.sun | 49 - lib/parms/conf/makefile.o38k | 49 - lib/parms/conf/makefile.regatta | 51 - lib/parms/conf/makefile.seaborg | 54 - lib/parms/docs/README | 109 - lib/parms/docs/refmanual.pdf | Bin 222963 -> 0 bytes lib/parms/docs/ug.pdf | Bin 209685 -> 0 bytes lib/parms/examples/DOTESTS | 249 - lib/parms/examples/README | 19 - lib/parms/examples/README-DOTESTS | 37 - lib/parms/examples/general/README | 132 - lib/parms/examples/general/aux.c | 300 - lib/parms/examples/general/aux.h | 125 - lib/parms/examples/general/dd-HB-dse.c | 437 - .../general/extras/dd-HB-dse-altix.pbs | 6 - .../examples/general/extras/dd-HB-dse-pwr.cmd | 15 - lib/parms/examples/general/extras/procfile | 7 - lib/parms/examples/general/faux.h | 5 - lib/parms/examples/general/fdd-HB-dse.F | 358 - lib/parms/examples/general/inputs | 30 - .../examples/general/inputs_explanations | 87 - lib/parms/examples/general/makefile | 72 - lib/parms/examples/general/matfileCmplx | 7 - lib/parms/examples/general/matfileReal | 8 - lib/parms/examples/grid/README | 192 - lib/parms/examples/grid/aux.c | 211 - lib/parms/examples/grid/aux.h | 85 - lib/parms/examples/grid/dd-grid.c | 300 - .../examples/grid/extras/dd-grid-altix.pbs | 6 - .../examples/grid/extras/dd-grid-pwr.cmd | 15 - lib/parms/examples/grid/extras/info | 8 - lib/parms/examples/grid/extras/procfile | 7 - lib/parms/examples/grid/faux.h | 5 - lib/parms/examples/grid/fdd-grid.F | 214 - lib/parms/examples/grid/fdmat.f | 633 - lib/parms/examples/grid/functs.f | 343 - lib/parms/examples/grid/inputs | 30 - lib/parms/examples/grid/makefile | 51 - lib/parms/examples/grid/skit.f | 136 - lib/parms/examples/matrices/SAYLR4 | 7057 ------ lib/parms/examples/matrices/SHERMAN3 | 7518 ------ lib/parms/examples/matrices/SHERMAN5 | 7239 ------ lib/parms/examples/matrices/dwg961b.cua | 4424 ---- lib/parms/examples/matrices/qc324.cua | 19516 ---------------- lib/parms/examples/petsc/README | 77 - .../petsc/conf/makefile.blade_intel_ompi | 66 - .../examples/petsc/conf/makefile.in_laptop | 65 - .../examples/petsc/conf/makefile.itasca_impi | 66 - lib/parms/examples/petsc/dd-petsc.c | 322 - lib/parms/examples/petsc/fparms.c | 79 - lib/parms/examples/petsc/ftest.F | 263 - lib/parms/examples/petsc/makefile | 31 - lib/parms/examples/petsc/makefile.in | 35 - lib/parms/examples/petsc/matfileCmplx | 7 - lib/parms/examples/petsc/matfileReal | 8 - lib/parms/examples/petsc/parms.c | 482 - lib/parms/examples/petsc/parms_opts | 26 - lib/parms/examples/petsc/protos.h | 113 - lib/parms/examples/petsc/readmat.c | 38 - lib/parms/examples/petsc/test.c | 214 - lib/parms/include/fparms.h | 33 - lib/parms/include/parms.h | 23 - lib/parms/include/parms_comm.h | 111 - lib/parms/include/parms_map.h | 262 - lib/parms/include/parms_mat.h | 337 - lib/parms/include/parms_mem.h | 82 - lib/parms/include/parms_operator.h | 167 - lib/parms/include/parms_pc.h | 392 - lib/parms/include/parms_solver.h | 194 - lib/parms/include/parms_sys.h | 196 - lib/parms/include/parms_sys_cmplx.h | 172 - lib/parms/include/parms_sys_dbl.h | 148 - lib/parms/include/parms_table.h | 99 - lib/parms/include/parms_timer.h | 125 - lib/parms/include/parms_vec.h | 289 - lib/parms/include/parms_viewer.h | 100 - lib/parms/src/DDPQ/MatOps.c | 732 - lib/parms/src/DDPQ/PQ.c | 448 - lib/parms/src/DDPQ/arms2.c | 690 - lib/parms/src/DDPQ/globheads.h | 151 - lib/parms/src/DDPQ/ilutpC.c | 753 - lib/parms/src/DDPQ/misc.c | 485 - lib/parms/src/DDPQ/piluNEW.c | 637 - lib/parms/src/DDPQ/protos.h | 133 - lib/parms/src/DDPQ/setblks.c | 245 - lib/parms/src/DDPQ/sets.c | 811 - lib/parms/src/DDPQ/svdInvC.c | 125 - lib/parms/src/DDPQ/systimer.c | 9 - lib/parms/src/FORTRAN/README | 25 - lib/parms/src/FORTRAN/parms_mapf.c | 114 - lib/parms/src/FORTRAN/parms_matf.c | 119 - lib/parms/src/FORTRAN/parms_pcf.c | 217 - lib/parms/src/FORTRAN/parms_solverf.c | 105 - lib/parms/src/FORTRAN/parms_timerf.c | 74 - lib/parms/src/FORTRAN/parms_vecf.c | 134 - lib/parms/src/FORTRAN/parms_viewerf.c | 55 - lib/parms/src/bicgstab.c | 313 - lib/parms/src/bicgstab_ras.c | 360 - lib/parms/src/cg.c | 256 - lib/parms/src/fgmres.c | 423 - lib/parms/src/gmres.c | 425 - lib/parms/src/include/parms_comm_impl.h | 77 - lib/parms/src/include/parms_map_impl.h | 87 - lib/parms/src/include/parms_mat_impl.h | 154 - lib/parms/src/include/parms_opt_impl.h | 38 - lib/parms/src/include/parms_pc_impl.h | 48 - lib/parms/src/include/parms_solver_impl.h | 55 - lib/parms/src/include/parms_table_impl.h | 37 - lib/parms/src/include/parms_timer_impl.h | 31 - lib/parms/src/include/parms_viewer_impl.h | 26 - lib/parms/src/parms_comm.c | 402 - lib/parms/src/parms_complex.c | 104 - lib/parms/src/parms_ilu_vcsr.c | 1747 -- lib/parms/src/parms_map.c | 759 - lib/parms/src/parms_mat.c | 1413 -- lib/parms/src/parms_mat_dvcsr.c | 1443 -- lib/parms/src/parms_mat_vcsr.c | 213 - lib/parms/src/parms_mem.c | 178 - lib/parms/src/parms_operator.c | 90 - lib/parms/src/parms_pc.c | 683 - lib/parms/src/parms_pc_bj.c | 143 - lib/parms/src/parms_pc_ras.c | 226 - lib/parms/src/parms_pc_schur.c | 552 - lib/parms/src/parms_pc_schurras.c | 469 - lib/parms/src/parms_qsplit.c | 55 - lib/parms/src/parms_solver.c | 295 - lib/parms/src/parms_table.c | 283 - lib/parms/src/parms_timer.c | 199 - lib/parms/src/parms_vec.c | 805 - lib/parms/src/parms_viewer.c | 148 - lib/parms/src/pbicgstab.c | 326 - lib/parms/src/pbicgstab_ras.c | 381 - mesh_part/CMakeLists.txt | 2 +- src/CMakeLists.txt | 19 +- src/MOD_DYN.F90 | 9 +- src/info_module.F90 | 5 - src/oce_ale.F90 | 81 +- src/psolve.c | 263 - src/psolve.h | 27 - 159 files changed, 14 insertions(+), 75999 deletions(-) delete mode 100644 lib/parms/CMakeLists.txt delete mode 100755 lib/parms/COPYRIGHT delete mode 100755 lib/parms/LGPL delete mode 100644 lib/parms/Makefile delete mode 100755 lib/parms/Makefile_ollie delete mode 100755 lib/parms/README delete mode 100755 lib/parms/README_INSTALL delete mode 100755 lib/parms/conf/makefile.altix delete mode 100755 lib/parms/conf/makefile.bigben delete mode 100755 lib/parms/conf/makefile.blade_ompi_intel delete mode 100755 lib/parms/conf/makefile.calhoun_ompi_gnu delete mode 100755 lib/parms/conf/makefile.calhoun_ompi_intel delete mode 100755 lib/parms/conf/makefile.itasca_ompi_intel delete mode 100755 lib/parms/conf/makefile.laptop delete mode 100755 lib/parms/conf/makefile.linux.gcc delete mode 100755 lib/parms/conf/makefile.linux.gcc3 delete mode 100755 lib/parms/conf/makefile.linux.icc delete mode 100755 lib/parms/conf/makefile.linux.pathscale delete mode 100755 lib/parms/conf/makefile.linux.sun delete mode 100755 lib/parms/conf/makefile.o38k delete mode 100755 lib/parms/conf/makefile.regatta delete mode 100755 lib/parms/conf/makefile.seaborg delete mode 100755 lib/parms/docs/README delete mode 100755 lib/parms/docs/refmanual.pdf delete mode 100755 lib/parms/docs/ug.pdf delete mode 100755 lib/parms/examples/DOTESTS delete mode 100755 lib/parms/examples/README delete mode 100755 lib/parms/examples/README-DOTESTS delete mode 100755 lib/parms/examples/general/README delete mode 100755 lib/parms/examples/general/aux.c delete mode 100755 lib/parms/examples/general/aux.h delete mode 100755 lib/parms/examples/general/dd-HB-dse.c delete mode 100755 lib/parms/examples/general/extras/dd-HB-dse-altix.pbs delete mode 100755 lib/parms/examples/general/extras/dd-HB-dse-pwr.cmd delete mode 100755 lib/parms/examples/general/extras/procfile delete mode 100755 lib/parms/examples/general/faux.h delete mode 100755 lib/parms/examples/general/fdd-HB-dse.F delete mode 100755 lib/parms/examples/general/inputs delete mode 100755 lib/parms/examples/general/inputs_explanations delete mode 100755 lib/parms/examples/general/makefile delete mode 100755 lib/parms/examples/general/matfileCmplx delete mode 100755 lib/parms/examples/general/matfileReal delete mode 100755 lib/parms/examples/grid/README delete mode 100755 lib/parms/examples/grid/aux.c delete mode 100755 lib/parms/examples/grid/aux.h delete mode 100755 lib/parms/examples/grid/dd-grid.c delete mode 100755 lib/parms/examples/grid/extras/dd-grid-altix.pbs delete mode 100755 lib/parms/examples/grid/extras/dd-grid-pwr.cmd delete mode 100755 lib/parms/examples/grid/extras/info delete mode 100755 lib/parms/examples/grid/extras/procfile delete mode 100755 lib/parms/examples/grid/faux.h delete mode 100755 lib/parms/examples/grid/fdd-grid.F delete mode 100755 lib/parms/examples/grid/fdmat.f delete mode 100755 lib/parms/examples/grid/functs.f delete mode 100755 lib/parms/examples/grid/inputs delete mode 100755 lib/parms/examples/grid/makefile delete mode 100755 lib/parms/examples/grid/skit.f delete mode 100755 lib/parms/examples/matrices/SAYLR4 delete mode 100755 lib/parms/examples/matrices/SHERMAN3 delete mode 100755 lib/parms/examples/matrices/SHERMAN5 delete mode 100755 lib/parms/examples/matrices/dwg961b.cua delete mode 100755 lib/parms/examples/matrices/qc324.cua delete mode 100755 lib/parms/examples/petsc/README delete mode 100755 lib/parms/examples/petsc/conf/makefile.blade_intel_ompi delete mode 100755 lib/parms/examples/petsc/conf/makefile.in_laptop delete mode 100755 lib/parms/examples/petsc/conf/makefile.itasca_impi delete mode 100755 lib/parms/examples/petsc/dd-petsc.c delete mode 100755 lib/parms/examples/petsc/fparms.c delete mode 100755 lib/parms/examples/petsc/ftest.F delete mode 100755 lib/parms/examples/petsc/makefile delete mode 100755 lib/parms/examples/petsc/makefile.in delete mode 100755 lib/parms/examples/petsc/matfileCmplx delete mode 100755 lib/parms/examples/petsc/matfileReal delete mode 100755 lib/parms/examples/petsc/parms.c delete mode 100755 lib/parms/examples/petsc/parms_opts delete mode 100755 lib/parms/examples/petsc/protos.h delete mode 100755 lib/parms/examples/petsc/readmat.c delete mode 100755 lib/parms/examples/petsc/test.c delete mode 100755 lib/parms/include/fparms.h delete mode 100755 lib/parms/include/parms.h delete mode 100755 lib/parms/include/parms_comm.h delete mode 100755 lib/parms/include/parms_map.h delete mode 100755 lib/parms/include/parms_mat.h delete mode 100755 lib/parms/include/parms_mem.h delete mode 100755 lib/parms/include/parms_operator.h delete mode 100755 lib/parms/include/parms_pc.h delete mode 100755 lib/parms/include/parms_solver.h delete mode 100755 lib/parms/include/parms_sys.h delete mode 100755 lib/parms/include/parms_sys_cmplx.h delete mode 100755 lib/parms/include/parms_sys_dbl.h delete mode 100755 lib/parms/include/parms_table.h delete mode 100755 lib/parms/include/parms_timer.h delete mode 100755 lib/parms/include/parms_vec.h delete mode 100755 lib/parms/include/parms_viewer.h delete mode 100755 lib/parms/src/DDPQ/MatOps.c delete mode 100755 lib/parms/src/DDPQ/PQ.c delete mode 100755 lib/parms/src/DDPQ/arms2.c delete mode 100755 lib/parms/src/DDPQ/globheads.h delete mode 100755 lib/parms/src/DDPQ/ilutpC.c delete mode 100755 lib/parms/src/DDPQ/misc.c delete mode 100755 lib/parms/src/DDPQ/piluNEW.c delete mode 100755 lib/parms/src/DDPQ/protos.h delete mode 100755 lib/parms/src/DDPQ/setblks.c delete mode 100755 lib/parms/src/DDPQ/sets.c delete mode 100755 lib/parms/src/DDPQ/svdInvC.c delete mode 100644 lib/parms/src/DDPQ/systimer.c delete mode 100755 lib/parms/src/FORTRAN/README delete mode 100755 lib/parms/src/FORTRAN/parms_mapf.c delete mode 100755 lib/parms/src/FORTRAN/parms_matf.c delete mode 100755 lib/parms/src/FORTRAN/parms_pcf.c delete mode 100755 lib/parms/src/FORTRAN/parms_solverf.c delete mode 100755 lib/parms/src/FORTRAN/parms_timerf.c delete mode 100755 lib/parms/src/FORTRAN/parms_vecf.c delete mode 100755 lib/parms/src/FORTRAN/parms_viewerf.c delete mode 100755 lib/parms/src/bicgstab.c delete mode 100644 lib/parms/src/bicgstab_ras.c delete mode 100644 lib/parms/src/cg.c delete mode 100755 lib/parms/src/fgmres.c delete mode 100755 lib/parms/src/gmres.c delete mode 100755 lib/parms/src/include/parms_comm_impl.h delete mode 100755 lib/parms/src/include/parms_map_impl.h delete mode 100755 lib/parms/src/include/parms_mat_impl.h delete mode 100755 lib/parms/src/include/parms_opt_impl.h delete mode 100755 lib/parms/src/include/parms_pc_impl.h delete mode 100755 lib/parms/src/include/parms_solver_impl.h delete mode 100755 lib/parms/src/include/parms_table_impl.h delete mode 100755 lib/parms/src/include/parms_timer_impl.h delete mode 100755 lib/parms/src/include/parms_viewer_impl.h delete mode 100755 lib/parms/src/parms_comm.c delete mode 100755 lib/parms/src/parms_complex.c delete mode 100755 lib/parms/src/parms_ilu_vcsr.c delete mode 100755 lib/parms/src/parms_map.c delete mode 100755 lib/parms/src/parms_mat.c delete mode 100755 lib/parms/src/parms_mat_dvcsr.c delete mode 100755 lib/parms/src/parms_mat_vcsr.c delete mode 100755 lib/parms/src/parms_mem.c delete mode 100755 lib/parms/src/parms_operator.c delete mode 100755 lib/parms/src/parms_pc.c delete mode 100755 lib/parms/src/parms_pc_bj.c delete mode 100755 lib/parms/src/parms_pc_ras.c delete mode 100755 lib/parms/src/parms_pc_schur.c delete mode 100755 lib/parms/src/parms_pc_schurras.c delete mode 100755 lib/parms/src/parms_qsplit.c delete mode 100755 lib/parms/src/parms_solver.c delete mode 100755 lib/parms/src/parms_table.c delete mode 100755 lib/parms/src/parms_timer.c delete mode 100755 lib/parms/src/parms_vec.c delete mode 100755 lib/parms/src/parms_viewer.c delete mode 100755 lib/parms/src/pbicgstab.c delete mode 100644 lib/parms/src/pbicgstab_ras.c delete mode 100644 src/psolve.c delete mode 100644 src/psolve.h diff --git a/dwarf/dwarf_ice/dwarf_ini/CMakeLists.txt b/dwarf/dwarf_ice/dwarf_ini/CMakeLists.txt index 02176a227..378d926ce 100644 --- a/dwarf/dwarf_ice/dwarf_ini/CMakeLists.txt +++ b/dwarf/dwarf_ice/dwarf_ini/CMakeLists.txt @@ -45,20 +45,17 @@ file(GLOB sources_Fortran ${src_home}/*.F90) # depends on the metis library #add_subdirectory(../lib/metis-5.1.0 ${PROJECT_BINARY_DIR}/metis) #include_directories(../lib/metis-5.1.0/include) -# depends on the parms library -#add_subdirectory(../lib/parms ${PROJECT_BINARY_DIR}/parms) #add_subdirectory(async_threads_cpp) #include(${CMAKE_CURRENT_LIST_DIR}/../cmake/FindNETCDF.cmake) #add_library(${PROJECT_NAME}_C ${sources_C}) -#target_compile_definitions(${PROJECT_NAME}_C PRIVATE PARMS USE_MPI REAL=double DBL HAS_BLAS FORTRAN_UNDERSCORE VOID_POINTER_SIZE_8 SGI LINUX UNDER_ MPI2) -#target_link_libraries(${PROJECT_NAME}_C parms) #metis +#target_compile_definitions(${PROJECT_NAME}_C PRIVATE USE_MPI REAL=double DBL HAS_BLAS FORTRAN_UNDERSCORE VOID_POINTER_SIZE_8 SGI LINUX UNDER_ MPI2) # create our binary (set its name to name of this project) add_executable(${PROJECT_NAME} ${sources_Fortran}) -#target_compile_definitions(${PROJECT_NAME} PRIVATE PARMS -DMETIS_VERSION=5 -DPART_WEIGHTED -DMETISRANDOMSEED=35243) +#target_compile_definitions(${PROJECT_NAME} PRIVATE -DMETIS_VERSION=5 -DPART_WEIGHTED -DMETISRANDOMSEED=35243) #if(${DISABLE_MULTITHREADING}) # target_compile_definitions(${PROJECT_NAME} PRIVATE DISABLE_MULTITHREADING) #endif() diff --git a/dwarf/dwarf_tracer/dwarf_ini/CMakeLists.txt b/dwarf/dwarf_tracer/dwarf_ini/CMakeLists.txt index 4e6d3bbb1..cb3c2715a 100644 --- a/dwarf/dwarf_tracer/dwarf_ini/CMakeLists.txt +++ b/dwarf/dwarf_tracer/dwarf_ini/CMakeLists.txt @@ -45,20 +45,17 @@ file(GLOB sources_Fortran ${src_home}/*.F90) # depends on the metis library #add_subdirectory(../lib/metis-5.1.0 ${PROJECT_BINARY_DIR}/metis) #include_directories(../lib/metis-5.1.0/include) -# depends on the parms library -#add_subdirectory(../lib/parms ${PROJECT_BINARY_DIR}/parms) #add_subdirectory(async_threads_cpp) #include(${CMAKE_CURRENT_LIST_DIR}/../cmake/FindNETCDF.cmake) #add_library(${PROJECT_NAME}_C ${sources_C}) -#target_compile_definitions(${PROJECT_NAME}_C PRIVATE PARMS USE_MPI REAL=double DBL HAS_BLAS FORTRAN_UNDERSCORE VOID_POINTER_SIZE_8 SGI LINUX UNDER_ MPI2) -#target_link_libraries(${PROJECT_NAME}_C parms) #metis +#target_compile_definitions(${PROJECT_NAME}_C PRIVATE USE_MPI REAL=double DBL HAS_BLAS FORTRAN_UNDERSCORE VOID_POINTER_SIZE_8 SGI LINUX UNDER_ MPI2) # create our binary (set its name to name of this project) add_executable(${PROJECT_NAME} ${sources_Fortran}) -#target_compile_definitions(${PROJECT_NAME} PRIVATE PARMS -DMETIS_VERSION=5 -DPART_WEIGHTED -DMETISRANDOMSEED=35243) +#target_compile_definitions(${PROJECT_NAME} PRIVATE -DMETIS_VERSION=5 -DPART_WEIGHTED -DMETISRANDOMSEED=35243) #if(${DISABLE_MULTITHREADING}) # target_compile_definitions(${PROJECT_NAME} PRIVATE DISABLE_MULTITHREADING) #endif() diff --git a/lib/parms/CMakeLists.txt b/lib/parms/CMakeLists.txt deleted file mode 100644 index f81cc26fb..000000000 --- a/lib/parms/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -project(parms C) - -# Set location to look for find_package modules -set( CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../../cmake ) - -# Set -fPIC flag etc. -set( CMAKE_POSITION_INDEPENDENT_CODE ON ) - -# get our source files -set(src_home ${CMAKE_CURRENT_LIST_DIR}) # path to src directory starting from the dir containing our CMakeLists.txt -file(GLOB all_sources ${src_home}/src/*.c ${src_home}/src/DDPQ/*.c) - -if( NOT LAPACK_LIBRARIES ) - # First check if we have the Cray libraries - set( _cray_libsci_loaded $ENV{CRAY_LIBSCI_DIR} ) - if( _cray_libsci_loaded ) - # set( _CRAY_PRGENV $ENV{PE_ENV} ) - # string( TOLOWER "${_CRAY_PRGENV}" _cray_prgenv ) - # set( LAPACK_LIBRARIES sci_${_cray_prgenv} ) - else() - find_package(LAPACK REQUIRED) # cannot compile without! - endif() -endif() -if( NOT TARGET MPI::MPI_C ) - find_package(MPI REQUIRED COMPONENTS C) -endif() - -# create our library (set its name to name of this project) -add_library(${PROJECT_NAME} ${all_sources}) - -target_compile_definitions(${PROJECT_NAME} PRIVATE PARMS USE_MPI REAL=double DBL FORTRAN_UNDERSCORE VOID_POINTER_SIZE_8) - -target_include_directories(${PROJECT_NAME} - PRIVATE ${src_home}/src/include - PUBLIC $ -) - -target_link_libraries(${PROJECT_NAME} PRIVATE ${LAPACK_LIBRARIES}) -target_link_libraries(${PROJECT_NAME} PRIVATE MPI::MPI_C) - -if(${CMAKE_C_COMPILER_ID} STREQUAL "Intel") - target_compile_options(${PROJECT_NAME} PRIVATE -no-prec-div -no-prec-sqrt -fast-transcendentals -fp-model precise) - - if(${FESOM_PLATFORM_STRATEGY} STREQUAL levante.dkrz.de ) - target_compile_options(${PROJECT_NAME} PRIVATE -march=core-avx2 -mtune=core-avx2) - endif() -endif() diff --git a/lib/parms/COPYRIGHT b/lib/parms/COPYRIGHT deleted file mode 100755 index 45d22e1ed..000000000 --- a/lib/parms/COPYRIGHT +++ /dev/null @@ -1,28 +0,0 @@ ------------------------------------------------------------------------ - p A R M S -- V E R S I O N 2.0 ------------------------------------------------------------------------ - -Copyright (C) 2004, the Regents of the University of Minnesota - -pARMS is free software; you can redistribute it and/or modify it under -the terms of the GNU Lesser General Public License as published by the -Free Software Foundation; version 2.1 of the License. - -A copy of the licencing agreement is attached in the file LGPL. For -additional information contact the Free Software Foundation Inc., 59 -Temple Place - Suite 330, Boston, MA 02111, USA or visit the web-site - - http://www.gnu.org/copyleft/lesser.html - - -DISCLAIMER ----------- - -pARMS is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -License for more details. - -For more information contact saad@cs.umn.edu - - diff --git a/lib/parms/LGPL b/lib/parms/LGPL deleted file mode 100755 index 7abea2156..000000000 --- a/lib/parms/LGPL +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/lib/parms/Makefile b/lib/parms/Makefile deleted file mode 100644 index f07e14614..000000000 --- a/lib/parms/Makefile +++ /dev/null @@ -1,74 +0,0 @@ -# normally this files need not be edited -- architecture-dependent -# make commands are in makefile.in -- -# this makefile will make the library -- for testing PARARMS -- go to -# examples/grid or examples/general and see README files.. - -include ../../Makefile.in - -# path of the header files of pARMS -ICFLAGS = -I./include $(MPI_INC) - -# path of the header files for implmentaion of pARMS -ISRCINC = -I./src/include - -# path of the header files -IFFLAGS = -I./include - -# library path and directory declaration -LIB = ./lib/libparms.a -DIRS = ./ ./include ./lib ./src ./src/include ./src/DDPQ - -# files from the src directory -OBJ1 = ./src/parms_comm.o \ - ./src/parms_map.o ./src/parms_mat.o \ - ./src/parms_mat_vcsr.o ./src/parms_mat_dvcsr.o \ - ./src/parms_mem.o ./src/parms_operator.o \ - ./src/parms_ilu_vcsr.o \ - ./src/parms_pc.o \ - ./src/parms_pc_bj.o ./src/parms_pc_ras.o \ - ./src/parms_pc_schur.o ./src/parms_pc_schurras.o \ - ./src/parms_qsplit.o ./src/parms_solver.o \ - ./src/parms_table.o ./src/parms_timer.o \ - ./src/parms_vec.o ./src/parms_viewer.o \ - ./src/fgmres.o ./src/bicgstab.o ./src/cg.o ./src/pbicgstab.o \ - ./src/pbicgstab_ras.o ./src/bicgstab_ras.o\ - ./src/gmres.o ./src/parms_complex.o -#$(HYPRE_OBJ) \ - -# files from DDPQ directory -OBJ2 = ./src/DDPQ/arms2.o ./src/DDPQ/ilutpC.o \ - ./src/DDPQ/MatOps.o ./src/DDPQ/misc.o \ - ./src/DDPQ/PQ.o ./src/DDPQ/piluNEW.o \ - ./src/DDPQ/setblks.o ./src/DDPQ/sets.o \ - ./src/DDPQ/svdInvC.o - -OBJ = $(OBJ1) $(OBJ2) - -default: $(LIB) - -all: $(LIB) tests - -$(LIB): $(OBJ) - if [ ! -d lib ]; then \ - mkdir lib; \ - fi - $(AR) $(LIB) $(OBJ) - -.c.o: - ${MPICC} ${ICFLAGS} ${ISRCINC} $(COPT) \ - ${PARMS_DEFS} $< -c -o $@ - -cleanall: - @for dir in $(DIRS) ;\ - do \ - echo cleaning $$dir ;\ - (cd $$dir; rm -rf *.a *.o *.ex* *core* out* \#* *~) ;\ - done - -cleanobj: - @for dir in $(DIRS) ;\ - do \ - echo cleaning $$dir ;\ - (cd $$dir; rm -rf *.o *core* out* sol.* \#* *~) ;\ - done - diff --git a/lib/parms/Makefile_ollie b/lib/parms/Makefile_ollie deleted file mode 100755 index 01bde6407..000000000 --- a/lib/parms/Makefile_ollie +++ /dev/null @@ -1,73 +0,0 @@ -# normally this files need not be edited -- architecture-dependent -# make commands are in makefile.in -- -# this makefile will make the library -- for testing PARARMS -- go to -# examples/grid or examples/general and see README files.. - -include Makefile.in_ollie - -# path of the header files of pARMS -ICFLAGS = -I./include $(MPI_INC) - -# path of the header files for implmentaion of pARMS -ISRCINC = -I./src/include - -# path of the header files -IFFLAGS = -I./include - -# library path and directory declaration -LIB = ./lib/libparms.a -DIRS = ./ ./include ./lib ./src ./src/include ./src/DDPQ - -# files from the src directory -OBJ1 = ./src/parms_comm.o \ - ./src/parms_map.o ./src/parms_mat.o \ - ./src/parms_mat_vcsr.o ./src/parms_mat_dvcsr.o \ - ./src/parms_mem.o ./src/parms_operator.o \ - ./src/parms_ilu_vcsr.o \ - ./src/parms_pc.o \ - ./src/parms_pc_bj.o ./src/parms_pc_ras.o \ - ./src/parms_pc_schur.o ./src/parms_pc_schurras.o \ - ./src/parms_qsplit.o ./src/parms_solver.o \ - ./src/parms_table.o ./src/parms_timer.o \ - ./src/parms_vec.o ./src/parms_viewer.o \ - ./src/fgmres.o ./src/bicgstab.o ./src/cg.o \ - ./src/gmres.o ./src/parms_complex.o -#$(HYPRE_OBJ) \ - -# files from DDPQ directory -OBJ2 = ./src/DDPQ/arms2.o ./src/DDPQ/ilutpC.o \ - ./src/DDPQ/MatOps.o ./src/DDPQ/misc.o \ - ./src/DDPQ/PQ.o ./src/DDPQ/piluNEW.o \ - ./src/DDPQ/setblks.o ./src/DDPQ/sets.o \ - ./src/DDPQ/svdInvC.o - -OBJ = $(OBJ1) $(OBJ2) - -default: $(LIB) - -all: $(LIB) tests - -$(LIB): $(OBJ) - if [ ! -d lib ]; then \ - mkdir lib; \ - fi - $(AR) $(ARFLAGS) $(LIB) $(OBJ) - -.c.o: - ${CC} ${ICFLAGS} ${ISRCINC} $(COPT) \ - ${PARMS_DEFS} $< -c -o $@ - -cleanall: - @for dir in $(DIRS) ;\ - do \ - echo cleaning $$dir ;\ - (cd $$dir; rm -rf *.a *.o *.ex* *core* out* \#* *~) ;\ - done - -cleanobj: - @for dir in $(DIRS) ;\ - do \ - echo cleaning $$dir ;\ - (cd $$dir; rm -rf *.o *core* out* sol.* \#* *~) ;\ - done - diff --git a/lib/parms/README b/lib/parms/README deleted file mode 100755 index 2bff6a939..000000000 --- a/lib/parms/README +++ /dev/null @@ -1,68 +0,0 @@ -This is a modified version basen on - -/////////////////////////////////////////////////////////////////////// - -======================================================================= - --> Version 3.2 Latest changes made on: Fri Jan 14 17:52:55 CST 2011 -======================================================================= - -Welcome to pARMS3 - this is a new version of the parallel Algebraic -Recursive Multilevel Solvers which has been reimplemented -completely. This version includes - --- The Restrictive Additive Schwarz (RAS) procedures - --- Schur complement techniques (with RAS) - --- ddPQ ordering for local matrices.. [However, the Schur - complement techniques do not yet support ddPQ orderings] - --- (flexible) GMRES accelerator - -pARMS has been written mainly by Zhongze Li. --- various contributions to the code from - ** Yousef Saad - ** Masha Sosonkina - ** Daniel Osei-Kuffuor --- other contributors: Brian Suchomel, Matthias Bollhoefer, Na Li, and - Die Li. - ------------------------------------------------------------------------------- - -See README_INSTALL for information on installing pARMS version 3.1/ - -BEFORE INSTALLING PARMS READ FIRST THE COPYRIGHT AGREEMENT. - -////////////////////////////////////////////////////////////////////////////// - -additional routines are implemented by Annika Fuchs: -Solver: BICGS (bicgstab.c) -PC: SCHURRAS (parms_pc_schurras.c) adapted from Li Z, Saad Y: SchurRAS: A restricted version of the - overlapping Schur complement preconditioner, Report umsi-2004-76, Minnesota - Supercomputer Institute, University of Minnesota, Minneapolis, MN, 2004. - -parms_ilu_update() for reusing of the LU-Facorization (parms_ilu_vcsr.c) -parms_ilu_getu_vcsr() used by SchurRAS-PC -parms_mat_get_offdiag() used by SchurRAS-PC - -changes in parms_pc_schur.c parms_pc_ras.c parms_pc_bj.c with new parameter -data->issetup for reusing of the LU-Facorization - -changed convergence criterion in fgmres.c, gmres.c (ro <= tol) instead of (ro -<= eps1) - -/////////////////////////////////////////////////////////////////////////////// - -additional routine implemented by Natalja Rakowsky, natalja.rakowsky@awi.de - -Solver: PBICGS (pbicgstab.c) - - Pipelined BiCGstab, overlapping global sums with computation. See: - Siegfried Cools, Wim Vanroose - The communication-hiding pipelined BiCGStab method for the parallel solution of large unsymmetric linear systems - Parallel Computing 65, pp. 1-20, July 2017 - -Solver: PBICGS_RAS (pbicgstab_ras.c) - - Pipelined BiCGstab with hard-coded RAS preconditioner. This allows to overlap the - halo communication for RAS with some computations in bicgstab. diff --git a/lib/parms/README_INSTALL b/lib/parms/README_INSTALL deleted file mode 100755 index 3e5880a74..000000000 --- a/lib/parms/README_INSTALL +++ /dev/null @@ -1,86 +0,0 @@ - ------------------------------- INSTALLING AND RUNNING pARMS3 ----------------- - - -I. INSTALLING pARMS. - - a) Copy the appropriate conf/makefile.XX to makefile.in in this - directory - - b) Edit makefile.in to change the paths of BLAS and MPI. - If no BLAS installation exists, remove -DHAS_BLAS from CFLAGS in - make_inc - - NOTE: To compile the complex version of pARMS, replace the -DDBL flag in - makefile.in with -DDBL_CMPLX. - - c) Type 'make' to build the library -- - -II. Running the examples - - a) Now try running the example with systems from Finite Difference - matrices. Enter directory grid and type 'make dd-grid.ex' - to build all the executable and run it. Recall however that there - the number of processors is restricted to being equal to - nprocx*nprocy [see input file and README for details] - - b) Try running some examples with general matrices. - Enter directory 'examples/general'. There are several options: - -- if you want a simple run with the partitioner 'dse' available with - parms type make dd-HB-dse.ex and run the executable dd-HB-dse.ex - -- if you want a run with the partitioner 'metis' you need to - modify the makefile and change the flags XIFLAGS and XLIB - to give a path for the library and the include directory. - -- See the reference guide for details on how to setup the parms map - -- object when using metis. - - c) Enter directory 'examples/petsc and type 'make dd-petsc.ex - -user_defined_pc' to build - the petsc version of driver program. The option -user_defined_pc - allows us to use parms preconditioners with the PETSc solver. - Without this option, a default PETSc precoditioner will be used. - - d) Fortran versions of the above examples also exist in the same - directory. They typically have the prefix 'f' attached to the name. So - for instance, in the grid directory, one would type 'make fdd-grid.ex' - to build the executable, and 'mpirun -np 2 ./fdd-grid.ex' to run it - with two processors. Again note the restriction for the number of - processors for the grid problem. See input file and README file in the - grid directory for details. - - -III Details on the test runs - There are three subdirectories: grid, general, and petsc. - a). The file dd-grid.c in subdirectory grid - solve a poisson equation in parallel. The scale of problem - on each PE is fixed. - b). The file dd-HB-dse.c in subdirectory 'general' - show how to solve a system with a general matrix with pARMS. - c). The file dd-petsc.c in subdirectory petsc shows how to use - preconditioners in pARMS as add-ons to PETSc. - -IV. Sequences in which routines are called. - a). Partition the mesh or graph using a partitioner such as Metis, - or dse (included), or ParMetis, or zoltan, etc... - b). Create a map object based on the output from a mesh - partitioning software. For example, based on the output from - the Metis, you may create a map object by calling function - parms_MapCreateFromGlobal(...). - c). Create matrix based on the map created - above. parms_MatCreate(&mat, map); - d). Insert entries into the vectors and the matrix. Note that - the indices of entries are in global labeling. Vectors in this - version of pARMS use the standard C array pointers. FLOAT *rhs, *sol; - e). Set up the matrix by calling - parms_MatSetup(mat); After this call above, one cannot insert entries to - the matrix anymore. - f). Create a preconditioner based on the matrix created - above. parms_PCCreate(&pc, mat); You can set the type of the - preconditioner by parms_PCSetType. Currently, PCBJ(block - Jacobi), PCSCHUR(Schur complement), PCRAS(restrctive - additive Schwarz) are available. More robust preconditioner - like SCHURRAS will be available soon-- - g). Call parms_PCSetup(pc) to create the preconditioning matrix. - h). Create a solver based on the matrix and the preconditioner - created above. parms_SolverCreate(&solver, mat, pc); - i). Solve the linear equation. parms_SolverApply(solver, rhs, sol); diff --git a/lib/parms/conf/makefile.altix b/lib/parms/conf/makefile.altix deleted file mode 100755 index e6514226d..000000000 --- a/lib/parms/conf/makefile.altix +++ /dev/null @@ -1,54 +0,0 @@ -# NOTE: -# When using this makefile.in file, be sure to link your -# C code with CLINKFLAGS and your Fortran code with FLINKFLAGS -# - -SHELL = /bin/sh - -.SUFFIXES: -.SUFFIXES: .c .o .f .F - -# C compiler -CC = icc - -# optimization flags -COPTFLAGS = -O3 -Wall - -# other compile options -CFLAGS = -DUSE_MPI -DREAL=double -DDBL -DHAS_BLAS -CFFLAGS = -DFORTRAN_UNDERSCORE -DVOID_POINTER_SIZE_8 - -# FORTRAN compiler -FC = ifort -F90 = ifort - -# FORTRAN compile options -FFLAGS = -O3 -DVOID_POINTER_SIZE_8 -convert big_endian - -# linker and options (Read the above NOTE) -LINKER = ${FC} -F90LINKER = ${F90} -CLINKFLAGS = ${FFLAGS} -nofor_main -FLINKFLAGS = ${FFLAGS} - -# -RM = rm -RMFLAGS = -rf - -# archive and options -AR = ar -ARFLAGS = -cr -EXTFLAGS = -x - -# MPI library -MPI_LIB_DIR = -MPI_LIB = -lmpi - -# LAPACK, BLAS library -LAPACK_BLAS_DIR = -LAPACK_BLAS_LIB = -lscs - -# general math libary -MATH_LIB_DIR = -MATH_LIB = -lm - diff --git a/lib/parms/conf/makefile.bigben b/lib/parms/conf/makefile.bigben deleted file mode 100755 index c036fafcb..000000000 --- a/lib/parms/conf/makefile.bigben +++ /dev/null @@ -1,49 +0,0 @@ -SHELL = /bin/sh - -.SUFFIXES: -.SUFFIXES: .c .o .f .F - -# C compiler -CC = cc - -# optimization flags -COPTFLAGS = -fastsse -O3 -tp k8-64 -Mnontemporal -Mprefetch=distance:8,nta -c9x -mcmodel=medium - -# other compile options -CFLAGS = -DUSE_MPI -DREAL=double -DDBL -DHAS_BLAS -CFFLAGS = -DFORTRAN_UNDERSCORE -DVOID_POINTER_SIZE_8 - -# FORTRAN compiler -FC = ftn -F90 = ftn - -# FORTRAN compile options -FFLAGS = -DVOID_POINTER_SIZE_8 -fastsse -O3 -Mnontemporal -Mprefetch=distance:8,nta -tp k8-64 -Mbyteswapio -mcmodel=medium - -# linker and options -LINKER = ${FC} -F90LINKER = ${F90} -CLINKFLAGS = ${FFLAGS} -Mnomain -FLINKFLAGS = ${FFLAGS} - -# -RM = rm -RMFLAGS = -rf - -# archive and options -AR = ar -ARFLAGS = -cr -EXTFLAGS = -x - -# MPI library -MPI_LIB_DIR = -MPI_LIB = - -# LAPACK, BLAS library -LAPACK_BLAS_LIB_DIR = -LAPACK_BLAS_LIB = -lacml - -# general math libary -MATH_LIB_DIR = -MATH_LIB = - diff --git a/lib/parms/conf/makefile.blade_ompi_intel b/lib/parms/conf/makefile.blade_ompi_intel deleted file mode 100755 index 628f6bf9c..000000000 --- a/lib/parms/conf/makefile.blade_ompi_intel +++ /dev/null @@ -1,58 +0,0 @@ -# Sample makefile.in for MSI's bladecenter cluster. -# This assumes usage of the intel and ompi/intel modules. -# Please be sure to also load the base and mkl modules, for -# lapack and blas routines. -# -# i.e. Type: 'module load base intel ompi/intel mkl' to load the modules -# before doing a make. - -SHELL = /bin/sh - -.SUFFIXES: -.SUFFIXES: .c .o .f .F - -# C compiler -CC = mpicc - -# optimization flags -COPTFLAGS = -O3 -Wall -wd981 -g - -# other compile options -CFLAGS = -DUSE_MPI -DREAL=double -DDBL -DHAS_BLAS -CFFLAGS = -DFORTRAN_UNDERSCORE -DVOID_POINTER_SIZE_8 - -# FORTRAN compiler -F90 = mpif90 -FC = mpif77 - -# FORTRAN compile options -FFLAGS = -O3 -DVOID_POINTER_SIZE_8 -convert big_endian -g - -# linker and options -F90LINKER = ${F90} -LINKER = ${FC} -CLINKFLAGS = ${FFLAGS} -nofor_main -FLINKFLAGS = ${FFLAGS} - -# -RM = rm -RMFLAGS = -rf - -# archive and options -AR = ar -ARFLAGS = -cr -EXTFLAGS = -x - -# MPI library -MPI_LIB_DIR = -MPI_LIB = - -# LAPACK, BLAS library -LAPACK_BLAS_LIB_DIR = -L/soft/intel/mkl/10.2.1.017/lib/em64t -LAPACK_BLAS_LIB = -lmkl_lapack -lmkl_intel_thread -lmkl_core \ - -lguide -lmkl_intel_lp64 - -# general math libary -MATH_LIB_DIR = -MATH_LIB = -lm - diff --git a/lib/parms/conf/makefile.calhoun_ompi_gnu b/lib/parms/conf/makefile.calhoun_ompi_gnu deleted file mode 100755 index deefd7e20..000000000 --- a/lib/parms/conf/makefile.calhoun_ompi_gnu +++ /dev/null @@ -1,57 +0,0 @@ -# Sample makefile.in for MSI's SGI altix cluster - Calhoun machine. -# This assumes usage of the ompi/gnu module. Be sure to also load -# the mkl module, for lapack and blas routines. -# -# i.e. Type: 'module load ompi/gnu mkl' to load the modules -# before doing a make. - -SHELL = /bin/sh - -.SUFFIXES: -.SUFFIXES: .c .o .f .F - -# C compiler -CC = mpicc - -# optimization flags -COPTFLAGS = -O3 -Wall -g - -# other compile options -CFLAGS = -DUSE_MPI -DREAL=double -DDBL -DHAS_BLAS -CFFLAGS = -DFORTRAN_UNDERSCORE -DVOID_POINTER_SIZE_8 - -# FORTRAN compiler -F90 = mpif90 -FC = mpif77 - -# FORTRAN compile options -FFLAGS = -O3 -DVOID_POINTER_SIZE_8 -g - -# linker and options -F90LINKER = ${F90} -LINKER = ${FC} -CLINKFLAGS = ${FFLAGS} -FLINKFLAGS = ${FFLAGS} - -# -RM = rm -RMFLAGS = -rf - -# archive and options -AR = ar -ARFLAGS = -cr -EXTFLAGS = -x - -# MPI library -MPI_LIB_DIR = -MPI_LIB = -lmpi - -# LAPACK, BLAS library -LAPACK_BLAS_LIB_DIR = -L/soft/intel/mkl/10.2.1.017/lib/em64t -LAPACK_BLAS_LIB = -lmkl_lapack -lmkl_intel_thread -lmkl_core \ - -lguide -lmkl_intel_lp64 - -# general math libary -MATH_LIB_DIR = -MATH_LIB = -lm - diff --git a/lib/parms/conf/makefile.calhoun_ompi_intel b/lib/parms/conf/makefile.calhoun_ompi_intel deleted file mode 100755 index 9177182f6..000000000 --- a/lib/parms/conf/makefile.calhoun_ompi_intel +++ /dev/null @@ -1,58 +0,0 @@ -# Sample makefile.in for MSI's SGI altix cluster - Calhoun machine. -# This assumes usage of the intel and ompi/intel modules. -# Please be sure to also load the mkl module, for -# lapack and blas routines. -# -# i.e. Type: 'module load intel ompi/intel mkl' to load the modules -# before doing a make. - -SHELL = /bin/sh - -.SUFFIXES: -.SUFFIXES: .c .o .f .F - -# C compiler -CC = mpicc - -# optimization flags -COPTFLAGS = -O3 -Wall -wd981 -g - -# other compile options -CFLAGS = -DUSE_MPI -DREAL=double -DDBL -DHAS_BLAS -CFFLAGS = -DFORTRAN_UNDERSCORE -DVOID_POINTER_SIZE_8 - -# FORTRAN compiler -F90 = mpif90 -FC = mpif77 - -# FORTRAN compile options -FFLAGS = -O3 -g -DVOID_POINTER_SIZE_8 -convert big_endian - -# linker and options -F90LINKER = ${F90} -LINKER = ${FC} -CLINKFLAGS = ${FFLAGS} -nofor_main -FLINKFLAGS = ${FFLAGS} - -# -RM = rm -RMFLAGS = -rf - -# archive and options -AR = ar -ARFLAGS = -cr -EXTFLAGS = -x - -# MPI library -MPI_LIB_DIR = -MPI_LIB = -lmpi - -# LAPACK, BLAS library -LAPACK_BLAS_LIB_DIR = -L/soft/intel/mkl/10.2.1.017/lib/em64t -LAPACK_BLAS_LIB = -lmkl_lapack -lmkl_intel_thread -lmkl_core \ - -lguide -lmkl_intel_lp64 - -# general math libary -MATH_LIB_DIR = -MATH_LIB = -lm - diff --git a/lib/parms/conf/makefile.itasca_ompi_intel b/lib/parms/conf/makefile.itasca_ompi_intel deleted file mode 100755 index 628f6bf9c..000000000 --- a/lib/parms/conf/makefile.itasca_ompi_intel +++ /dev/null @@ -1,58 +0,0 @@ -# Sample makefile.in for MSI's bladecenter cluster. -# This assumes usage of the intel and ompi/intel modules. -# Please be sure to also load the base and mkl modules, for -# lapack and blas routines. -# -# i.e. Type: 'module load base intel ompi/intel mkl' to load the modules -# before doing a make. - -SHELL = /bin/sh - -.SUFFIXES: -.SUFFIXES: .c .o .f .F - -# C compiler -CC = mpicc - -# optimization flags -COPTFLAGS = -O3 -Wall -wd981 -g - -# other compile options -CFLAGS = -DUSE_MPI -DREAL=double -DDBL -DHAS_BLAS -CFFLAGS = -DFORTRAN_UNDERSCORE -DVOID_POINTER_SIZE_8 - -# FORTRAN compiler -F90 = mpif90 -FC = mpif77 - -# FORTRAN compile options -FFLAGS = -O3 -DVOID_POINTER_SIZE_8 -convert big_endian -g - -# linker and options -F90LINKER = ${F90} -LINKER = ${FC} -CLINKFLAGS = ${FFLAGS} -nofor_main -FLINKFLAGS = ${FFLAGS} - -# -RM = rm -RMFLAGS = -rf - -# archive and options -AR = ar -ARFLAGS = -cr -EXTFLAGS = -x - -# MPI library -MPI_LIB_DIR = -MPI_LIB = - -# LAPACK, BLAS library -LAPACK_BLAS_LIB_DIR = -L/soft/intel/mkl/10.2.1.017/lib/em64t -LAPACK_BLAS_LIB = -lmkl_lapack -lmkl_intel_thread -lmkl_core \ - -lguide -lmkl_intel_lp64 - -# general math libary -MATH_LIB_DIR = -MATH_LIB = -lm - diff --git a/lib/parms/conf/makefile.laptop b/lib/parms/conf/makefile.laptop deleted file mode 100755 index 691aad35c..000000000 --- a/lib/parms/conf/makefile.laptop +++ /dev/null @@ -1,49 +0,0 @@ -SHELL = /bin/sh - -.SUFFIXES: -.SUFFIXES: .c .o .f .F - -# C compiler -CC = mpicc - -# optimization flags -COPTFLAGS = -O3 -Wall -g - -# other compile options -CFLAGS = -DUSE_MPI -DREAL=double -DDBL -DHAS_BLAS -CFFLAGS = -DFORTRAN_UNDERSCORE -DVOID_POINTER_SIZE_8 - -# FORTRAN compiler -FC = mpif77 -F90 = mpif90 - -# FORTRAN compile options -FFLAGS = -O3 -DVOID_POINTER_SIZE_8 -g - -# linker and options -LINKER = ${FC} -F90LINKER = ${F90} -CLINKFLAGS = ${FFLAGS} -FLINKFLAGS = ${FFLAGS} - -# -RM = rm -RMFLAGS = -rf - -# archive and options -AR = ar -ARFLAGS = -cr -EXTFLAGS = -x - -# MPI library -MPI_LIB_DIR = -MPI_LIB = - -# LAPACK, BLAS library -LAPACK_BLAS_LIB_DIR = -LAPACK_BLAS_LIB = -llapack -lblas-3 - -# general math libary -MATH_LIB_DIR = -MATH_LIB = -lm - diff --git a/lib/parms/conf/makefile.linux.gcc b/lib/parms/conf/makefile.linux.gcc deleted file mode 100755 index 106ad22a2..000000000 --- a/lib/parms/conf/makefile.linux.gcc +++ /dev/null @@ -1,49 +0,0 @@ -SHELL = /bin/sh - -.SUFFIXES: -.SUFFIXES: .c .o .f .F - -# C compiler -CC = mpicc - -# optimization flags -COPTFLAGS = -O3 -Wall -g - -# other compile options -CFLAGS = -DUSE_MPI -DREAL=double -DDBL -DHAS_BLAS -CFFLAGS = -DFORTRAN_UNDERSCORE -DVOID_POINTER_SIZE_4 - -# FORTRAN compiler -F90 = mpif90 -FC = mpif77 - -# FORTRAN compile options -FFLAGS = -O3 -DVOID_POINTER_SIZE_4 -g - -# linker and options -F90LINKER = ${F90} -LINKER = ${FC} -CLINKFLAGS = ${FFLAGS} -FLINKFLAGS = ${FFLAGS} - -# -RM = rm -RMFLAGS = -rf - -# archive and options -AR = ar -ARFLAGS = -cr -EXTFLAGS = -x - -# MPI library -MPI_LIB_DIR = -MPI_LIB = - -# LAPACK, BLAS library -LAPACK_BLAS_LIB_DIR = -LAPACK_BLAS_LIB = -llapack -lblas-3 - -# general math libary -MATH_LIB_DIR = -MATH_LIB = -lm - diff --git a/lib/parms/conf/makefile.linux.gcc3 b/lib/parms/conf/makefile.linux.gcc3 deleted file mode 100755 index 291d490ce..000000000 --- a/lib/parms/conf/makefile.linux.gcc3 +++ /dev/null @@ -1,49 +0,0 @@ -SHELL = /bin/sh - -.SUFFIXES: -.SUFFIXES: .c .o .f .F - -# C compiler -CC = mpicc - -# optimization flags -COPTFLAGS = -O3 -Wall - -# other compile options -CFLAGS = -DUSE_MPI -DREAL=double -DDBL -DHAS_BLAS -DGCC3 -CFFLAGS = -DFORTRAN_UNDERSCORE -DVOID_POINTER_SIZE_4 - -# FORTRAN compiler -FC = mpif77 -F90 = mpif90 - -# FORTRAN compile options -FFLAGS = -O3 -DVOID_POINTER_SIZE_4 - -# linker and options -LINKER = ${FC} -F90LINKER = ${F90} -CLINKFLAGS = ${FFLAGS} -FLINKFLAGS = ${FFLAGS} - -# -RM = rm -RMFLAGS = -rf - -# archive and options -AR = ar -ARFLAGS = -cr -EXTFLAGS = -x - -# MPI library -MPI_LIB_DIR = -MPI_LIB = - -# LAPACK, BLAS library -LAPACK_BLAS_LIB_DIR = -LAPACK_BLAS_LIB = -llapack -lblas-3 - -# general math libary -MATH_LIB_DIR = -MATH_LIB = -lm - diff --git a/lib/parms/conf/makefile.linux.icc b/lib/parms/conf/makefile.linux.icc deleted file mode 100755 index b8c0d611f..000000000 --- a/lib/parms/conf/makefile.linux.icc +++ /dev/null @@ -1,53 +0,0 @@ -# NOTE: -# When using this makefile.in file, be sure to link your -# C code with CLINKFLAGS and your Fortran code with FLINKFLAGS -# - -SHELL = /bin/sh - -.SUFFIXES: -.SUFFIXES: .c .o .f .F - -# C compiler -CC = mpicc - -# optimization flags -COPTFLAGS = -O3 -Wall -g - -# other compile options -CFLAGS = -DUSE_MPI -DREAL=double -DDBL -DHAS_BLAS -CFFLAGS = -DFORTRAN_UNDERSCORE -DVOID_POINTER_SIZE_4 - -# FORTRAN compiler -FC = mpif77 -F90 = mpif90 -# FORTRAN compile options -FFLAGS = -O3 -DVOID_POINTER_SIZE_4 -g - -# linker and options (Read 'NOTE' above) -LINKER = ${FC} -F90LINKER = ${F90} -CLINKFLAGS = ${FFLAGS} -nofor_main -FLINKFLAGS = ${FFLAGS} - -# -RM = rm -RMFLAGS = -rf - -# archive and options -AR = ar -ARFLAGS = -cr -EXTFLAGS = -x - -# MPI library -MPI_LIB_DIR = -MPI_LIB = - -# LAPACK, BLAS library -LAPACK_BLAS_LIB_DIR = -LAPACK_BLAS_LIB = -llapack -lblas - -# general math libary -MATH_LIB_DIR = -MATH_LIB = -lm - diff --git a/lib/parms/conf/makefile.linux.pathscale b/lib/parms/conf/makefile.linux.pathscale deleted file mode 100755 index 059325ee1..000000000 --- a/lib/parms/conf/makefile.linux.pathscale +++ /dev/null @@ -1,53 +0,0 @@ -SHELL = /bin/sh - -.SUFFIXES: -.SUFFIXES: .c .o .f .F - -# C compiler -CC = mpicc - -# optimization flags -COPTFLAGS = -march=opteron -O3 -OPT:Ofast -fno-math-errno \ - -std=c99 -ffast-math -LNO:prefetch=2 -LNO:simd=2 - - -# other compile options -CFLAGS = -DUSE_MPI -DREAL=double -DDBL -DHAS_BLAS -DPATHSCALE -CFFLAGS = -DFORTRAN_UNDERSCORE -DVOID_POINTER_SIZE_8 - -# FORTRAN compiler -FC = mpif77 -F90 = mpif90 - -# FORTRAN compile options -FFLAGS = -DVOID_POINTER_SIZE_8 -march=opteron -O3 -OPT:Ofast \ - -fno-math-errno -ffast-math -LNO:prefetch=2 -LNO:simd=2 - - -# linker and options -LINKER = ${FC} -F90LINKER = ${F90} -CLINKFLAGS = ${FFLAGS} -FLINKFLAGS = ${FFLAGS} - -# -RM = rm -RMFLAGS = -rf - -# archive and options -AR = ar -ARFLAGS = -cr -EXTFLAGS = -x - -# MPI library -MPI_LIB_DIR = -MPI_LIB = - -# LAPACK, BLAS library -LAPACK_BLAS_LIB_DIR = -LAPACK_BLAS_LIB = ${ACML} - -# general math libary -MATH_LIB_DIR = -MATH_LIB = - diff --git a/lib/parms/conf/makefile.linux.sun b/lib/parms/conf/makefile.linux.sun deleted file mode 100755 index 8345ee15a..000000000 --- a/lib/parms/conf/makefile.linux.sun +++ /dev/null @@ -1,49 +0,0 @@ -SHELL = /bin/sh - -.SUFFIXES: -.SUFFIXES: .c .o .f .F - -# C compiler -CC = mpicc - -# optimization flags -COPTFLAGS = -fast -xarch=sse2 -xvector=simd - -# other compile options -CFLAGS = -DUSE_MPI -DREAL=double -DDBL -DHAS_BLAS -CFFLAGS = -DFORTRAN_UNDERSCORE -DVOID_POINTER_SIZE_4 - -# FORTRAN compiler -FC = mpif77 -F90 = mpif90 - -# FORTRAN compile options -FFLAGS = -DVOID_POINTER_SIZE_4 -fast -xarch=sse2 -xvector=simd - -# linker and options -LINKER = ${FC} -F90LINKER = ${F90} -CLINKFLAGS = ${FFLAGS} -FLINKFLAGS = ${FFLAGS} - -# -RM = rm -RMFLAGS = -rf - -# archive and options -AR = ar -ARFLAGS = -cr -EXTFLAGS = -x - -# MPI library -MPI_LIB_DIR = -MPI_LIB = - -# LAPACK, BLAS library -LAPACK_BLAS_LIB_DIR = -LAPACK_BLAS_LIB = -xlic_lib=sunperf - -# general math libary -MATH_LIB_DIR = -MATH_LIB = -lm - diff --git a/lib/parms/conf/makefile.o38k b/lib/parms/conf/makefile.o38k deleted file mode 100755 index 9e2e7cdf6..000000000 --- a/lib/parms/conf/makefile.o38k +++ /dev/null @@ -1,49 +0,0 @@ -SHELL = /bin/sh - -.SUFFIXES: -.SUFFIXES: .c .o .f .F - -# C compiler -CC = cc - -# optimization flags -COPTFLAGS = -O3 - -# other compile options -CFLAGS = -DUSE_MPI -DREAL=double -DDBL -DHAS_BLAS -64 -CFFLAGS = -DFORTRAN_UNDERSCORE -DVOID_POINTER_SIZE_8 - -# FORTRAN compiler -FC = f90 -F90 = f90 - -# FORTRAN compile options -FFLAGS = -O3 -DVOID_POINTER_SIZE_8 -64 - -# linker and options -LINKER = ${FC} -F90LINKER = ${F90} -CLINKFLAGS = ${FFLAGS} -FLINKFLAGS = ${FFLAGS} - -# -RM = rm -RMFLAGS = -rf - -# archive and options -AR = ar -ARFLAGS = -cr -EXTFLAGS = -x - -# MPI library -MPI_LIB_DIR = -MPI_LIB = -lmpi - -# BLAS and LAPACK library -LAPACK_BLAS_LIB_DIR = -LAPACK_BLAS_LIB = -lscs - -# general math libary -MATH_LIB_DIR = -MATH_LIB = -lm - diff --git a/lib/parms/conf/makefile.regatta b/lib/parms/conf/makefile.regatta deleted file mode 100755 index e46aff257..000000000 --- a/lib/parms/conf/makefile.regatta +++ /dev/null @@ -1,51 +0,0 @@ -SHELL = /bin/sh - -.SUFFIXES: -.SUFFIXES: .c .o .f .F - -# C compiler -CC = mpcc_r - -# optimization flags -COPTFLAGS = -O3 -qarch=pwr4 -qtune=pwr4 - -# other compile options -CFLAGS = -DUSE_MPI -DREAL=double -DDBL -DHAS_BLAS \ - -q64 -qstrict -qcpluscmt -CFFLAGS = -DVOID_POINTER_SIZE_8 - -# FORTRAN compiler -FC = mpxlf_r -F90 = mpxlf90_r - -# FORTRAN compile options -FFLAGS = -O3 -qarch=pwr4 -qtune=pwr4 -qstrict -q64 \ - -WF,-DVOID_POINTER_SIZE_8 - -# linker and options -LINKER = ${FC} -F90LINKER = ${F90} -CLINKFLAGS = -O3 -qarch=pwr4 -qtune=pwr4 -qstrict -q64 -FLINKFLAGS = -O3 -qarch=pwr4 -qtune=pwr4 -qstrict -q64 - -# -RM = rm -RMFLAGS = -rf - -# archive and options -AR = ar -ARFLAGS = -cr -X64 -EXTFLAGS = - -# MPI library -MPI_LIB_DIR = -MPI_LIB = - -# LAPACK, BLAS library -LAPACK_BLAS_LIB_DIR = -LAPACK_BLAS_LIB = -L/usr/local/lib -llapack64 -lessl - -# general math libary -MATH_LIB_DIR = -MATH_LIB = -lm - diff --git a/lib/parms/conf/makefile.seaborg b/lib/parms/conf/makefile.seaborg deleted file mode 100755 index 2a3545d4d..000000000 --- a/lib/parms/conf/makefile.seaborg +++ /dev/null @@ -1,54 +0,0 @@ -SHELL = /bin/sh - -.SUFFIXES: -.SUFFIXES: .c .o .f .F - -# C compiler -CC = mpcc_r - -# optimization flags -COPTFLAGS = -O3 -qarch=pwr3 -qtune=pwr3 \ - - -# other compile options -CFLAGS = -DUSE_MPI -DREAL=double -DDBL -DHAS_BLAS \ - -qstrict -qcpluscmt -q64 -CFFLAGS = -DVOID_POINTER_SIZE_8 - -# FORTRAN compiler -FC = mpxlf_r -F90 = mpxlf90_r - -# FORTRAN compile options -FFLAGS = -O3 -qarch=pwr3 -qtune=pwr3 -qstrict \ - -q64 -WF,-DVOID_POINTER_SIZE_8 - -# linker and options -LINKER = ${FC} -F90LINKER = ${F90} -CLINKFLAGS = -O3 -qarch=pwr3 -qtune=pwr3 -qstrict \ - -q64 -FLINKFLAGS = -O3 -qarch=pwr3 -qtune=pwr3 -qstrict \ - -q64 - -# -RM = rm -RMFLAGS = -rf - -# archive and options -AR = ar -ARFLAGS = -cr -X64 -EXTFLAGS = - -# MPI library -MPI_LIB_DIR = -MPI_LIB = - -# LAPACK, BLAS library -LAPACK_BLAS_LIB_DIR = -LAPACK_BLAS_LIB = ${LAPACK} -lessl - -# general math libary -MATH_LIB_DIR = -MATH_LIB = -lm - diff --git a/lib/parms/docs/README b/lib/parms/docs/README deleted file mode 100755 index 7bc55b02b..000000000 --- a/lib/parms/docs/README +++ /dev/null @@ -1,109 +0,0 @@ - -This package contains a simplified version of the new pARMS package, -initially developed by Zhongze Li and Yousef Saad. A number of -changes have been made relative to the earlier versions in an effort -to improve readability. Below is a description of the pARMS object -hierarchy, and some details on how the package may be used and/ -extended. - --DOK. ------------------------------------------------------- - - -Hierarchy of Struct Objects -=========================== - -1) Matrix: - Parms_Mat - | - ___________________|___________ - | | - MatVCSR(Serial CSR matrix) MatDVCSR(Parallel CSR matrix) - -Each one of these have their own definitions for the following operations characterizing the Parms_Mat struct: -a) apply (which is matvec) -b) setup (setting up communication handler for matvec, and reordering matrix into internal and interface nodes) -c) setcommtype (set the communication type: P2P or DERIVED) -d) mvpy (Performs z = beta*y + alpha*A*x: where z, y, and x are vectors; alpha and beta are scalars; and A is the matrix.) -e) getdiag (get diagonal part of local matrix) -f) getlmat (get local matrix) -g) extend ( Extend the submatrix mat by including equations that correspond to immediate neighbouring variables.) -h) mvoffd ( matvec for off-diagonal part) -i) gethandler (get the communication handler for matvec) -j) matfree (free the matrix) - -eg. Calling parms_MatVec will call the appropriate matvec operation depending on which type of matrix we are using (ie. matvcsr or matdvcsr). - -See ./src/include/parms_mat_impl.h, ./src/parms_mat.c, ./src/parms_mat_vcsr.c, and ./src/parms_mat_dvcsr.c for details. - -2) Preconditioner: - - Parms_PC - | -________________________________|______________________________ -| | | -SCHUR RAS BLOCK JACOBI - | | | -Parms_OP Parms_OP Parms_OP - -The Parms_PC struct defines the global preconditioners (SCHUR, RAS, and BLOCK JACOBI). Each one implements the following operations: -a) apply (apply the preconditioner to a vector) -b) setup (setup the preconditioner - memory allocation, ilu factorization , etc) -c) getratio (get the fill factor or memory use) -d) pc_free (free the precon) -e) pc_view (view preconditioner info) - -Each of these global preconditioners implements a local (ILU-based) preconditioner defined as the Parms_OP struct. - - See \src\include\parms_pc_impl.h, \src\parms_pc.c, parms_pc_schur.c, parms_pc_ras.c, and parms_pc_bj.c for details - -3) Operators (ILU-based local precons): - - Parms_OP - | -_________________________________|_______________________________ -| | | | -ARMS ILUT ILUK ILU0 - -These operators implement the following operations: -a) apply (apply local ilu preconditioner to a vector) -b) lsol (perform forward solve) -c) invS (perform the schur complement solve (at the last level)) -d) ascend (perform block back substitution) -e) getsize ( get the size of the local schur complement) -f) getnnz (get the nnz for the local preconditioner (also returns the size of the original matrix)) -g) operator_free (free local precon) -h) operator_view (view local precon info) - - See \src\include\parms_opt_impl.h, \src\parms_operator.c), and \src\parms_ilu_vcsr.c for iluk, ilu0, and ilut, and \src\DDPQ\arms2.c for arms. - - -4) Solver: - - Parms_Solver - | - _____________|____________ - | | - FGMRES GMRES(left preconditioned) - -The Parms_Solver struct currently has gmres and fgmres as the only choices. It implements the following operations: - -a) apply (apply the solver) -b) getresidual (get the residual vector) -c) getresidualnorm (return the residual 2-norm) -d) setksize ( set Krylov dimension) -e) setneig (set number of eigenvalues to compute -- NOT CURRENTLY IMPLEMENTED) -f) solver_free (free solver) -g) solver_view (view solver details) - - See \src\include\parms_solver_impl.h, \src\parms_solver.c, and \src\fgmres.c for details. - -================================================== - -The above hierarchies help make it simple to add new features to the code. For instance, to add a new solver, one only needs to ensure that the new -solver to be added implements the appropriate solver operations as defined in \src\include\parms_solver_impl.h (and implemented in \src\fgmres.c). -The new solver can then be included as a choice in the /src/parms_solver.c file. Similarly, new preconditioners and operators may be added. - ---- Daniel Osei-Kuffuor ---- ----------------------------- - diff --git a/lib/parms/docs/refmanual.pdf b/lib/parms/docs/refmanual.pdf deleted file mode 100755 index 3acc3d783b289dc56eb4fb905f32dcba9261f652..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 222963 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 Hqv}qSn-TSNR=kLy_Z$gXrmm-M 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%0DoajI8*(mDdpot<# zsbC89x@Je-ptz;wgy%vjdB%Hd@S!8u$WT_7zri1ZS#PVEf^pB>Ayj5E&4CQ`6nbai zBv=E7FHPj(c1n2*5C8W7TH=5Qct$#k?Ofo>tj@3e6}BlNTj)8kCQ_jd<$P1^GcuXg z_jJ$sa4r6I587DJHJ)w&GRbZ(@uy@@REf}M$)Sp}UrJ>42J)Qv?Y!Jl8Gncs1fLzo zUea^Z1GxFM?`ld4YP8aQ;DX;CW&du`jZjKcz!ou} zI6|V-cFZOePR)h&}4qY!Q^nRs$vUc4$a_(OY3eIE#U3SoPH!#0fJ=QP&?p zH+Ep%qUoK>3zU6pZz&mp)dR#Q&yM_t?JI)Apv#Lc9Y4L%C&uSYa{!pPGwCAl!2bFk z^y2cDFZQ6o-$TG(6yr%C`)By0U9)?@|Bjg+p{<>HjRa&pLL`W)i*GyacvGN~JENKOuru;+NW!%MO>8`;@s1$!&3G z6e>Q!bc%~T=^>sPX2%xN^!}m()sYQrqJH?SE|80Pa(w7>E8I%(n__Q10aAaLFd%lB z@3++mnS&_^2NW{?L_m==^sa_NVp(=XwA?!oqco|(LNqQ^oE~UMF1x6gH#v8 zk`aI8pj?QtGQf?T92?pdb6<`2*fc-?PT6Gz`y>$gek(*5$wE-FKr=69rwO@Pr#I@b1|9 zSfw{vs5aRQ1lv0deEI@C$8r3qaTykmIaNUj7Ga*{$aL{|HE2W5!1)l!>n|co z9UU7e8dFW#v`ASmerTmwSNt?15@N!`7Dbc8d#pJ%ozLxxDlF=e*Mb8KTlLL4d=&q_ zdM8j*D|O3}ZeKoUafsa*a=N6F;94#zNh&(6=wtr2Zh{7o+d^GKMPL(ZC7;oEsZ`xZ zZ?TtlT=-9>h`X8DSg0F{k*XN9QmoB_)P9?JbdcAowIB$AlFs0L0FhCZt&2n0q5ijB zQ43Akfl-kb_{kVlgj&xuRb=l~UUm(KdUJ-Wa9*DeX(5FN;83a^?P^Ziu8autlYz={8Dk-{(+|;v(Z}olnFL{dF!i z@g89>$zw>79>js4qCTbRBV^||OyTnklK|Eq#vvLhOLS~VW;jrQx6IA3>phZ;7}g|~ zAXlCswXZDy9evj&d^A3!PhF6X5R;fqNXg4cVUXB|D=EQw_E{LtFb$y{x~-Q_TtUu? z2o%+Dlj=ZE^b8`n(Z^+Wa*^ldo399)Ay2 zn|#&2FzlNRNpb&|l3%J{IACskhlC1VPRWBFxE=jK{Q-$l?GTiP*>d@QC!IRHF8Kzw z+>v>nQtGUnj`(6s&TOp(s%CJ=?Tl96e5;7m9~Z5aVJ*UNGG{rt1t?`Ej`A ziIhB<9Nt@}Y9xv8Rr?fQ)A6&^lB9}n(80V*{rhQ%LM-i9jfQ*6=psWMUUaPk0CgUv zcdK`cs+(6lV(~U$>M)=7*Qk7bDLKgZc`23<2*)%MTxKJC@jp&ipjSzAH2tGy75ccq z`ELYmn-jT*UH=FCg6)6s5dSy)LP$hYO;h*>zffjk{6FIttc?G!e!|KnbAyqhqK7)Bx%L z4S*&<8(;)5vU4_5lNoZO6T0p;d*x_GSPFfWyz;5#VU#Y-azT75_8(ul0Ve&C0xptgZyt`2>btdBK{i;VPa$bxh4M%hH$X6vHkb#e|jOD>>M2b z;X;W1?_Nk;>yHO(!U@OdTE>ckGDj@wK}9kpA{jA9 zTrOEQ#PPQ9-t*nN^jxE5bF}ro=2Yua>$k*bJJ|u@3-iRNb7O*q)=xQ#m;y#+bqWgu z9M~uEAy|M!fSXMWwhQw$5%<>|F5E}ZP;u-VN-;P@2r5MZJ5+Gks=ojTUO^*phcvLD zytp8P3<(Me1cdwxA+(x7+Hi0vUqeY0aS*MDX1tc z9Q*9SE!Yna7qJpl07Q>80DTdDW(nyGj@5TTWEeUVkh+uNE=;sR4i47U-Tf+LPw>Im zRz!yh=)(vIJB;oHG93S=LJ|Da^?=VcH4jXJci&eA({^RLRvRsE1mxj}ZQaUHxhS1lXzT28OzW{ZhHAyD$hNn%y2F;^E22=7Bl` z4dMpDBdRqFoHmj32Se$H1roXp01wvXGc5N_#Dv;|X%GYd(C31>(G)@i5=Q=r$&0|p z+l#;1c@Av*Dx|upg0oInD|nJ-i`5E9(G zK?MQqf5qq#2pSE>3A6_!{<-t&71-4W1h#>SxAUCe7og)A91P?Wi4S53>mDk2`&AR~ z9D@JVq!fV|K?HmxnDPV$+-Hy?hH94~Pkwh-jy`>SOTR;i|X0gJ{&i2J}lu5&sMbRPsX#xY;9M{^m^v zJtsg84s)lM$8u0n83L5^TdfCN5zI2;4do+X>H9k6TVVCO@-9H->zm=EzO3rpA>*Fo z>pMuu9!Vp=7erC|NwlC;7&bf${^UD6EATtVl>QIj(EMe9k{cKl8$OJp#Bx9j^4=|0 zFxdLJFp|4`fb_|=ieRcom|%YgBjRXXCqy456xbJHcoqi_=7Qf|5O>u!5*C~M_ogzq zHDu#An};w-ArKNC!d*XT;Q=`r3CInqD112|)~n7c5U@WLT#z(y!3j7Jo}pux0VNFu z0@Cc);hUXQ0o{54osm)C4H@3yhP3tPJ5Xt=`)f*EtHlp{Ir^le&dIS(bsn7yLzMhE_O-wKgkf7X2ktcx@JPZw8kpr|d39mcxI0 z4acc)P34i>|II*$@?62zZD%T=_<|7dRXRKSH&ABAf~xm2;C^zOa|pZ$zD6d5{yVdb z^S3?mYM4hIO~dcHy?M!li?l8GxD(Wm^Ju9a&mzv4#0IMtko67hvxpTa{_yvWVo1rt z)A>f~xvpCtia1k(uE6Wt1nG}S^)M6$3*YoDEEun@GNTfvBJ(~@a{NrgXO#`9<$|nx zT7Xtig6X7U55As@5AjLI;Av+`O#tvVUxTp)Zu&v-Z;U=7jR1nyx!SZn^^u%l`W&Ad zEfeqxf3hX>^Zu%2koI{ebQ`39f74gadd6*THv4yr%Q+6?=R67}Z485-B1M@B4%z;o zieuVwA?W^9q1S<1p@#<~aWqRKt(7x2b;Ij>OjbN2tm-kF61>IYspj)9+~meSt!?hO zlM8UxPlP{u*d=(cbpF%)T~g1+7B+sd&{B3ty?;N;S7tkEcEg)&Su|J}XCXB)HtyiF zP#hy*Xs6Zt2%k^eX%I*rfjc;xQ;77M`oF%%unO*=^})NlM+ad( zZ?n5#GcrRS3cf!PEiPd-@rt?--K7XASNW;d{;TmFQ@kGubYHtEa0qy8?s)>ez{ICO z0s?gb zk>sM2<{z8))s(UoGylH7|JtxPWEl1933k1{d9A|XLQ_4Is`Jq_}w81Gx&TqKI~6a zb{e??w?uzP0awZ@FbMwqqb~C`2g1&}c4Q5(QE#hlJd@nW!-}~H2B`Z(B8(1rh}cau zf9biKoVM^#37D70uq{#*l!+-D<@yCKPBw^31{I(>3{ZY#HNP*Svh*?eO#0X@n5X-kxt(!S9K-{k}9& z!>PY|L$y`^(8B;OVdN5Mx>sd*t)TPcVF7cKt6&zt$D^!00@8WaA%US6$a!WxAiG+z z)-{S8;1Biq$ zL7TwSurw+;HPJ^QO8!`yM8PLeEc0qtmg}Bl084v+i0nNM8+>GSx(~l_A@_Tv zXr>S|=|Uw}eT5+nAS%0C?q$}GpeH`IS05GFgz<<{n+w^jbCeKAGl9&OiMm}|%P0Yi zj{Tl5Ing0s6L_0`e|*_(BW>@X_2sX(&G3NAWyFfmCOt$IgOeiCQFlIdm~b zGnB;%pi;>rva2c!W+ZQu*}5=e-%s{hVEmJdjsY*^LXIxdYJx93ky~~>aHoii#RnZj z3*}Rt&?fPFvK<$&%uRK;N>5g6g`sw~HNR@k zgX3>kY8j+QV(&953H3VK=Rid*`f-!b!rL1;dU~o?<>#86$1sNJF(JyO$8MlFe~J=WJ03vnG9Obh~1B>fYTgH zGCvrW0d3pIL(tHRkZY<|yFRCRJ{}?WM~k6iT6Pj}v96bblrC8#EGS^QrYz1An#u8! z?#D8u@AixP_GO)fnxz$_*UcI)OoEZR=CboPK~M4TJX46PFH?5U_w%~O;38%$4)`ZV zq0Tmv)~8CC%C8igiS1Lbb0upHE4rBYaKftUgbTsIS;Bqela?Fvce>4#&q%u2XgCTn z%Sai`4oIs5U?pSLU3Z=0HrcgHk<{XIJX;31gKDqQt#6#{T4^j9no~RPX*GL2G-fST z_t~G+e51QKUmpp9{Kj-AJM4Of6%D3^VJzMPHdd_{=|<)n(E!9>QW}5mV-C%uX|*n# zNcN(?S8JYL$G$9ru{-U2(g&{(L4#N2u2b0u+e!6w`VOvb3ZS7;n`)eG1`mne^M=P2 zI$0p5P^3~y64HFt#9s0#>^($L=Vd#p!+6#~T4SJU#211%iG@n3GDy{GX~W?-t!SGj zEiw>Q-HNZOe(Yd|xQVl>H6h!WvLieAqH{~n+R$O(39gc?H_RSZ1J)tCpZk{&i0pDG z?Oz;jrW}p=T_pn(-JU4Cd%8$NvZ;J;!#F4m$ZG4_dW(S-2?V@`Vvb)S-53h z8XHtyCr4&QJ|zMuO}`;_`mr4uv1-`jm|*M?_(RL{Eh*(dBo!sW5!F!Ik9Th1XZdRc zcPE%!D!+u)R=N{yX2Ba1Dsz25Y?q;Q+~EiOGzp~~6y(na8Z@tIRTH4)}GmaX7xU#Z$#$i>(9WU`a^8jjNKWPPzKpTfsluTdlqLffI2er=Vv zpO5Hca#DTBj2%*A%2xtgdMVEzPeug=WQI2;92C)_r#+iWf?h{kc|xmUZj_bzBNPDs zRdp;vz$zvjksF$3Ou!3>SdhbQUIZRLIWEqG{c8J7;Sq;-s|J;@gOi3_)dsW^{2+jzKE@EeWN*w}?32duPoOSkN#8Y+S| z!uC+#kmMjMWEgq0kn|1bLeFz=ROdO61a#ARUFzXIh5SSXDONHsKk_GSf!u1aH3(bT14*Td{l{9qHV^LiSIMpTZ>V0`6l z;J+R%oKotRU>y|7%i4c|hQzG1z*zK1cNuy`rzaeMJui-d(tbMQ{0IQw7Cwto5U(bA zrlsm?C09AAdYK5(n>G!ngCz6H^3qZjQX>4}{L)h<>9T>H+vNE`aqv!DF>M6HPS0%k zivvFwX;wQfG8wMM7rk+?*3Xo_5yMNGIR}LVM!~HekbxG11I3!}0wKnqD3n9*eEH{I z!IG*=H!(iX5hZ?|sa`N9#@>)n2+QjhO|Kk;Va1h>XsA34xam>WbQw7*bWpFzc*I%7 zG@0IoA(TV`KA+AAl&=Yf-^y2dXZe-aLtr>g+I&t1pD0@E1Mmkz7!v1gWpwJSE{Cx$ zXc^HeZqQb6-%Og#s}}1s@p(H4YG8S%fh-^@mCV(h5WWRO=~U$5UkMKO83@FNS$*^- zo8^Qo+Qf!ujG&-XczZP-_#9*X7~#c- zp;$*VxjtUxPfE@PHw&}6*eKe6wRfYBh_q+i%J+#qhLYa@+nS?#)ilUw@X8ow6C&W@XB>Xh~S zN*xPS6QI4;@74r1J_r92pQpm7;cV{i>*u#)=?r#B+8RE(i(}zk)W1(WHKbr~3q2;; z=FcH4t4*_G+kSB)9Ur%Jt@97Ndaf_VuhL7{zKe>gQ4oB6G&awj23VwKDmgN~*nOY9 zbt>+0s?@imR3SY-MYgr_^Mu+4yc{uYt>sEHQ+=-2_Dz9Z{s=;r$MQ)I&yopH1ma8N zRB)1uWIKU&6q!U*n>+mBru!_Ss}$btDKJ5`H^H>1Ek9eIIM3!-8-+_#Sl%Ps$n`Zt zfPW(2RSi=!2pqB@&}z;P&GusZ1^8DYXRahxBT6 z%t^dTLp-NFMCUM(VSYnmuV$s3d>1P$9p5ZR()+a@j30qDa;c8S0>4Qvu%CxVVJCLg zt3aEhy6^e$If+SFI%nZ)Qihx6WXKvrcc#@iTAn?t! zq>dhteak{Le|L`Y3CMOoxP3E4KIswd!X-`Z-Gh0{k@3N8l(9$)vI(nAjz_N1QbD(D zyyeB!(LcmKb(@UOMvf*4TQA)km+b!xlKco|Hl!61Vpz0ctCt!cSzE_t6!*>Q)rTycYVs?wPPc?dv zp`HMEF!Z#W?DxiwY0%=`RnvQ2QdK5Cc#B!FkKl$W=Dg7x@)_` zoSxn|{6Zlh#Atj)d-Gj1B4p}7|J*{RGX4B}AyqxfAAbRyYaVGxSxg-5^{!2LMRmK~ zWVm)l&-NhJVc$SakJ{0}7lRYtPSrh5w?IuyGcRuqoRbruWqJv&i`Kp!E*VU!yzhBl z;}l=MqHSk=uGomU%%$EydO;uqEs&&D3 zQ>Tlc8Q17XaXMhwj*|&pGaZrnJ+8z*%$Y>e1Y7U>uF0^v3hlHTvZH9>*0&I2M$T?b zRKwGS7|CV*)==$!Vtg6W}4Z^pps9tnGC_Tijmf3;#8^_tN-G}rZr(P?gghSD=T z4=$(f-8#oeHkdfu8(C+YadZqp`<*x0?EY2%@2Siy!dPaFWWXne1wVIZEH;3yrrn!J znIYr`BMu#1;?tVp*MtynOw?@XcU?5fhfKBqVPmaOM>DEC$91K-7HIS*QS(EM#+%i_ zxsnB>wph5ex#sKh!4n5KHgGlM{(HBs0npJ5L}ICEn**Rc3P*3|A{Z1Xm}IFl~FkltjGfG z0|j*Lofnf*$x4`T9iLv;7vkS~fnL@mgrc2Q8JeI~X1XkOtdCb&cObownhf({EbsVo z8OdSxkcE7j`4E{7V?EQ|myum?xzWd)q1PF0dJ2?j-kEGS3zWKM_U*OuS4BO}T|7a% z{?R{k#5!a=Ga^bgnLnOii^l`R-E$Hx9Ft^39PlW=nyo2JbLldzoDDAa;wccd63as@ zGEByTaNj1jOQwtcCg+$ZiVs#VH|pfGi(XfDn5c6^8sUe6ho$Q^NF-O(&X9N&j=b7r zXIiywT^u$M+Mh8jPd%C3HVJPzb|7Rv+PYw~gG*?$YDwO4UauWZufy9|ylEl&nr8mA2SmyjfRC94r{< z65z!W)1@ZfzVvc0u&w$<9IiDavJPjdx-0veJg!yly|S#oSij7~;1O<>?ybw9M^?3Q@Bk!w-0DDlWWoYmP~ote>@6MYmhM(_>ef~T3pNoQrBSXtK#+y)+eYO2R2 z0iE(+9K%0>K+8_S>4ypH7g-!2HyDCT3vlG-&SDx1K~lM^cuWb_Gk$8sq9~M_Jm@?^ zy?sbli^Ac86t`j-)@8=cdqgTk;2oq|+s(rlHZSy;`tdnT^-;Q^8!5E;FN3bPQTX>D%#Z8GjurGnl4Ul!y{nact z=wNvzx^qYR=z{85gI(BD?;>tFkJYO#Kl6Wx4Jkmg)g5})AhxZri+OL&TYtE1?Y8Fo zY%09c2;)GD1+FLeTU{+};_o#44V;i$A2-BWnfo8d{sF=(W~|WbXQ4 zNMJxpSm5--hiv#sA@{Nnq6fewoY3K#t7Gbnt2R4ygFh@mLgf#Kpp}T3z5*A}$xD~^wg-0*IY(t)E1GT(lX8lt~)+%+gN z505)PWJ0`YBy<}p$~bACOsYCHH*6A}yHgWEZ5G_5OwA=`h^#^lx$URO$ETg`&dA>8 z!O>?z?Tsy-qs)N1cdG44{%r8lUz$jpEHSt-X3%DKF`57|7-p=ffB+)-C6Q4z91dKq0D%QJ^Phgzkq-sHKS zM_rDz>(fXztEa`iC-#k-07D<`B}dA6sIp?z-f$OM{>Fx?_iIwQF%^FKF@a|(acT0J zsp&aNKA?v=l-)*Cn}}ys;MOs0YH7AHpI1KB5KYj?4jdJz`+eVcjA}NuQz6THIR%co*V*l;E1-1pK|=BU7b4oS@+=F3HgY6 zFLY0uubGls&&Ddck2fPrrhD1wWs$2Da+BC58x!AbHW2dG`n)1x@%M-#B-YgsY7+Cc zklcIzvxXb8<@hn-6sHi*OElx9p#V(kk|EouAjn;8iOq(BX8P0?j=(+Lp^7O-PJe0Z zQtfs&hZq_j$$|Bf2iJlD9z;mS9f^0Bd5446ja~n?j=vs-=`hGzr8?)b!Y5PUp*RYY zuG)caD*sn}?-d`c?|K_agp4+CMS{#^iQds)JQvh7I zeVEuWoZopQ!J2eRMfv8B^_NI=z$Nbd2XnxYm^ISZt=vSm$RarCG*3%t(iTmjNn-kZe{NSf3jWnd@XW{GwQ1cBkEno{5C94 zN57>L=O?ixnUJ5iy4P80$>C*}K_=7r(A~293BynWiqbv@>#y8u2#N$Im4r|va3nO< zbP-s^hzdFi2h?3Rq~^8YxMTJBQeT-$mKk&D3v#(`do_Y8@M?vL?!_!U%^mB8_kR32 znzR}o@Xj6RdqSx&lf~$*d8r2zy<6R{dwr9cQFX%EwkuDU@zqQCX}cxqOvKkq?y_3P zT#)RURMvSpEAI`B#EIc5_(};c7~6lNT4+ z=b0;&j>#E=c&>#`T^;q*;hUNxGJ)QB0k?u3x;XaiBB2DWSR7`lcdiu{HXQ9Dm3o|c z_+yx<5d)r*dnUZyw=&0oE{}+2^;hq00RQ=fC0`TXGOmPZk5afswDljfntoi0I+dgm zXY9A;HLh8~>3q#pgCN16$+p{nSi$A*1R<*&@odIKo0k%u2k1k-O=+By#3*xG4V$N zYS^Na-sdgtO;cyr868#BgD+7a7?U_2q$xVQh$9wX%~KI&$%>sbm-EfI%C!ugDK?do z+tD0bVc7_e0XHkSqVI$o#CK!LgN25-nwNNl zH(cffgyjLe9F~ZYc~AQ6EX`$$gR&sWsItvaausNisZ#j%BRCr07GLV(T0Oc+Gn#H1 z_@&lMK=512!_*1y$qr3)BG^bX884cj0|>UqpEPLuFyfwU@arIEuV3!7=}1e?1%<~W zyg@7Q$3MK>>5)AHZg!+RM9~gyc@FL>EiJM4&F$OnG!@MBhjZHqSuE5=z+nL3@7+jq ziFfa7@eq$EYEc162NLzK#PnI8+yt>z@tIcy|SYP;^ zJ@sn;8*Rk)-)zbMpEi=zkQ5hG{!eYh_`laiOw9kGjTi_x*xCQ@V55JkMP2+qjSm4c z69W{Tkhy`Ql8H0HpMRvt@P9ENlYf}!e<~iw|HL(}|6fw`AA;upMQYgp+i(9rq=t>1 z>Aw&SBLM>gJqP=Leg1b-!@$VM!1AA{=KsLdyn@Q(Y%KUk3fiyg3xG-a?;&l`v~^Ab z1WoPT)bwwYs@uDTA~3gglD2hfw`4WGzVB9cw3Y?FZ+KPTr~tFH7eEEoQ4C@H0R-e? ztfOaq1Q0$>(qIRinzD|XnyMOGTB;xrv)bD=G`4if>js7ZI(bhGDC3zMyH_DIJ$kbw z2L}W$%B}*WwgyO%bBvc|_zl5`@;@KRGG-UBo4@25tgR#|Qv)6pcaS&s>cn6I}s74TFR@JA2_HG=HPU z!L{*7OVifY#)!7Y!61A&0cv^zK#wZ~<;Oe&XmSI~0Qxb zYlj+|njap+gK$Sx50duR4^@LFucMg5yUW1IBO-;CuLBMIWlZ^=^vAnmN;aDPbw0yZ|m+C1QU?8gxQ z3xDFOk(<%SrD=5^0(#x(u09x0X2$pY-||G7L@W&BQ;;O2&&?y1zhALaIA;)h`lhBQ zM<)OP*8u!#$j~Kx1C<@?K)=cjUxdEbeUpP|hS2n0Hb7^fjKDoV0x$OTj$lAI*V+EM zc)zM&bcBs{0MY_vF#tw+91u}oa!+aub65YO&E8&dRQ#duE-&f;Q+{7xuZcUC?&*Sk zE4IIBzaG8P7Y{@U2a@=|7jM0sh=^fSeL(@yuzh_K{V;o`rvUa1PQYD1Y!iN;zjC*J zlBm)Aeyby2a{Lr^m>_-MYS6W2ueq#Qzd2ADy=)<9cfFWLgtdh*Aah?#?;TS(Q)IQh z>OXw5zqz!(zau}fe}3%}fBm2n>>FBslw=-De}2VmjDZ_mKR5SW60&jhS_R=AImS{bCW12ml>HGbjUb{`Dtd<^OICSh-svV3a{P z0DRxC0AQ%6rT@m>xa>+->Fy$ju6BMXfqLtd{>o6mu?D35)Vo#T3itNE9G70Cc^3)f(YU0=>XJTdjL)myo~v6osq)% z{p8C5kgfZLA@`BI>Kzz@(Np{gRRNG4`<<~wO!$R=F3+CfAAr%Pf2Fdw=|UQQd!qct z*LB?hBH!t~VXa^N8yjDGV{rNnZ|FTOU?1o`rY-*lS>q3SkE-D#d_x!OQ!S(JY8vDN zoj1+ki{4wt>d$|SeZ4nN+nC>hRmQBJ;93**-=M16@U5Sr-*yvZa<9+T)!SJfZ`^?2 zyIeUMgfl2+0o}CVv^y}(6_A!~Rmff^gEh><+=88X@qCjz^*TGZ4lyzDqUxzTy~bWN z*{}Ja_AXLcC(dBV=9yh; z98O>WLA!{7A&GEb%Be=4mY-2vU*4e8ov*}!CQAi2_ zXM7RCHn>r@f%|<};l)Hx4-wPhs$?FC+!B;LnT;i7N;+yJ8t<)8UmV);2lW~xw zS8!yY14p)%U`TERHX`lzV$NVtY1GmAf}P|3g^XVX1Vb=^wOt-pt#zcZ}mp2$u6%Ohw9Y8#Xs6mw&r=(%HO zF?FR^|M}ff;lG;~X55F_ZFvooe(xqcs4_yR!t~~rdsA;NeH&h!)SmzM&0NY1 z|1tL*IJ{Gt=+MsllGij7JDP?PUuozudqe!gbm0R*&SuFFA^8~tLWpqhsZ%u~NZc87 zcp83|{4(ZsA#?x*_r=}7da9CYlMyTiU1wOOKna<)v+JR3%INP@#&Q4S*?FdDFo?gY zXLSyYs(3O!ic6;ND@O8CiQ2P%j3y|u?M`OPrp_bJg6r_$D?;df7@1w~qrLAd5>JC; zS~BKkr1~~sQfcjdJ>Gso5S!4x8^+fRGz<{mT$@9EFQ1_oGz+0{ntsVe^o0a+H6o0k zMeU)Oq1X&ZrW;`POuVlsRfnX}aN;c-LkAKY3m0TK#HmBi3uaqriQkG4gxpDcx`w?Z3ew~*Fn=e$6CngSL@zKc_J?bAu z|5TgK;Wcs~d`}TQ$qEVpfF27z2kE*T-jg^j+#MM5v_4S7U6k}|y9r-K71@72GjNJp z|E%IyhY!<=U};wHR00EP^^C?Go#reWp#|Y1o}H2BoU}7A(`>Q#yr7~IeX}z$P<$cHBH0S!o?VHHdrJ2ArFqz+*(eYTw8`mqNjyssd z{zav`h-QQwy*fB;(Cf%y5JOnw`kUz!v2)wQ(TCAYno8Rb}g&nnlYEC;1kv z980X|gUpLX5?Cm0E@d&_0TdhDv)fK#REOrcOyTCyy;n|#)|2sJLl$WJ6jtN6-{hq@ zzg$9GIg#W1do2pG%AR-zjUO_uN( z{4mJnzFjbqK>Z|wvRVJ#pl$&dW2bU=azmfR;CqsgJ1I0OV)4~OEzLu{0?NR6P)DpPKBz5SH$Yo7e#_Nw>bUdKFrdPu-5O*rGVf$X z%aFNf85ntZuro9Flu-5wNR~_MiYu87AldGecujaJ`8jAuzV@fsrt0T=4;>SzeFH|x z_%MFy#rDSXjI8vH9$ckb47&Ww1z$0ffM+oouLnxmRogo2Ay=(fZ65J!_I-L)Lv}jD z=xZV_(qfS4Z};*P)N?0hL2ZaKyEM=Z>_`^0h8#ZiYZ1i$`Cu78SDfAzJhB!KZd9Za zaTREjAlC6a{?T%e4XDB+T(BPX2&j+o33@3_aiNN|?%X)rX=_bkriw;DN89JkLP|br zD7sm75yQ3UD@YM&kx+dgp8A4`K^N?-aWTI0CgDy&3L}~r)lNALW+KBS>syNu5>rHK z$OA9~w8}u8iLW|7yoPu1<9x1scQAMsa9-NS=y5_OKsku#hctmY6l}wn(qtr&v3TId z0XP17)cA{mz;`y81Q#^&u1%-aAY8~vq`9VR;+8{L$rYMND!;^pBrQxLh`DhcpukM< zAiveXZVf?x_SFnXVDAQ|vBg#a_dKULE&Wz=p49OmP^H!jQ0sc^bU} zpQs*DmQrD;RrcmWQa+UHB>Z=y?7P{Fjj1zGW*^G5^>W7{wocj5b!Bw-1Hm?eEM`JB zK`Kb#t`lbN@|LmE{Wp?S1P=M$>+}%EDd;{#(;0t=lOHe1fmJztVZUA(d6jecPI>A7 z^~)yVLGzpZQ4_*xuZxovI7(@;$8W{v!Zgr&t7zm5OM;6#P4}V|N`Ch$_H|6UV6I%p zX|P4#Wfx=I9=-KvMwg6Xu-G|pD;<9W_g#h=0Eo5P+&XskWit`WPiGPO*{T~+2nXNZPif@s;!LfEHbM-& z=r@TnXXGJ^6}D-bSr3cw+!DD#oB~2Jd4L` z*Tx(YabWu^D|Tr+rbccvXcwhLhXmV-H4Q`%N*`>U(}a!esOwQd%EuN7*`Jej>Rj=j zW~nq*-v8JxyBqRl3u(2Dm1}lBOsSI-yYA3hVt3vrFbTCw_^hgr$lFE?V5FV&MYTcF zx8kR!UXQub~sC(W-TKfNC2}U=6{|3 z71bj#YeF#t%(!=C(Z0~WiXDR7=HlV}&K}f8G-QC`(q};%53$xrYP`GeM+!eC_vrt= z<>O6*NWkc0KAU1M+)i&Rdx+`Kim?n~^`AN?xgU!G-eLU#5^L%U4bW&QW^Kb9uCF(g9JJql{88e% z81t`te)`2{fsc7)6YyuDiqiG4r*O(Y=gkBe?6;;wS{uwjM-zp&U2)NSI^t{O;_}V&*TRDgC>xzv@9)3V6&DA%&DVAU639)K1*L0i}J{RNcOjylUUYF=9bk!QVo zdqsSQWQTCFcZd>{W%XJaDXq&Hj^7BxJM$`A04{94Mw>?xs@CFoNwde&JsyLWKh;gW z+U+5Q8ymFYWx-Xqln*5G_NJb;o`=!k#*RVGkC^eL9_wUBJPY2Eb8@BMP|S{6!2MOW z$YwHno<$0Flk{_zaO`!kbgPvxVIpwK9M8|+>Vw4Afv{pY~QufLzbd{E) z_-WfYKCQ!ho=(+;do-|4&*FD;EIKOKablWwRrs9-`Fc&D{HyLl%nx78g>$r+c=GYo zN~xm3n!$jw5qXSuG2g=L7?iU&TrHu~By2y;ll?DeTj^qE*X|CW7;hbqUEx6v13#Ii^Vc9i|2Yy{|?_m>lmu)#{p7GqQDh3s2P#+7HqyL4S=<`iEDGWFpM z2Ie0njv|W8rSP+yd;v=3={p%3vOGtK9MTSHDEQEcrZqnl$9SvzY2Lw*ST2oJ{1PvQ zAT!RgRhV(4>LryBtvIJ0Phe`W3;;Dbu!^rDa8N9ml#l*k_9x}E(hBgA&IF)70&*T4 z$h4JRGs@~Hf!3IlZHj(K?tGDko0)jm9WCb{GNcnDphi;ViY@5gsXFN7X53??}{=4CVXS z>@Hcg2$�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 Make sure you have correct hostfiles in GRIDS and GENERAL.\n Please, edit the file named "hostfile".' - while (1) - echo -n ' Proceed with testing [yes/no]? ' - set ans = $< - switch ($ans) - case no: - exit - case yes: - break - default - continue - endsw - end -endif -echo ' ---> checking whether to make library' - -# -(cd .. ; make) -# - -###--->>>GENERAL -if ($todo == 'all' || $todo == 'general') then -echo ' ---> make executables in examples/general ' -cd general - -echo ' -------------------- CREATING dd-HB-dse.ex ' -make dd-HB-dse.ex - -if ( $part_name == metis ) then -echo ' -------------------- CREATING dd-HB-metis.ex ' -make dd-HB-metis.ex -endif - -if ( $part_name == parmetis ) then -echo ' -------------------- CREATING dd-HB-parmetis.ex ' -make dd-HB-parmetis.ex -endif - -echo ' -------------------- DONE ' -cd .. -endif -##end GENERAL - -###--->>>GRIDS -if ($todo == 'all' || $todo == 'grids') then -echo ' --> make executables in example/grids ...' - -cd grid - -echo ' -------------------- CREATING dd-grid.ex ' -make dd-grid.ex -cd .. -endif -##end GRIDS - -##======================================================================= -# END compiling/linking - begin running = -##======================================================================= - -###--->>>GENERAL -if ($todo == 'all' || $todo == 'general') then -echo ------------------------------------------------------------------- -echo ' ** running DOTESTS on general. ' -echo ' ** Partitioner:' $part_name ' Machine: ' $machine -echo ------------------------------------------------------------------- - -cd general - -#cat extras/inputs > inputs - -if (-f temp) then - 'rm' temp -endif - -echo ' ' > temp -echo ' TEST programs in examples/general' >> temp -echo ' ' >> temp - -set count=0 -foreach meth1 (' BJ ' ' RAS ' ' SCHUR ') -foreach meth2 (' ILU0 ' ' ILUK ' ' ILUT ' ' ARMS ' 'ARMS-ddPQ') -echo ' ----------------------------------------------------------------' >>temp -echo ' --------------------' $meth1 'with' $meth2 '--------------------'>>temp -echo ' ----------------------------------------------------------------' >>temp -# -# -#echo '------------------->>' $meth1 'with' $meth2 -@ count++ - -set infile=inputs -# -if($meth2 == ARMS-ddPQ) then - echo $meth1' global preconditioner' >! inputs0 - echo 'ARMS local preconditioner' >> inputs0 - sed -n '3,5 p' $infile >> inputs0 - echo '1 Nonsymmetric ARMS - ARMS-ddPQ' >> inputs0 - sed -n '7,28 p' $infile >> inputs0 -else if($meth2 == ARMS) then - echo $meth1' global preconditioner' >! inputs0 - echo $meth2 'local preconditioner' >> inputs0 - sed -n '3,5 p' $infile >> inputs0 - echo '0 Nonsymmetric ARMS - ARMS-ddPQ' >> inputs0 - sed -n '7,28 p' $infile >> inputs0 -else - echo $meth1' global preconditioner' >! inputs0 - echo $meth2' local preconditioner' >> inputs0 - sed -n '3,28 p' $infile >> inputs0 -endif -# -echo ' --------------------' $meth1 'with' $meth2 'on' 'dd-HB-dse.ex' -echo ' ' >> temp -echo ' -----TEST-PROGRAM DD-HB-DSE.EX------------------------------' >>temp -if ( $machine == AIX ) then - poe dd-HB-dse.ex -procs $np >> temp -else - mpirun -np $np ./dd-HB-dse.ex inputs0 >> temp -# mpirun -np $np -machinefile hostfile dd-HB-dse.ex >> temp -endif -# -end -end -## -## DONE -## -echo ' -------------------- DONE ' -'mv' temp ../OUTPUTS/GENERAL_out -cd .. -endif -#----------------------------------------------------------------------- -# RUN TESTS IN GRIDS. -#----------------------------------------------------------------------- - -###--->>>GRIDS -if ($todo == 'all' || $todo == 'grids') then - -echo ------------------------------------------------------------------- -echo ' ** running DOTESTS on GRIDS. ' -echo ' ** Partitioner:' $part_name ' Machine: ' $machine -echo ------------------------------------------------------------------- - -cd grid - -if (-f temp) then - 'rm' temp -endif - -echo ' ' > temp -echo ' TEST programs in examples/grids' >> temp -echo ' ' >> temp - -@ count=0 -foreach meth1 (' BJ ' ' RAS ' ' SCHUR ') -foreach meth2 (' ILU0 ' ' ILUK ' ' ILUT ' ' ARMS ' 'ARMS-ddPQ') -echo ' ----------------------------------------------------------------' >>temp -echo ' --------------------' $meth1 'with' $meth2 '--------------------'>>temp -echo ' ----------------------------------------------------------------' >>temp -# -#echo '------------------->>' $meth - -@ count++ - -set infile=inputs -# -if($meth2 == ARMS-ddPQ) then - echo $meth1' global preconditioner' >! inputs0 - echo 'ARMS local preconditioner' >> inputs0 - sed -n '3,5 p' $infile >> inputs0 - echo '1 Nonsymmetric ARMS - ARMS-ddPQ' >> inputs0 - sed -n '7,30 p' $infile >> inputs0 -else if($meth2 == ARMS) then - echo $meth1' global preconditioner' >! inputs0 - echo $meth2 'local preconditioner' >> inputs0 - sed -n '3,5 p' $infile >> inputs0 - echo '0 Nonsymmetric ARMS - ARMS-ddPQ' >> inputs0 - sed -n '7,30 p' $infile >> inputs0 -else - echo $meth1' global preconditioner' >! inputs0 - echo $meth2' local preconditioner' >> inputs0 - sed -n '3,30 p' $infile >> inputs0 -endif -# -echo ' -----TEST-PROGRAM dd-grid.ex----------------------------------' >>temp -echo ' --------------------' $meth1 'with' $meth2 'on' 'dd-grid.ex' -if ( $machine == AIX ) then - poe dd-grid.ex -procs $np >> temp -else - mpirun -np $np ./dd-grid.ex inputs0 >> temp -# mpirun -np $np -machinefile hostfile dd-grid.ex >> temp -endif -# -end -end -## -## DONE -## -'mv' temp ../OUTPUTS/GRIDS_out -cd .. -endif -echo ' --> TESTS COMPLETED! ' - -unset machine -unset part_name -unset np -unset infile -unset count -unset ans - - diff --git a/lib/parms/examples/README b/lib/parms/examples/README deleted file mode 100755 index 8fb4e5fa3..000000000 --- a/lib/parms/examples/README +++ /dev/null @@ -1,19 +0,0 @@ - -1. Directory 'general' contains a few test drivers to solve a distributed sparse - linear system using preconditioned (F)GMRES. The test matrix is - read from a file in the Harwell-Boeing format... - -2. Directory 'grid' contains test drivers related to simple 2-D and - 3-D centered finite difference elliptic PDEs on rectangular domains. - -3. Directory 'petsc' contains a test driver to solve a linear system with PETSc. It also - contains the necessary files for pARMS to interface with PETSc. This allows - preconditioners in pARMS to be used as add-ons to PETSc. - -4. In each of these 3 directories, there is also a directory called 'extras' which contains - sample *.pbs and *.cmd scripts for submitting batch jobs for PBS scheduler and LoadLeveler - respectively. - -5. Directory 'matrices' contains some matrices stored in HB format. - - diff --git a/lib/parms/examples/README-DOTESTS b/lib/parms/examples/README-DOTESTS deleted file mode 100755 index 37139567b..000000000 --- a/lib/parms/examples/README-DOTESTS +++ /dev/null @@ -1,37 +0,0 @@ -NOTE: DOTESTS is a csh script - -Check installation -=================== -If your architecture supports an interactive execution of parallel jobs, -you may be able to use the DOTESTS script to check the installation and to -observe the effect of various preconditioning options. - -%DOTESTS grids - will run all preconditioners on sample grid problems -%DOTESTS general - will run all preconditioners on general matrices in the Harwell - Boeing collection (see GENERAL -- and information related to the input file) - -%DOTESTS all or %DOTESTS - will run both suites - -The results of the runs will be located in the folder ./OUTPUTS/ - -For non-AIX machines, you will be asked by the script to provide -hostfiles for your computing platform. You may terminate the script and -create the proper hostfiles. - -Script tuning --------------- -In the text of DOTESTS script, you may modify the number of processors -(first line after header comments in script) - -but remember that the number of processors in GRIDS should be a multiple -of npx x npy -- so np cannot be an arbitrary number. - -Also, you can modify the option -machinefile which tells the program where to -read the hostfile. On MSI blade machine, this option is -hostfile. - -========================================================================== -Report any problems to: dosei@cs.umn.edu, saad@cs.umn.edu or masha@scl.ameslab.gov - - diff --git a/lib/parms/examples/general/README b/lib/parms/examples/general/README deleted file mode 100755 index 85b394f76..000000000 --- a/lib/parms/examples/general/README +++ /dev/null @@ -1,132 +0,0 @@ - -README for tests/general directory -================================ - -This directory contains a few test drivers to solve a distributed -sparse linear system using preconditioned FGMRES. - - * dd-HB-dse.c is a test program with a harwell-boeing matrix - using parms's simple DSE partitioner. Each processor reads - the whole matrix from a file [The matrix is assumed to be in the - Harwell-Boeing format]. - It then partitions its graph using DSE, a simple partitioning - routine, and scatters the local matrices to each processor. Once - these submatrices are received each processor solves the problem - using preconditioned (F)GMRES. See README or comments for the list of - available preconditioners. - - -The command -%make dd-HB-dse.ex -will create an executable for the corresponding driver. - -To compile the code in general, make sure that the makefile.in in -the home directory of PARMS has been configured properly and make sure to -have the library generated [make lib at the top directory -- see -instructions on how to make the library] - -All test drivers will read input test parameters -from a file. The sample input file called "inputs" in -examples/general is read by default (i.e., when the executables are -invoked with no arguments on the command line). Another file can -be specified for the inputs by entering it as an argument at the -command line. - -Example: - -%mpirun -np dd-HB-dse.ex -The file called "inputs" will be read by the executable. - -%mpirun -np dd-HB-dse.ex my_inputs -The file called "my_inputs" will be read by the executable. - -------------------------------------------------------------------- -The other files in this directory are: - -- skit.f, a suite or routines adapted from SPARSKIT for processing matrices - in Harwell-Boeing sparse matrix format - -- aux.c, reads input parameters for the run from a file - -- inputs : input file for various parameters - - -NOTE: The functions defined in the above files handle both the real - and complex cases. See the driver for the preprocessor calls - to the respective functions. ---------------------------------------------------------------------- - -=========== -INPUT FILE -=========== - -A sample of the input file (see also file "inputs") follows - -SHERMAN3 The name of matrix -ras parallel preconditioner (RAS, SCHUR, BJ) -arms local preconditioner (ilu0, iluk, ilut, arms) -0.01 eps(tolerance for inner iteration) -1.0e-6 eps1(tolerance for outer iteration) -5 nlev(number of levels for ARMS) -400 bsize(block size for block independent sets) -0.2 tolind(tolerance used in independent set) -60 im (krylov subspace size for outer iteration) -500 maxits (outer fgmres) -5 kim (krylov subspace size for inner iteration) -5 itsgmr(inner fgmres) -10 lfil0(ilut, iluk and arms for lfil[0-3]) -10 lfil4(schur) -10 lfil5(ILUT L, ILUT U) -0.001 droptol0(droptol[0=3], L, U, L^{-1}F, EU^{-1} -0.001 droptol4(for schur complements at each level) -0.001 droptol5(for ILUT in last level Schur Complement) - ------------------------------------------------------------------------ - -=================== -Matrix Files -=================== -The files, matfileReal and matfileCmplx contain a list of matrices -to be tested. These matrices are stored in the /examples/matices/ -folder, and contain both complex and real-valued matrices. matfileReal -will read only the real matrices, and matfileCmplx will read only the -complex-valued matrices. - -The list of matrices to be tested is terminated by ##. ---------------------------------------------------------------------- - -========================== -PRECONDITIONERS AVAILABLE -========================== - -The following preconditioner methods are available in PARMS: - -BJ, SCHUR, RAS - -RAS indicates restrictive additive Schwarz preconditioner. - -BJ indicates block Jacobi preconditioner. - -SCHUR indicates Schur complement based preconditioner. - -NOTE: SCHUR require inner iterations -(so ipar[3], ipar[4] should be > 0. It is a good idea to set -ipar[3] = ipar[4] = a small number - e.g., 5) - -All above parallel preconditioners can be combined with the following -local preconditioners: - -ILU0, ILUK, ILUT, ARMS - ------------------------------------------------------------------------ - -======= -EXTRAS -======= - -The directory "extras" contains script files and sample input files. -*.pbs and *.cmd are script files for submitting jobs for PBS -scheduler and LoadLeveler respectively. These need to be edited -before being submitted since they correspond to the specific -environment available at the University of Minnesota. - diff --git a/lib/parms/examples/general/aux.c b/lib/parms/examples/general/aux.c deleted file mode 100755 index e32c3c873..000000000 --- a/lib/parms/examples/general/aux.c +++ /dev/null @@ -1,300 +0,0 @@ -#include -#include -#include -#include -#include "aux.h" - -int read_param(char *fname, char mname[MAX_MAT][MAX_LINE], fprm prm) -{ - FILE *fp, *fmat; - char buf[MAX_LINE], *tmpstring; - char *p, *p1, *p2; - int i; - - if (NULL == (fp = fopen(fname, "r"))) { - fprintf(stderr, "cannot open file %s\n", fname); - exit(1); - } - - for (i = 0; i < 18; i++) { - prm->ipar[i] = -1; - } - -/*----Get parallel Precon Type ---*/ - if(fgets(buf, MAX_LINE, fp) == NULL){ - fprintf(stderr, "Error reading global precon type"); - exit(1); - } - STR2U(p, p1); - if (!strcmp(p1, "RAS")) { - prm->pctype = PCRAS; - } - else if (!strcmp(p1, "SCHUR")){ - prm->pctype = PCSCHUR; - } - else if (!strcmp(p1, "BJ")) { - prm->pctype = PCBJ; - } - free(p1); -/* --- Get Local Precon type ---*/ - if(fgets(buf, MAX_LINE, fp) == NULL){ - fprintf(stderr, "Error reading local precon type"); - exit(1); - } - STR2U(p, p1); - if (!strcmp(p1, "ILU0")) { - prm->pcilutype = PCILU0; - } - else if (!strcmp(p1, "ILUK")) { - prm->pcilutype = PCILUK; - } - else if (!strcmp(p1, "ILUT")) { - prm->pcilutype = PCILUT; - } - else if (!strcmp(p1, "ARMS")) { - prm->pcilutype = PCARMS; - } - free(p1); -/*--- Get tolerance for local solve---*/ - if(fscanf(fp,"%lf",&prm->pgfpar[0]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Get tolerance for global solve ---*/ - if(fscanf(fp,"%lf",&prm->pgfpar[1]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Get number of levels ---*/ - if(fscanf(fp,"%d",&prm->ipar[0]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Use symmetric or nonsymmetric perm ---*/ - if(fscanf(fp,"%d",&prm->ipar[1]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- B block size ---*/ - if(fscanf(fp,"%d",&prm->ipar[2]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Tolerance for independent sets ---*/ - if(fscanf(fp,"%lf",&prm->tolind) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Outer Krylov dimension ---*/ - if(fscanf(fp,"%d",&prm->ipar[6]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Maximum outer iterations ---*/ - if(fscanf(fp,"%d",&prm->ipar[7]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Inner Krylov dimension ---*/ - if(fscanf(fp,"%d",&prm->ipar[4]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- maximum number of inner iterations ---*/ - if(fscanf(fp,"%d",&prm->ipar[5]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- nonsymmetric perm. for interlevel blocks ---*/ - if(fscanf(fp,"%d",&prm->ipar[10]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Column Permutations - interlevel blocks ---*/ - if(fscanf(fp,"%d",&prm->ipar[11]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Row scaling - interlevel blocks ---*/ - if(fscanf(fp,"%d",&prm->ipar[12]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Column scaling - interlevel blocks ---*/ - if(fscanf(fp,"%d",&prm->ipar[13]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Nonsymm. Perm. for last level SC. ---*/ - if(fscanf(fp,"%d",&prm->ipar[14]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Col. Perm. for last level SC. ---*/ - if(fscanf(fp,"%d",&prm->ipar[15]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Row scaling - last level SC. ---*/ - if(fscanf(fp,"%d",&prm->ipar[16]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Column scaling - last level SC. ---*/ - if(fscanf(fp,"%d",&prm->ipar[17]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- lfil0 for ilut, iluk, and arms for lfil[0-3] ---*/ - if(fscanf(fp,"%d",&prm->lfil[0]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- lfil for shur ---*/ - if(fscanf(fp,"%d",&prm->lfil[4]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- lfil for ILUT L and U ---*/ - if(fscanf(fp,"%d",&prm->lfil[5]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- droptol0(droptol[0=3], L, U, L^{-1}F, EU^{-1}---*/ - if(fscanf(fp,"%lf",&prm->droptol[0]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- droptol4(for schur complements at each level) ---*/ - if(fscanf(fp,"%lf",&prm->droptol[4]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- droptol5(for ILUT in last level Schur Complement) ---*/ - if(fscanf(fp,"%lf",&prm->droptol[5]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - - prm->droptol[1] = prm->droptol[2] = prm->droptol[3] = - prm->droptol[0]; - prm->droptol[6] = prm->droptol[5]; - - prm->lfil[1] = prm->lfil[2] = prm->lfil[3] = - prm->lfil[0]; - prm->lfil[6] = prm->lfil[5]; - - fclose(fp); - -/*------- Done reading inputs, now read list of Matrices -------------*/ -#if defined(DBL_CMPLX) - if (NULL == (fmat = fopen("./matfileCmplx", "r"))) { - fprintf(stderr, "cannot open file: matfile\n"); - exit(1); - } -#else - if (NULL == (fmat = fopen("./matfileReal", "r"))) { - fprintf(stderr, "cannot open file: matfile\n"); - exit(1); - } -#endif - i = 0; - do { - if(fgets(buf,MAX_LINE,fmat) == NULL){ - fprintf(stderr, "Error reading matrix list"); - exit(1); - } - tmpstring = (char *) strtok(buf, " "); - tmpstring++; - tmpstring[strlen(tmpstring)-1]='\0'; - strcpy(mname[i], --tmpstring); - i++; - } while (strncmp(buf, "##", 2)); - - fclose(fmat); - - return 0; -} - -void set_pc_params(parms_PC pc, fprm prm) -{ - parms_PCSetType(pc, prm->pctype); - parms_PCSetILUType(pc, prm->pcilutype); - parms_PCSetNlevels(pc, prm->ipar[0]); - parms_PCSetPermType(pc, prm->ipar[1]); - parms_PCSetBsize(pc, prm->ipar[2]); - parms_PCSetInnerEps(pc, prm->pgfpar[0]); - parms_PCSetInnerKSize(pc, prm->ipar[4]); - parms_PCSetInnerMaxits(pc, prm->ipar[5]); - parms_PCSetFill(pc, prm->lfil); - parms_PCSetTol(pc, prm->droptol); - parms_PCSetTolInd(pc, prm->tolind); - parms_PCSetPermScalOptions(pc, &prm->ipar[10], 1); - parms_PCSetPermScalOptions(pc, &prm->ipar[14], 0); -} -void set_solver_params(parms_Solver solver, fprm prm) -{ - char buf[BUFFLEN]; - - sprintf(buf, "%d", prm->ipar[7]); - parms_SolverSetParam(solver, MAXITS, buf); - sprintf(buf, "%d", prm->ipar[6]); - parms_SolverSetParam(solver, KSIZE, buf); - sprintf(buf, "%g", prm->pgfpar[1]); - parms_SolverSetParam(solver, DTOL, buf); -} - -void fread_param_(char *fname, fprm *prm, char *matrix, int *matlen, int len) -{ - char mname[MAX_MAT][MAX_LINE], *buff, *buff2; - - buff2 = malloc((len+1)*sizeof(*buff2)); - strncpy(buff2, fname, len); - buff2[len] = '\0'; - *prm = malloc(sizeof(**prm)); - read_param(buff2, mname, *prm); - buff = &mname[0][0]; - strncpy(matrix, buff, strlen(buff)); - matrix[strlen(buff)] = '\0'; - *matlen = (int)strlen(matrix); - free(buff2); - -} - -void fset_pc_params_(parms_PC *pc, fprm *prm) -{ - set_pc_params(*pc, *prm); -} - -void fset_solver_params_(parms_Solver *solver, fprm *prm) -{ - set_solver_params(*solver, *prm); -} - -void fprm_free_(fprm *prm) -{ - free(*prm); -} diff --git a/lib/parms/examples/general/aux.h b/lib/parms/examples/general/aux.h deleted file mode 100755 index 98693c1f7..000000000 --- a/lib/parms/examples/general/aux.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef _AUX_HEADER_INCLUDED_H -#define _AUX_HEADER_INCLUDED_H -#define MAX_LINE 100 -#define MAX_MAT 100 - -#include "parms.h" - -#define BUFFLEN 200 -#define STR2U(p, p1) \ - if (1) { \ - p = buf; \ - while (' ' == *p) { \ - p++; \ - } \ - p1 = malloc((strlen(p)+1)*sizeof(*p1)); \ - p2 = p1; \ - while (' ' != *p) { \ - *p2 = toupper(*p); \ - ++p; \ - ++p2; \ - } \ - *p2 = '\0'; \ - } else - -typedef struct fprm_ { - PCTYPE pctype; - PCILUTYPE pcilutype; - double pgfpar[2]; - double tolind; - double droptol[7]; - int ipar[18]; - int lfil[7]; -} *fprm; - -#if defined(FORTRAN_CAPS) -#define fread_param_ FREAD_PARAM -#define fset_pc_params_ FSET_PC_PARAMS -#define fset_solver_params_ FSET_SOLVER_PARAMS -#define fprm_free_ FPRM_FREE -#define wreadmtc_ WREADMTC -#define readmtc_ READMTC -#define csrcsc_ CSRCSC -#define aplb_ APLB -#define dse_ DSE -#elif defined(FORTRAN_DOUBLE_UNDERSCORE) -#define fread_param_ fread_param__ -#define fset_pc_params_ fset_pc_params__ -#define fset_solver_params_ fset_solver_params__ -#define fprm_free_ fprm_free__ -#define wreadmtc_ wreadmtc__ -#define readmtc_ readmtc__ -#define csrcsc_ csrcsc__ -#define aplb_ aplb__ -#define dse_ dse__ -#elif !defined(FORTRAN_UNDERSCORE) -#define fread_param_ fread_param -#define fset_pc_params_ fset_pc_params -#define fset_solver_params_ fset_solver_params -#define fprm_free_ fprm_free -#define wreadmtc_ wreadmtc -#define readmtc_ readmtc -#define csrcsc_ csrcsc -#define aplb_ aplb -#define dse_ dse -#endif - -extern int read_param(char *fname, char mname[MAX_MAT][MAX_LINE], fprm prm); -extern void set_pc_params(parms_PC pc, fprm prm); -extern void set_solver_params(parms_Solver solver, fprm prm); -extern void fread_param_(char *fname, fprm *prm, char *matrix, int *matlen, int len); -extern void fset_pc_params_(parms_PC *pc, fprm *prm); -extern void fset_solver_params_(parms_Solver *solver, fprm *prm); -extern void fprm_free_(fprm *prm); -extern void wreadmtc_(int *nmax, int *nzmax, int *job, char *fname,int - *len, double *a, int *ja, int *ia, double *rhs, - int *nrhs, char *guesol, int *nrow, int *ncol, - int *nnz, char *title, char *key, char *type, - int *ierr); -extern void readmtc_(int *nmax, int *nzmax, int *job, char *fname, double - *a, int *ja, int *ia, double *rhs, int *nrhs, char - *guesol, int *nrow, int *ncol,int *nnz, char *title, - char *key, char *type, int *ierr); -extern void csrcsc_(int *n, int *job, int *ipos, double *a, int *ja, - int *ia, double *ao, int *jao, int *iao); -extern void aplb_(int *nrow, int *ncol, int *job, double *a, int *ja, - int *ia, double *b, int *jb, int *ib, double *c, int - *jc, int *ic, int *nnzmax, int *iw, int *ierr); -extern void dse_(int *n, int *ja, int *ia, int *ndom, int *riord, int - *dom, int *idom, int *mask, int *jwk, int *link); - -/*---------- complex routines ------------*/ -#if defined(DBL_CMPLX) -#if defined(FORTRAN_CAPS) -#define wreadmtc_ WREADMTC -#define readmtc_ READMTC -#define csrcsc_ CSRCSC -#define aplb_ APLB -#elif defined(FORTRAN_DOUBLE_UNDERSCORE) -#define wreadmtc_ wreadmtc__ -#define readmtc_ readmtc__ -#define csrcsc_ csrcsc__ -#define aplb_ aplb__ -#elif !defined(FORTRAN_UNDERSCORE) -#define wreadmtc_ wreadmtc -#define readmtc_ readmtc -#define csrcsc_ csrcsc -#define aplb_ aplb -#endif -extern void zwreadmtc_(int *nmax, int *nzmax, int *job, char *fname,int - *len, complex double *a, int *ja, int *ia, complex double *rhs, - int *nrhs, char *guesol, int *nrow, int *ncol, - int *nnz, char *title, char *key, char *type, - int *ierr); -extern void zreadmtc_(int *nmax, int *nzmax, int *job, char *fname, complex double - *a, int *ja, int *ia, complex double *rhs, int *nrhs, char - *guesol, int *nrow, int *ncol,int *nnz, char *title, - char *key, char *type, int *ierr); -extern void zcsrcsc_(int *n, int *job, int *ipos, complex double *a, int *ja, - int *ia, complex double *ao, int *jao, int *iao); -extern void zaplb_(int *nrow, int *ncol, int *job, complex double *a, int *ja, - int *ia, complex double *b, int *jb, int *ib, complex double *c, int - *jc, int *ic, int *nnzmax, int *iw, int *ierr); -#endif - -#endif diff --git a/lib/parms/examples/general/dd-HB-dse.c b/lib/parms/examples/general/dd-HB-dse.c deleted file mode 100755 index f8f9961dd..000000000 --- a/lib/parms/examples/general/dd-HB-dse.c +++ /dev/null @@ -1,437 +0,0 @@ -/*---------------------------------------------------------------------- - * Program dd-HB-dse - *---------------------------------------------------------------------- - * - * In this test program, each processor reads the whole matrix - * from file. The matrix is assumed to be in Harwell-Boeing format. - * Matrix graph is then partitioned using DSE, a simple partitioning - * routine, and scatters the local matrices to each processor. Once - * these submatrices are received each processor solves the problem - * using preconditioned FGMRES preconditioned with : - * BJ, RAS, SCHUR - *--------------------------------------------------------------------*/ - -#include -#include -#include -#if defined(__ICC) -#include -#else -#include -#endif -#include "parms.h" -#include "aux.h" - -int main(int argc, char *argv[]) -{ - - /* declarations related to Harwell-boeing format for reading the HB - matri. Second part is related to I/O parameters */ - char mname[MAX_MAT][MAX_LINE], guesol[2], title[72], key[8], type[3]; - int nrhs, nc, n, nnz, tmp0, tmp, tmp2, tmp3, job, mat; - int myid, ierr, i, nloc; - /* memory usage of the preconditioning matrix */ - double ratio; - /* working array for reading matrix */ - double norm, res1, tpc, ttol; - FLOAT *a, *rhstmp; - int *ja, *ia; - int npro,its, *im; - fprm prm; - FILE *fp=NULL, *fout=NULL; - char *name, *iluname, buf[40]; - -/*-------------------- variables related to dse partitioning */ - int *riord, *dom, *idom, *mask, *jwk, *link; - -/*-------------------- variables related to pARMS */ - parms_Map map; - FLOAT *x, *y, *rhs, *resvec; - parms_Mat A; - parms_PC pc; - parms_Solver solver; - parms_Timer tm; - -/* Viewer object for solver */ -// parms_Viewer sv; - -/*-------------------- initialize MPI environment */ - MPI_Init(&argc, &argv); - MPI_Comm_rank(MPI_COMM_WORLD, &myid); - MPI_Comm_size(MPI_COMM_WORLD, &npro); - - tmp0 = 0; - nrhs = 0; - -/*-------------------- read matrix name from input file */ - prm = malloc(sizeof(*prm)); - if (prm == NULL) { - fprintf(stderr, "cannot allocate memory for prm\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - - if (argc >= 2) { - read_param(argv[1],mname, prm); - } - else if (argc == 1){ - read_param("inputs",mname, prm); - } - - /* variable "mname" stores the name of the file in HB format. Read a - Harwell-Boeing matrix. using wreadmtc c-version of sparsit - routine - call wreadmtc a first time to determine sizes of - arrys. read in values on the second call. - */ - -/* --- Begin loop over matrices ----*/ - mat = 0; - while(mname[mat][1] != '#'){ - a = NULL; ja = NULL; ia = NULL; rhstmp = NULL; - -#if defined(DBL_CMPLX) - zreadmtc_(&tmp0,&tmp0,&tmp0,mname[mat],a,ja,ia,rhstmp,&nrhs, - guesol,&n,&nc,&nnz,title,key,type,&ierr); -#else - readmtc_(&tmp0,&tmp0,&tmp0,mname[mat],a,ja,ia,rhstmp,&nrhs, - guesol,&n,&nc,&nnz,title,key,type,&ierr); -#endif - - a = malloc(nnz*sizeof(*a)); - if (a == NULL) { - fprintf(stderr, "cannot allocate memory for a\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - ja = malloc(nnz*sizeof(*ja)); - if (ja == NULL) { - fprintf(stderr, "cannot allocate memory for ja\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - ia = malloc((n+1)*sizeof(*ia)); - if (ia == NULL) { - fprintf(stderr, "cannot allocate memory for ia\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - rhstmp = malloc(n*sizeof(*rhstmp)); - if (rhstmp == NULL) { - fprintf(stderr, "cannot allocate memory for rhstmp\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - if(nrhs != 0) - tmp = 3; - else - tmp = 2; - tmp2 = n; - tmp3 = nnz; - - /*-------------------- Array sizes determined. Now call - wreadmtc again for really reading */ -/* - wreadmtc_(&tmp2,&tmp3,&tmp,mname[mat],&len,a,ja,ia,rhstmp,&nrhs, - guesol,&n,&nc,&nnz,title,key,type,&ierr); -*/ -#if defined(DBL_CMPLX) - zreadmtc_(&tmp2,&tmp3,&tmp,mname[mat],a,ja,ia,rhstmp,&nrhs, - guesol,&n,&nc,&nnz,title,key,type,&ierr); -#else - readmtc_(&tmp2,&tmp3,&tmp,mname[mat],a,ja,ia,rhstmp,&nrhs, - guesol,&n,&nc,&nnz,title,key,type,&ierr); -#endif - - if(ierr != 0) { - fprintf(stderr, "ierr = %d\n", ierr); - fprintf(stderr, "cannot read matrix\n"); - MPI_Finalize(); - exit(1); - } - - if(myid == 0){ - if(argc == 3) { - if (NULL == (fp = fopen(argv[2], "w"))) { - fprintf(stderr, "cannot open file %s\n", argv[2]); - exit(1); - } - } - else { - fp = stdout; - } - fprintf(fp, "\nMatrix %d: %.*s %.*s \n",(mat+1),8,key,3,type); - fprintf(fp, "n = %d, nnz = %d\n", n, nnz); - } - -/*-------------Convert from CSC to CSR format ------------*/ - int *jb, *ib; - FLOAT *b; - b = malloc(nnz*sizeof(*b)); - if (b == NULL) { - fprintf(stderr, "cannot allocate memory for b\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - jb = malloc(nnz*sizeof(*jb)); - if (jb == NULL) { - fprintf(stderr, "cannot allocate memory for jb\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - ib = malloc((n+1)*sizeof(*ib)); - if (ib == NULL) { - fprintf(stderr, "cannot allocate memory for ib\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - job = 1; -#if defined(DBL_CMPLX) - zcsrcsc_(&n, &job, &job, a, ja, ia, b, jb, ib); -#else - csrcsc_(&n, &job, &job, a, ja, ia, b, jb, ib); -#endif -/*---------------Copy CSR matrix ------------------*/ - - memcpy(ia, ib, (n+1)*sizeof(*ia)); - memcpy(ja, jb, nnz*sizeof(*ja)); - memcpy(a, b, nnz*sizeof(*a)); - -/*---------------Free CSR matrix ------------------*/ - - free(ib); - free(b); - free(jb); - - idom = malloc((npro+1)*sizeof(*idom)); - if (idom == NULL) { - fprintf(stderr, "cannot allocate memory for idom\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - dom = malloc(n*sizeof(*dom)); - if (dom == NULL) { - fprintf(stderr, "cannot allocate memory for dom\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - - if (npro == 1) { - for (i = 0; i < n; i++) { - dom[i] = i+1; - } - idom[0] = 1; - idom[1] = n+1; - } - else { - riord = malloc(n*sizeof(*riord)); - if (riord == NULL) { - fprintf(stderr, "cannot allocate memory for riord\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - mask = malloc(n*sizeof(*mask)); - if (mask == NULL) { - fprintf(stderr, "cannot allocate memory for mask\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - jwk = malloc(2*n*sizeof(*jwk)); - if (jwk == NULL) { - fprintf(stderr, "cannot allocate memory for jwk\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - link = malloc(n*sizeof(*link)); - if (link == NULL) { - fprintf(stderr, "cannot allocate memory for link\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - dse_(&n, ja, ia, &npro, riord, dom, idom, mask, jwk, link); - free(riord); - free(mask); - free(jwk); - free(link); - } -/* - for (i = 0; i < n; i++) { - dom[i] = i+1; - } - for(i=1; improcx; - *mprocy = (*prm)->mprocy; - *xnmesh = (*prm)->xnmesh; - *ynmesh = (*prm)->ynmesh; - - free(mname); -} - -void fset_pc_params_(parms_PC *pc, fprm *prm) -{ - set_pc_params(*pc, *prm); -} - -void fset_solver_params_(parms_Solver *solver, fprm *prm) -{ - set_solver_params(*solver, *prm); -} - -void fprm_free_(fprm *prm) -{ - free(*prm); -} diff --git a/lib/parms/examples/grid/aux.h b/lib/parms/examples/grid/aux.h deleted file mode 100755 index f56754bb5..000000000 --- a/lib/parms/examples/grid/aux.h +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef PARMS_AUX_READ_H -#define PARMS_AUX_READ_H - -#include -#include -#include "parms.h" - -#define MAXLINE 200 -#define STR2U(p, p1) \ - if (1) { \ - p = buf; \ - while (' ' == *p) { \ - p++; \ - } \ - p1 = malloc((strlen(p)+1)*sizeof(*p1)); \ - p2 = p1; \ - while (' ' != *p) { \ - *p2 = toupper(*p); \ - ++p; \ - ++p2; \ - } \ - *p2 = '\0'; \ - } else - -typedef struct fprm_ { - PCTYPE pctype; - PCILUTYPE pcilutype; - double pgfpar[2]; - double tolind; - double droptol[7]; - int ipar[18]; - int lfil[7]; - int mprocx; - int mprocy; - int xnmesh; - int ynmesh; -} *fprm; - -#if defined(FORTRAN_CAPS) -#define fread_param_ FREAD_PARAM -#define fset_pc_params_ FSET_PC_PARAMS -#define fset_solver_params_ FSET_SOLVER_PARAMS -#define fprm_free_ FPRM_FREE -#define part1_ PART1 -#define part2_ PART2 -#define gen5loc_ GEN5LOC -#elif defined(FORTRAN_DOUBLE_UNDERSCORE) -#define fread_param_ fread_param__ -#define fset_pc_params_ fset_pc_params__ -#define fset_solver_params_ fset_solver_params__ -#define fprm_free_ fprm_free__ -#define part1_ part1__ -#define part2_ part2__ -#define gen5loc_ gen5loc__ -#elif !defined(FORTRAN_UNDERSCORE) -#define fread_param_ fread_param -#define fset_pc_params_ fset_pc_params -#define fset_solver_params_ fset_solver_params -#define fprm_free_ fprm_free -#define part1_ part1 -#define part2_ part2 -#define gen5loc_ gen5loc -#endif - -extern int read_param(char *fname, fprm prm); -extern void set_pc_params(parms_PC pc, fprm prm); -extern void set_solver_params(parms_Solver solver, fprm prm); -extern void fset_pc_params_(parms_PC *pc, fprm *prm); -extern void fset_solver_params_(parms_Solver *solver, fprm *prm); -extern void fread_param_(char *fname, fprm *prm, int *mprocx, int - *mprocy, int *xnmesh, int *ynmesh, int len); -extern void fprm_free_(fprm *prm); -extern void part1_(int *nx,int *ny,int *mpx,int *mpy,int *ovp,int - *lst,int *lstptr,int *iout); -extern void part2_(int *nx,int *ny,int *nz,int *mpx,int *mpy,int *mpz, - int *ovp,int *lst,int *lstptr,int *iout); -extern void gen5loc_(int *nx,int *ny,int *nz,int *nloc,int - *riord,double *a,int *ja,int *ia, double - *stencil); -#if defined(DBL_CMPLX) -extern void zgen5loc_(int *nx,int *ny,int *nz,int *nloc,int - *riord,complex double *a,int *ja,int *ia, complex double - *stencil); -#endif -#endif diff --git a/lib/parms/examples/grid/dd-grid.c b/lib/parms/examples/grid/dd-grid.c deleted file mode 100755 index 1629e4c05..000000000 --- a/lib/parms/examples/grid/dd-grid.c +++ /dev/null @@ -1,300 +0,0 @@ -/*----------------------------------------------------------------------- - * Test program - WORKS ONLY WHEN nprocx * nprocy == np - *----------------------------------------------------------------------- - * - * Each node generates its own part of the 5-point matrix using a - * simple regular partitioning. Then the rhs is generated, the local - * data structure is set and the system is solved using - * preconditioned FGMRES. - * - *----------------------------------------------------------------------- - * - * the mesh is defined as follows. The grid is virtually laid out on a - * 2-dimensional array of processors: mprocx in x direction, mprocy in - * y direction. mprocx is read from a file (inputs) and mprocy is - * determined as mprocy = numproc / mprocx where numproc is the total - * number of processors available for this run. The simplest case is to - * always take mprocx=1. Then the mesh sizeso in each direction are set - * by the commands: - * - * nx = nmesh*mprocx - * ny = nmesh*mprocy - * nz = 1 - *---------------------------------------------------------------------*/ - -#include -#include -#include -#include -#include "parms.h" -#include "aux.h" - -int main(int argc, char *argv[]) -{ - /* n -- number of total variables - * nloc -- number of local variables - * nx -- number of variables in the x direction - * ny -- number of variables in the y direction - * nz -- 1 - * iov -- overlap (always 0 for vertex based partitioning) - * mprocx -- number of processors in the x direction - * mprocy -- number of processors in the y direction - * maxits -- maximum number of iterations - * nmesh -- mesh points in x and y directions - * im -- the dimension of Krylov subspace - * lfil -- lfil for ilut - * maptmp -- global labelings which are stored processor by processor - * mapptr -- a pointer array. mapptr[i] points to the beginning of - * processor i - * nnz -- the number of none zero elements - */ - int npro, myid,n,nloc,nx,ny,nz,iov,mprocx,mprocy,mprocz,xnmesh,ynmesh, nnz; - int *maptmp,*mapptr, iout, *ja, *ia, *lvars, size,nrow,start; - int its; - double tpc, ttol, res0, res1, ratio; - int count; - fprm prm; - char *name, *iluname; - FILE *fp; - /* variables related to pARMS */ - parms_Map map; - FLOAT *sol, *rhs, *y; - parms_Mat A; - parms_PC pc; - parms_Solver solver; - parms_Timer t; - -/* Viewer object */ -// parms_Viewer mv; - - /* - * a -- a in CSR format - * stencil-- working array - */ - FLOAT stencil[100], *a; - -/*-------------------- Initialize MPI environment */ - MPI_Init(&argc, &argv); - MPI_Comm_size(MPI_COMM_WORLD, &npro); - MPI_Comm_rank(MPI_COMM_WORLD, &myid); - -/*--------- read data from file input ------------*/ - prm = malloc(sizeof(*prm)); - if (prm == NULL) { - fprintf(stderr, "cannot allocate memory for prm\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - - if (argc >= 2) { - read_param(argv[1], prm); - } - else if (argc == 1){ - read_param("inputs", prm); - } - - if (myid == 0) { - fprintf(stderr, "mprocx = %d, mprocy = %d\n", prm->mprocx, prm->mprocy); - fprintf(stderr, "xnmesh = %d, ynmesh = %d\n", prm->xnmesh, prm->ynmesh); - } - - mprocx = prm->mprocx; - mprocy = prm->mprocy; - xnmesh = prm->xnmesh; - ynmesh = prm->ynmesh; - -/*------------------- mprocy = dm->comm->npro / mprocx; */ - if (npro != mprocx*mprocy) { - if (myid == 0) { - fprintf(stderr, " ** ERROR nproc is not equal to mprocx*mprocy = %d\n", - mprocx*mprocy); - } - MPI_Barrier(MPI_COMM_WORLD); - MPI_Abort(MPI_COMM_WORLD, 99); - exit(1); - } - - nx = mprocx * xnmesh; - ny = mprocy * ynmesh; - nz = 1; - size = xnmesh*ynmesh*mprocx*mprocy; - - mapptr = malloc((npro+1)*sizeof(*mapptr)); - if (mapptr == NULL) { - fprintf(stderr, "cannot allocate memory for mapptr\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - maptmp = malloc(size*sizeof(*maptmp)); - if (maptmp == NULL) { - fprintf(stderr, "cannot allocate memory for maptmp\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - - iov = 0; - mprocz = 1; -/*-------------------- call part2_ from skit.f to generate local mesh */ - part2_(&nx, &ny, &nz, &mprocx, &mprocy, &mprocz, &iov, maptmp, - mapptr, &iout); - n = nx * ny * nz; -/*-------------------- Create map object and setup mapping from - global labels to processors */ - parms_MapCreateFromPtr(&map, n, maptmp, mapptr, MPI_COMM_WORLD, 1, NONINTERLACED); - nloc = parms_MapGetLocalSize(map); - lvars = (int *)malloc(nloc*sizeof(int)); - parms_MapGetGlobalIndices(map, lvars); -/*-------------------- Allocate temporary arrays for storing - local matrix in CSR format */ - nnz = 10*xnmesh*ynmesh; - - a = malloc(nnz*sizeof(*a)); - if (a == NULL) { - fprintf(stderr, "cannot allocate memory for a\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - ja = malloc(nnz*sizeof(*ja)); - if (ja == NULL) { - fprintf(stderr, "cannot allocate memory for ja\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - ia = malloc((nloc+1)*sizeof(*ia)); - if (ia == NULL) { - fprintf(stderr, "cannot allocate memory for ia\n"); - MPI_Abort(MPI_COMM_WORLD, 66); - } - - nrow = mapptr[myid+1] - mapptr[myid]; - start = mapptr[myid] - mapptr[0]; -/*-------------------- Generate the local matrix using gen5loc (in fdmat.f) */ -#if defined(DBL_CMPLX) - zgen5loc_(&nx, &ny, &nz, &nloc, &maptmp[start],a,ja,ia,stencil); -#else - gen5loc_(&nx, &ny, &nz, &nloc, &maptmp[start],a,ja,ia,stencil); -#endif -/*-------------------- Create a matrix object based on map */ - parms_MatCreate(&A, map); - -/*-------------------- Insert entries into the matrix */ - parms_MatSetValues(A, nrow, &maptmp[start], ia, ja, a, INSERT); -/*-------------------- Free the matrix stored in CSR format (ia,ja,a) */ - free(a); - free(ja); - free(ia); - - free(mapptr); - free(maptmp); -/*-------------------- setup matrix A */ - parms_MatSetup(A); -/*-------------------- create timer */ - parms_TimerCreate(&t); -/*-------------------- create preconditioner */ - parms_PCCreate(&pc, A); -/*-------------------- set parameters for pc */ - set_pc_params(pc, prm); -/*-------------------- reset the timer */ - parms_TimerReset(t); -/*-------------------- setup preconditioner */ - parms_PCSetup(pc); - tpc = parms_TimerGet(t); -/*-------------------- stats: get ratio of nnz(preconditioning matrix) - over nnz(A) */ - parms_PCGetRatio(pc, &ratio); -/*-------------------- pause the timer */ - parms_TimerPause(t); -/*-------------------- Create a krylov subspace object */ - parms_SolverCreate(&solver, A, pc); -/*-------------------- Set pamaters for solver */ - set_solver_params(solver, prm); - - free(prm); -/*-------------------- Create solution and right-hand-side vectors */ - - rhs = (FLOAT *)malloc(nloc*sizeof(FLOAT)); - sol = (FLOAT *)malloc(nloc*sizeof(FLOAT)); - y = (FLOAT *)malloc(nloc*sizeof(FLOAT)); - -/*------------- print matrix --- DEBUG --------*/ -/* - parms_ViewerCreate(&mv, "matrix.out"); - parms_MatViewCOO_dvcsr(A, mv); -*/ - -/*-------------------- Set up an artifical right-hand-side vector */ - for(count=0; count -#include -#include -#include -#include "petscao.h" -#include "petscksp.h" -#include "parms.h" -#include "protos.h" - -int main(int argc, char *argv[]) -{ - int myid,usmy,ierr,len; - /* declarations related to Harwell-boeing format for reading the HB - matri. Second part is related to I/O parameters */ - char matrix[BUFLEN], guesol[2], title[72], - key[8], type[3]; - int nrhs, nc, n, nnz, tmp0, tmp, tmp2, tmp3, job; - - /* working array for reading matrix */ - FLOAT *a,*rhstmp,*b; - int *ja, *ia,*jb,*ib; - int npro, *iwk, i1,i; - int mnnz, ii, jj; - - /* working array for symmetryzing matrix */ - FLOAT *mc; - int *jc, *ic; - int nnzrow, pos; - - Vec sol, rhs, y; - Mat A; - KSP ksp; - PC pc; - PetscReal res1, res2, value; - int j, iters; - -/* User defined preconditioner */ - PetscTruth user_defined_pc; /* flag option for user defined preconditioner */ - - /* initialize MPI environment */ - PetscInitialize(&argc, &argv, (char *)0, (char *)0); - MPI_Comm_rank(PETSC_COMM_WORLD, &myid); - MPI_Comm_size(PETSC_COMM_WORLD, &npro); - -/* ------------ Check to make sure compilations for pARMS and PETSC are consistent -----*/ -#if defined(DBL_CMPLX) - if(sizeof(PetscScalar) == sizeof(double)) - { - fprintf(stderr," %d: Compilation mismatch: Using complex version of pARMS with real version of PETSC !!\n", myid); - fprintf(stderr," %d: Compile real version of pARMS to use real version of PETSC \n", myid); - MPI_Finalize(); - exit(0); - } -#else - if(sizeof(PetscScalar) == sizeof(double _Complex)) - { - fprintf(stderr," %d: Compilation mismatch: Using real version of pARMS with complex version of PETSC !!\n", myid); - fprintf(stderr," %d: Compile complex version of pARMS to use complex version of PETSC \n", myid); - MPI_Finalize(); - exit(0); - } -#endif - - tmp0 = 0; - nrhs = 1; - /* Read matrix; either using user-defined function (routine uread) or - | SPARSKIT function for reading Harwell-Boeieng matrices - */ - /* read matrix name from file input */ - read_matrix(matrix); - - /* variable "matrix" stores the name of the file in HB format - | - | Read a Harwell-Boeing matrix. using wreadmtc c-version of - | sparsit routine - call wreadmtc a first time to determine sizes - | of arryas. read in values on the second call. - */ - len = strlen(matrix); - a = NULL; ja = NULL; ia = NULL; rhstmp = NULL; -#if defined(DBL_CMPLX) - zwreadmtc_(&tmp0,&tmp0,&tmp0,matrix,&len,a,ja,ia,rhstmp,&nrhs, - guesol,&n,&nc,&nnz,title,key,type,&ierr); -#else - wreadmtc_(&tmp0,&tmp0,&tmp0,matrix,&len,a,ja,ia,rhstmp,&nrhs, - guesol,&n,&nc,&nnz,title,key,type,&ierr); -#endif - ia = malloc((n+1)*sizeof(*ia)); - ja = malloc(nnz*sizeof(*ja)); - a = malloc(nnz*sizeof(*a)); - - if (nrhs) { - tmp = 3; - tmp2 = n; - tmp3 = nnz; - if (guesol[0] == 'G' || guesol[0] == 'g') nrhs++; - if (guesol[1] == 'X' || guesol[1] == 'x') nrhs++; - rhstmp = malloc((n*nrhs)*sizeof(*rhstmp)); - } - if (!nrhs) { - tmp = 2; - tmp2 = n; - tmp3 = nnz; - } - - /* Array sizes determined. Now call wreadmtc again for really - reading */ -#if defined(DBL_CMPLX) - zwreadmtc_(&tmp2,&tmp3,&tmp,matrix,&len,a,ja,ia,rhstmp,&nrhs, - guesol,&n,&nc,&nnz,title,key,type,&ierr); -#else - wreadmtc_(&tmp2,&tmp3,&tmp,matrix,&len,a,ja,ia,rhstmp,&nrhs, - guesol,&n,&nc,&nnz,title,key,type,&ierr); -#endif - free(rhstmp); - if(ierr > 0 && ierr < 4 && !myid) { - fprintf(stderr, "cannot read matrix\n"); - fprintf(stderr, "ierr = %d\n", ierr); - exit(1); - } - else if (ierr >= 4 && !myid){ - fprintf(stderr, "*** Warning: RHS or solution vectors are not read. Artificial RHS will be used\n"); - nrhs = 0; - } - - if(myid == 0) { - fprintf(stdout,"READ the matrix %.*s %.*s \n",8,key,3,type); - } - - - if (myid == 0){ - fprintf(stdout,"Matrix dimension is %d, Number of nonzeros is %d\n",n,nnz); - } - - - /* the matrix is symmetrized */ - b = malloc(nnz*sizeof(*b)); - jb = malloc(nnz*sizeof(*jb)); - ib = malloc((n+1)*sizeof(*ib)); - - job = 1; - i = 1; -/* ---- convert from CSC to CSR matrix ------*/ -#if defined(DBL_CMPLX) - zcsrcsc_(&n, &job, &i, a, ja, ia, b, jb, ib); -#else - csrcsc_(&n, &job, &i, a, ja, ia, b, jb, ib); -#endif - -/*----- symmetrize or not ---- */ - usmy = 0; - if(usmy == 1) { - iwk = malloc(n*sizeof(*iwk)); - mc = malloc((2*nnz)*sizeof(*mc)); - jc = malloc((2*nnz)*sizeof(*jc)); - ic = malloc((n+1)*sizeof(*ic)); - - for(i = 0; i < nnz; i++) { - b[i] = 0.0; - } - job = 1; - i1 = 2*nnz; - - /* compute C = A + B */ -#if defined(DBL_CMPLX) - zaplb_(&n, &n, &job, a, ja, ia, b, jb, ib, mc, jc, ic, &i1, iwk, - &ierr); -#else - aplb_(&n, &n, &job, a, ja, ia, b, jb, ib, mc, jc, ic, &i1, iwk, - &ierr); -#endif - if (ierr) fprintf(stderr, "Error: in aplb, ierr = %d\n", ierr); - nnz = ic[n]-1; - if (!myid) - fprintf(stderr, - "Matrix pattern has been symmetrized; added %d nonzeros.\n", - ic[n]-ia[n]); - - ja = realloc(ja, nnz*sizeof(*ja)); - a = realloc(a, nnz*sizeof(*a)); - - memcpy(ia, ic, (n+1)*sizeof(*ia)); - memcpy(ja, jc, nnz*sizeof(*ia)); - memcpy(a, mc, nnz*sizeof(*a)); - - free(b); - free(jb); - free(ib); - free(mc); - free(jc); - free(ic); - free(iwk); - } - else - { - memcpy(ia, ib, (n+1)*sizeof(*ia)); - memcpy(ja, jb, nnz*sizeof(*ia)); - memcpy(a, b, nnz*sizeof(*a)); - free(b); - free(jb); - free(ib); - } - - /* create Mat object */ - mnnz = 0; - for (i = 1; i <= n; i++) { - nnzrow = ia[i] - ia[i-1]; - if (mnnz < nnzrow) { - mnnz = nnzrow; - } - } - - /* create a matrix object */ - MatCreateMPIAIJ(PETSC_COMM_WORLD, PETSC_DECIDE, PETSC_DECIDE, n, n, mnnz, PETSC_NULL, mnnz, - PETSC_NULL, &A); - - for(i = 1; i <= n; i++) { - nnzrow = ia[i] - ia[i-1]; - pos = ia[i-1] - ia[0]; - ii = i-1; - for (j = 0; j < nnzrow; j++) { - jj = ja[pos+j] - 1; - MatSetValues(A, 1, &ii, 1, &jj, &a[pos+j], INSERT_VALUES); - } - } - - /* free temporary arrays */ - free(a); free(ja); free(ia); - - /* assemble the matrix */ - MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY); - MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY); - - /* create an abstract krylov subspace object */ - KSPCreate(PETSC_COMM_WORLD, &ksp); - - /* create solution and right-hand-side vector */ - VecCreateMPI(PETSC_COMM_WORLD, PETSC_DECIDE, n, &rhs); - VecDuplicate(rhs, &sol); - VecDuplicate(rhs, &y); - - value = 1.0; - VecSet(sol, value); - MatMult(A, sol, rhs); - - /* set initial guess */ - value = 0.0; - VecSet(sol, value); - - VecAssemblyBegin(sol); - VecAssemblyEnd(sol); - - /* initial residual norm */ - MatMult(A, sol, y); - value = -1.0; - VecAXPY(rhs, value, y); - VecNorm(y, NORM_2, &res1); - - /* solve linear systems with the krylov subspace method selected */ - KSPSetOperators(ksp, A, A, SAME_NONZERO_PATTERN); - KSPSetType(ksp, KSPFGMRES); - KSPGMRESSetRestart(ksp, 20); - KSPSetTolerances(ksp, 1.0e-6, 1.0e-20, 1.0e6, 1000); - /* Extract PC context from the KSP context */ - ierr = KSPGetPC(ksp,&pc);CHKERRQ(ierr); - /* - Set a user-defined "shell" preconditioner if desired - */ - ierr = PetscOptionsHasName(PETSC_NULL,"-user_defined_pc",&user_defined_pc);CHKERRQ(ierr); - if (user_defined_pc) { - - /* (Required) Indicate to PETSc that we're using a "shell" preconditioner */ - ierr = PCSetType(pc,PCSHELL);CHKERRQ(ierr); - - /* (Required) Set the user-defined routine for applying the preconditioner */ - ierr = PCShellSetApply(pc,PCApply_PARMS);CHKERRQ(ierr); - ierr = PCShellSetContext(pc,&pc);CHKERRQ(ierr); - - /* (Optional) Set a name for the preconditioner, used for PCView() */ - ierr = PCShellSetName(pc,"pARMS Preconditioner");CHKERRQ(ierr); - - /* (Optional) Do any setup required for the preconditioner */ - ierr = PCShellSetSetUp(pc,PCSetUp_PARMS);CHKERRQ(ierr); - - /* (Optional) Destructor for the preconditioner */ - ierr = PCShellSetDestroy(pc,PCDestroy_PARMS);CHKERRQ(ierr); - - } else { - ierr = PCSetType(pc,PCBJACOBI);CHKERRQ(ierr); - } - - KSPSetFromOptions(ksp); - - KSPSolve(ksp, rhs, sol); - KSPGetIterationNumber(ksp, &iters); - VecNorm(rhs, NORM_2, &res1); - KSPGetResidualNorm(ksp, &res2); - if(myid == 0) { - fprintf(stderr, "The number of iteration %5s %d\n", "=",iters); - fprintf(stderr, "The initial residual error = %8.3e\n", res1); - fprintf(stderr, "The solution residual error = %8.3e\n", res2); - } - - /* free memories for map, vec, mat, solver */ - MatDestroy(A); - VecDestroy(sol); - VecDestroy(rhs); - VecDestroy(y); - KSPDestroy(ksp); - - /* exit MPI environment */ - PetscFinalize(); - return EXIT_SUCCESS; -} diff --git a/lib/parms/examples/petsc/fparms.c b/lib/parms/examples/petsc/fparms.c deleted file mode 100755 index 31d4a2047..000000000 --- a/lib/parms/examples/petsc/fparms.c +++ /dev/null @@ -1,79 +0,0 @@ -/* This file contains pARMS interface routines to petsc for fortran calls. Note that this is - * only compatible with the latest version of petsc (version 3.1). For older - * versions (version 3.0) use fparms.c_petsc_3.0. -*/ -#include -#include -#include -#include -#include -#include "petsc.h" -#include "petscksp.h" -#include "petscsys.h" -#include "parms.h" -#include "protos.h" -#ifdef _SUN -#define f_pcsetup_parms_ f_pcsetup_parms_ -#define f_pcapply_parms_ f_pcapply_parms_ -#define f_pcdestroy_parms_ f_pcdestroy_parms_ -#else -#ifdef _LINUX -#define f_pcsetup_parms_ f_pcsetup_parms__ -#define f_pcapply_parms_ f_pcapply_parms__ -#define f_pcdestroy_parms_ f_pcdestroy_parms__ -#else -#ifdef _IBM -#define f_pcsetup_parms_ f_pcsetup_parms -#define f_pcapply_parms_ f_pcapply_parms -#define f_pcdestroy_parms_ f_pcdestroy_parms -#else -#ifdef _SGI -#define f_pcsetup_parms_ f_pcsetup_parms_ -#define f_pcapply_parms_ f_pcapply_parms_ -#define f_pcdestroy_parms_ f_pcdestroy_parms_ -#endif -#endif -#endif -#endif - -/*--------------------------------------------------------------------- -| interface for FORTRAN calls to pARMS routines. -+---------------------------------------------------------------------*/ - -PetscErrorCode f_pcsetup_parms_(PC *ctx, PetscErrorCode *ierr) -{ -/* function protos */ - PetscErrorCode PCSetUp_PARMS(PC ); - int parms_SetOptions(parms_PC , char *); -/*end protos */ - - *ierr = PCSetUp_PARMS(*ctx);CHKERRQ(*ierr); - - return(*ierr); - -} - -PetscErrorCode f_pcapply_parms_(PC *dummy, Vec *b,Vec *x, PetscErrorCode *ierr) -{ -/*function protos */ - PetscErrorCode PCApply_PARMS(PC,Vec ,Vec ); -/*end protos*/ - - *ierr = PCApply_PARMS(*dummy,*b,*x);CHKERRQ(*ierr); - - return(*ierr); -} - -PetscErrorCode f_pcdestroy_parms_(PC *dummy, PetscErrorCode *ierr) -{ - -/* function protos */ - PetscErrorCode PCDestroy_PARMS(PC); -/* end protos */ - - *ierr = PCDestroy_PARMS(*dummy);CHKERRQ(*ierr); - - return(*ierr); -} - - diff --git a/lib/parms/examples/petsc/ftest.F b/lib/parms/examples/petsc/ftest.F deleted file mode 100755 index ad1f15ecb..000000000 --- a/lib/parms/examples/petsc/ftest.F +++ /dev/null @@ -1,263 +0,0 @@ -! -! Solves a linear system in parallel with KSP. Also indicates -! use of a user-provided preconditioner. Input parameters include: -! -user_defined_pc : Activate a user-defined preconditioner -! -! Program usage: mpiexec ex15f [-help] [all PETSc options] -! -!/*T -! Concepts: KSP^basic parallel example -! Concepts: PC^setting a user-defined shell preconditioner -! Processors: n -!T*/ -! -! ------------------------------------------------------------------------- - - program main - implicit none - -! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -! Include files -! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -! -! petsc.h - base PETSc routines petscvec.h - vectors -! petscsys.h - system routines petscmat.h - matrices -! petscksp.h - Krylov subspace methods petscpc.h - preconditioners - -#include "finclude/petsc.h" -!#include "finclude/petscvec.h" -!#include "finclude/petscmat.h" -!#include "finclude/petscpc.h" -!#include "finclude/petscksp.h" - -! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -! Variable declarations -! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -! -! Variables: -! ksp - linear solver context -! ksp - Krylov subspace method context -! pc - preconditioner context -! x, b, u - approx solution, right-hand-side, exact solution vectors -! A - matrix that defines linear system -! its - iterations for convergence -! norm - norm of solution error - - Vec x,b,u - Mat A - PC pc - KSP ksp - PetscScalar v,one,neg_one - double precision norm,tol - PetscErrorCode ierr - PetscInt i,j,II,JJ,Istart - PetscInt Iend,m,n,i1,its,five - PetscMPIInt rank - PetscTruth user_defined_pc,flg - -! Note: Any user-defined Fortran routines MUST be declared as external. - - external f_pcsetup_parms, f_pcapply_parms - external f_pcdestroy_parms - -! Common block to store data for user-provided preconditioner -! common /myshellpc/ diag -! Vec diag - -! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -! Beginning of program -! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - call PetscInitialize(PETSC_NULL_CHARACTER,ierr) - one = 1.0 - neg_one = -1.0 - i1 = 1 - m = 8 - n = 7 - five = 5 - call PetscOptionsGetInt(PETSC_NULL_CHARACTER,'-m',m,flg,ierr) - call PetscOptionsGetInt(PETSC_NULL_CHARACTER,'-n',n,flg,ierr) - call MPI_Comm_rank(PETSC_COMM_WORLD,rank,ierr) - -! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -! Compute the matrix and right-hand-side vector that define -! the linear system, Ax = b. -! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -! Create parallel matrix, specifying only its global dimensions. -! When using MatCreate(), the matrix format can be specified at -! runtime. Also, the parallel partitioning of the matrix is -! determined by PETSc at runtime. - - call MatCreate(PETSC_COMM_WORLD,A,ierr) - call MatSetSizes(A,PETSC_DECIDE,PETSC_DECIDE,m*n,m*n,ierr) - call MatSetType(A, MATAIJ,ierr) - call MatSetFromOptions(A,ierr) - call MatMPIAIJSetPreallocation(A,five,PETSC_NULL_INTEGER,five, - & PETSC_NULL_INTEGER,ierr) - call MatSeqAIJSetPreallocation(A,five,PETSC_NULL_INTEGER,ierr) - -! Currently, all PETSc parallel matrix formats are partitioned by -! contiguous chunks of rows across the processors. Determine which -! rows of the matrix are locally owned. - - call MatGetOwnershipRange(A,Istart,Iend,ierr) - -! Set matrix elements for the 2-D, five-point stencil in parallel. -! - Each processor needs to insert only elements that it owns -! locally (but any non-local elements will be sent to the -! appropriate processor during matrix assembly). -! - Always specify global row and columns of matrix entries. -! - Note that MatSetValues() uses 0-based row and column numbers -! in Fortran as well as in C. - - do 10, II=Istart,Iend-1 - v = -1.0 - i = II/n - j = II - i*n - if (i.gt.0) then - JJ = II - n - call MatSetValues(A,i1,II,i1,JJ,v,ADD_VALUES,ierr) - endif - if (i.lt.m-1) then - JJ = II + n - call MatSetValues(A,i1,II,i1,JJ,v,ADD_VALUES,ierr) - endif - if (j.gt.0) then - JJ = II - 1 - call MatSetValues(A,i1,II,i1,JJ,v,ADD_VALUES,ierr) - endif - if (j.lt.n-1) then - JJ = II + 1 - call MatSetValues(A,i1,II,i1,JJ,v,ADD_VALUES,ierr) - endif - v = 4.0 - call MatSetValues(A,i1,II,i1,II,v,ADD_VALUES,ierr) - 10 continue - -! Assemble matrix, using the 2-step process: -! MatAssemblyBegin(), MatAssemblyEnd() -! Computations can be done while messages are in transition, -! by placing code between these two statements. - - call MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY,ierr) - call MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY,ierr) - -! Create parallel vectors. -! - Here, the parallel partitioning of the vector is determined by -! PETSc at runtime. We could also specify the local dimensions -! if desired -- or use the more general routine VecCreate(). -! - When solving a linear system, the vectors and matrices MUST -! be partitioned accordingly. PETSc automatically generates -! appropriately partitioned matrices and vectors when MatCreate() -! and VecCreate() are used with the same communicator. -! - Note: We form 1 vector from scratch and then duplicate as needed. - - call VecCreateMPI(PETSC_COMM_WORLD,PETSC_DECIDE,m*n,u,ierr) - call VecDuplicate(u,b,ierr) - call VecDuplicate(b,x,ierr) - -! Set exact solution; then compute right-hand-side vector. - - call VecSet(u,one,ierr) - call MatMult(A,u,b,ierr) - -! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -! Create the linear solver and set various options -! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -! Create linear solver context - - call KSPCreate(PETSC_COMM_WORLD,ksp,ierr) - -! Set operators. Here the matrix that defines the linear system -! also serves as the preconditioning matrix. - - call KSPSetOperators(ksp,A,A,DIFFERENT_NONZERO_PATTERN,ierr) - -! Set linear solver defaults for this problem (optional). -! - By extracting the KSP and PC contexts from the KSP context, -! we can then directly directly call any KSP and PC routines -! to set various options. - - call KSPGetPC(ksp,pc,ierr) - tol = 1.e-7 - call KSPSetTolerances(ksp,tol,PETSC_DEFAULT_DOUBLE_PRECISION, - & PETSC_DEFAULT_DOUBLE_PRECISION,PETSC_DEFAULT_INTEGER,ierr) - -! -! Set a user-defined shell preconditioner if desired -! - call PetscOptionsHasName(PETSC_NULL_CHARACTER,'-user_defined_pc', - & user_defined_pc,ierr) - - if (user_defined_pc) then - -! (Required) Indicate to PETSc that we are using a shell preconditioner - call PCSetType(pc,PCSHELL,ierr) - -! (Required) Set the user-defined routine for applying the preconditioner - call PCShellSetApply(pc,f_PCApply_PARMS,ierr) - call PCShellSetContext(pc,pc,ierr) - -! (Optional) Set a name for the preconditioner, used for PCView() - call PCShellSetName(pc,"pARMS Preconditioner", ierr) - -! (Optional) Do any setup required for the preconditioner */ - call PCShellSetSetUp(pc, f_PCSetUp_PARMS,ierr) - -! (Optional) Frees any objects we created for the preconditioner - call PCShellSetDestroy(pc,f_PCDestroy_PARMS,ierr) - - else - call PCSetType(pc,PCBJACOBI,ierr) - endif - -! Set runtime options, e.g., -! -ksp_type -pc_type -ksp_monitor -ksp_rtol -! These options will override those specified above as long as -! KSPSetFromOptions() is called _after_ any other customization -! routines. - - call KSPSetFromOptions(ksp,ierr) - -! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -! Solve the linear system -! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - call KSPSolve(ksp,b,x,ierr) - -! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -! Check solution and clean up -! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -! Check the error - - call VecAXPY(x,neg_one,u,ierr) - call VecNorm(x,NORM_2,norm,ierr) - call KSPGetIterationNumber(ksp,its,ierr) - - if (rank .eq. 0) then - if (norm .gt. 1.e-12) then - write(6,100) norm,its - else - write(6,110) its - endif - endif - 100 format('Norm of error ',1pe10.4,' iterations ',i5) - 110 format('Norm of error < 1.e-12,iterations ',i5) - -! Free work space. All PETSc objects should be destroyed when they -! are no longer needed. - - call KSPDestroy(ksp,ierr) - call VecDestroy(u,ierr) - call VecDestroy(x,ierr) - call VecDestroy(b,ierr) - call MatDestroy(A,ierr) - -! Always call PetscFinalize() before exiting a program. - - call PetscFinalize(ierr) - end - diff --git a/lib/parms/examples/petsc/makefile b/lib/parms/examples/petsc/makefile deleted file mode 100755 index 9df38f283..000000000 --- a/lib/parms/examples/petsc/makefile +++ /dev/null @@ -1,31 +0,0 @@ -SHELL = /bin/sh - -include makefile.in - -allexe: test.ex ftest.ex dd-petsc.ex - -test.ex: test.o parms.o chkopts - ${LINKER} ${CLINKFLAGS} -o $@ test.o parms.o \ - ${SHFLAGS} ${LIBFLAGS} ${PARMS_LIBS} ${XLIBDIR} ${XLIB} \ - ${MPI_LIB_DIR} ${MPI_LIB} ${LAPACK_BLAS_LIB_DIR} ${LAPACK_BLAS_LIB} \ - ${MATH_LIB_DIR} ${MATH_LIB} ${PETSC_KSP_LIB} - ${RM} test.o - -ftest.ex: ftest.o parms.o fparms.o chkopts - ${F90LINKER} ${FLINKFLAGS} -o $@ ftest.o parms.o fparms.o \ - ${SHFLAGS} ${LIBFLAGS} ${PARMS_LIBS} ${XLIBDIR} ${XLIB} \ - ${MPI_LIB_DIR} ${MPI_LIB} ${LAPACK_BLAS_LIB_DIR} ${LAPACK_BLAS_LIB} \ - ${MATH_LIB_DIR} ${MATH_LIB} ${PETSC_KSP_LIB} - ${RM} ftest.o - -dd-petsc.ex: dd-petsc.o parms.o readmat.o skit.o chkopts - ${LINKER} ${CLINKFLAGS} -o $@ dd-petsc.o parms.o readmat.o skit.o \ - ${SHFLAGS} ${LIBFLAGS} ${PARMS_LIBS} ${XLIBDIR} ${XLIB} \ - ${MPI_LIB_DIR} ${MPI_LIB} ${LAPACK_BLAS_LIB_DIR} ${LAPACK_BLAS_LIB} \ - ${MATH_LIB_DIR} ${MATH_LIB} ${PETSC_KSP_LIB} - ${RM} dd-petsc.o - -cleanall:: - @-${RM} -rf *.o core *~ *# *.ex paramfile.* - - diff --git a/lib/parms/examples/petsc/makefile.in b/lib/parms/examples/petsc/makefile.in deleted file mode 100755 index 7308d8393..000000000 --- a/lib/parms/examples/petsc/makefile.in +++ /dev/null @@ -1,35 +0,0 @@ -SHELL = /bin/sh - - -include ${PETSC_DIR}/conf/rules -include ${PETSC_DIR}/conf/variables - -include ../../makefile.in - -# path of the header files of pARMS -ICFLAGS = -I../../include ${PETSC_INCLUDE} - -# path of the header files for implmentation of pARMS -ISRCINC = -I../../src/include ${PETSC_INCLUDE} - -# path of the header files -IFFLAGS = -I../../include ${PETSC_INCLUDE} - -# linker and options -F90LINKER = ${F90} -LINKER = ${FC} -CLINKFLAGS = ${FFLAGS} ${PETSC_FCPPFLAGS} -FLINKFLAGS = ${FFLAGS} ${PETSC_FCPPFLAGS} - -PARMS_LIBS = -L../../lib -lparms - -.c.o: - ${CC} ${ICFLAGS} ${ISRCINC} ${XIFLAGS} $(COPTFLAGS) \ - ${CFLAGS} ${CFFLAGS} ${PETSC_CCPPFLAGS} ${PETSC_FCPPFLAGS} $< -c -o $@ - -.F.o: - ${F90} -FI ${IFFLAGS} ${FFLAGS} $< -c -o $(@F) - -.f.o: - ${FC} ${FFLAGS} $< -c -o $(@F) - diff --git a/lib/parms/examples/petsc/matfileCmplx b/lib/parms/examples/petsc/matfileCmplx deleted file mode 100755 index c055ab2df..000000000 --- a/lib/parms/examples/petsc/matfileCmplx +++ /dev/null @@ -1,7 +0,0 @@ -../matrices/qc324.cua -## - - -###List of complex matrices### -../matrices/qc324.cua -../matrices/dwg961b.cua diff --git a/lib/parms/examples/petsc/matfileReal b/lib/parms/examples/petsc/matfileReal deleted file mode 100755 index c59950334..000000000 --- a/lib/parms/examples/petsc/matfileReal +++ /dev/null @@ -1,8 +0,0 @@ -../matrices/SHERMAN5 -## - - -###List of complex matrices### -../matrices/SHERMAN5 -../matrices/SAYLR4 -../matrices/SHERMAN3 diff --git a/lib/parms/examples/petsc/parms.c b/lib/parms/examples/petsc/parms.c deleted file mode 100755 index fda9a2eeb..000000000 --- a/lib/parms/examples/petsc/parms.c +++ /dev/null @@ -1,482 +0,0 @@ -/* This file contains pARMS interface routines to petsc. Note that this is - * only compatible with the latest version of petsc (version 3.1). For older - * versions (version 3.0) use parms.c_petsc_3.0. -*/ -#include -#include -#include -#include -#include "petscksp.h" -#include "parms.h" -#include "protos.h" - -static PC_PARMS *PREC; /* user-defined preconditioner context */ - -#undef __FUNCT__ -#define __FUNCT__ "PCCreate_PARMS" -PetscErrorCode PCCreate_PARMS(PC_PARMS **prec) -{ - PC_PARMS *newpc; - PetscErrorCode ierr; - - ierr = PetscNew(PC_PARMS,&newpc);CHKERRQ(ierr); - - newpc->map = 0; - newpc->A = 0; - newpc->pc = 0; - - *prec = newpc; - - return(0); -} - -#undef __FUNCT__ -#define __FUNCT__ "PCSetUp_PARMS" -PetscErrorCode PCSetUp_PARMS(PC ctx) -{ - Mat pmat; - PC *precon = &ctx; - parms_Map map; - parms_Mat A; - parms_PC pc; - char *iluname, *pname; - int *maptmp, *mapptr, npro, rank, low, high; - int m, n, lsize, *ia, *ja, *ja1, ncols, pos, i; - int length, *im; - double fill_fact; - PetscScalar *aa, *aa1; - const PetscInt *cols; - const PetscScalar *values; - -/* pARMS options file name */ - char fname[] = "parms_opts"; - - MPI_Comm_size(PETSC_COMM_WORLD, &npro); - MPI_Comm_rank(PETSC_COMM_WORLD, &rank); - -/* ------------ Check to make sure compilations for pARMS and PETSC are consistent -----*/ -#if defined(DBL_CMPLX) - if(sizeof(PetscScalar) == sizeof(double)) - { - fprintf(stderr," %d: Compilation mismatch: Using complex version of pARMS with real version of PETSC !!\n", rank); - fprintf(stderr," %d: Compile real version of pARMS to use real version of PETSC \n", rank); - MPI_Finalize(); - exit(0); - } -#else - if(sizeof(PetscScalar) == sizeof(double _Complex)) - { - fprintf(stderr," %d: Compilation mismatch: Using real version of pARMS with complex version of PETSC !!\n", rank); - fprintf(stderr," %d: Compile complex version of pARMS to use complex version of PETSC \n", rank); - MPI_Finalize(); - exit(0); - } -#endif - -/* Get preconditioning matrix from petsc and setup -/ pARMS structs -*/ - PCGetOperators(*precon,PETSC_NULL,&pmat,PETSC_NULL); - - MatGetSize(pmat, &n, &m); - MatGetOwnershipRange(pmat, &low, &high); - PetscMalloc((npro+1)*sizeof(int), &mapptr); - PetscMalloc(n*sizeof(int), &maptmp); - lsize = high - low; - - MPI_Allgather(&lsize, 1, MPI_INT, mapptr+1, 1, MPI_INT, PETSC_COMM_WORLD); - mapptr[0] = 1; - for (i = 0; i < npro; i++) { - mapptr[i+1] += mapptr[i]; - } - for (i = 0; i < n; i++) { - maptmp[i] = i+1; - } - -/* create parms map object */ - if(PREC == NULL) - parms_MapCreateFromPtr(&map, n, maptmp, mapptr, MPI_COMM_WORLD, 1, NONINTERLACED); - else /*---- map re-use data structure */ - map = PREC->map; - -/* create parms mat object */ - if(PREC == NULL) - parms_MatCreate(&A, map); - else - { -/*------ Data structure is being re-used ----*/ - A = PREC->A; - parms_MatReset(A, DIFFERENT_NONZERO_STRUCTURE); - } -/* setup and copy csr data structure for parms */ - PetscMalloc((lsize+1)*sizeof(int), &ia); - ia[0] = 1; - length = lsize * 8; - PetscMalloc(length*sizeof(int), &ja); - PetscMalloc(length*sizeof(PetscScalar), &aa); - - for (i = low; i < high; i++) - { - pos = ia[i-low]-1; - MatGetRow(pmat, i, &ncols, &cols, &values); - ia[i-low+1] = ia[i-low] + ncols; - - if (ia[i-low+1] >= length) - { - length += ncols; - PetscMalloc(length*sizeof(int), &ja1); - memcpy(ja1, ja, (ia[i-low]-1)*sizeof(int)); - PetscFree(ja); - ja = ja1; - PetscMalloc(length*sizeof(PetscScalar), &aa1); - memcpy(aa1, aa, (ia[i-low]-1)*sizeof(PetscScalar)); - PetscFree(aa); - aa = aa1; - } - memcpy(&ja[pos], cols, ncols*sizeof(int)); - memcpy(&aa[pos], values,ncols*sizeof(PetscScalar)); - MatRestoreRow(pmat, i, &ncols, &cols, &values); - } - -/* csr info is for local matrix so initialize im[] locally */ - PetscMalloc(lsize*sizeof(int), &im); - memcpy(im, &maptmp[mapptr[rank]-1], lsize*sizeof(int)); - -/* convert csr aa vector from petscscalar to double */ - for(i=0; ipc; - -/* Set Preconditioner options based on parameters in - parms_opts file -*/ - parms_SetOptions(pc, fname); - parms_PCSetup(pc); - -/* Print some preconditioner stats */ - parms_PCGetName(pc, &pname); - parms_PCILUGetName(pc, &iluname); - parms_PCGetRatio(pc, &fill_fact); - if (rank == 0) { - printf("The global preconditioner %2s %s\n", " ", pname); - printf("The local preconditioner %4s %s\n", " ", iluname); - printf("The memory usage %12s %-4.2f\n", "=", fill_fact); - printf("The number of processors %4s %-4d\n", "=", npro); - } - - -/* Setup preconditioner object for petsc */ - if(PREC == NULL) - { - PCCreate_PARMS(&PREC); - PREC->map = map; - PREC->A = A; - PREC->pc = pc; - } - return(0); -} - -/* prec - precondition object - b - right-hand-side vector - x - solution vector on return (preconditioned vector) -*/ -#undef __FUNCT__ -#define __FUNCT__ "PCApply_PARMS" -PetscErrorCode PCApply_PARMS(PC dummy,Vec b,Vec x) -{ - PetscErrorCode ierr; - PetscScalar *x1, *b1; - FLOAT *rhs, *rhs1, *sol, *sol1; - int nloc, i, rank; - - MPI_Comm_rank(PETSC_COMM_WORLD, &rank); -/* get petsc vector */ - ierr = VecGetArray(b, &b1);CHKERRQ(ierr); - ierr = VecGetArray(x, &x1);CHKERRQ(ierr); - -/* get size of local system and allocate memory - for rhs and solution vectors -*/ - nloc = parms_MapGetLocalSize(PREC->map); - PetscMalloc((nloc)*sizeof(FLOAT), &rhs); - PetscMalloc((nloc)*sizeof(FLOAT), &sol); - PetscMalloc((nloc)*sizeof(FLOAT), &rhs1); - PetscMalloc((nloc)*sizeof(FLOAT), &sol1); - -/* convert from petsscalar to float */ - for(i=0; imap); - -/* Do preconditioner solve */ - parms_PCApply(PREC->pc, rhs, sol1); - - parms_VecInvPermAux(sol1, sol, PREC->map); - -/* convert from float to petscscalar*/ - for(i=0; iA)); - parms_MapFree(&(PREC->map)); - parms_PCFree(&(PREC->pc)); - - ierr = PetscFree(PREC);CHKERRQ(ierr); - return(0); -} - - -/* Some auxilliary functions -============================ -*/ - -/* Set pARMS preconditioner options. It is easier - to simply read from file, so options are set - in the file parms_opts -*/ -int parms_SetOptions(parms_PC pc, char fname[]) -{ - FILE *fp; - char buf[MAX_LINE], *p, *p1, *p2; - int i, its, par, lfil[7], nlev, meth[8]; - double eps, droptol[7], tolind; - - for(i=0; i<8; i++) - meth[i] = 0; - - if (NULL == (fp = fopen(fname, "r"))) { - fprintf(stderr, "cannot open parms options file %s\n", fname); - exit(1); - } - -/*----Get parallel Precon Type ---*/ - if(fgets(buf, MAX_LINE, fp)== NULL){ - fprintf(stderr, "Error reading global precon type"); - exit(1); - } - STR2U(p, p1); - if (!strcmp(p1, "RAS")) { - parms_PCSetType(pc, PCRAS); - } - else if (!strcmp(p1, "SCHUR")){ - parms_PCSetType(pc, PCSCHUR); - } - else if (!strcmp(p1, "BJ")) { - parms_PCSetType(pc, PCBJ); - } - free(p1); -/* --- Get Local Precon type ---*/ - if(fgets(buf, MAX_LINE, fp) == NULL){ - fprintf(stderr, "Error reading local precon type"); - exit(1); - } - STR2U(p, p1); - if (!strcmp(p1, "ILU0")) { - parms_PCSetILUType(pc, PCILU0); - } - else if (!strcmp(p1, "ILUK")) { - parms_PCSetILUType(pc, PCILUK); - } - else if (!strcmp(p1, "ILUT")) { - parms_PCSetILUType(pc, PCILUT); - } - else if (!strcmp(p1, "ARMS")) { - parms_PCSetILUType(pc, PCARMS); - } - free(p1); -/*--- Get tolerance for local solve---*/ - if(fscanf(fp,"%lf",&eps) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - parms_PCSetInnerEps(pc, eps); - while(fgetc(fp) != '\n'); -/*--- Get number of levels ---*/ - if(fscanf(fp,"%d",&nlev) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - parms_PCSetNlevels(pc, nlev); - while(fgetc(fp) != '\n'); -/*--- Use symmetric or nonsymmetric perm ---*/ - if(fscanf(fp,"%d",&par) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - parms_PCSetPermType(pc, par); - while(fgetc(fp) != '\n'); -/*--- B block size ---*/ - if(fscanf(fp,"%d",&par) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - parms_PCSetBsize(pc, par); - while(fgetc(fp) != '\n'); -/*--- Tolerance for independent sets ---*/ - if(fscanf(fp,"%lf",&tolind) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - parms_PCSetTolInd(pc, tolind); - while(fgetc(fp) != '\n'); -/*--- Inner Krylov dimension ---*/ - if(fscanf(fp,"%d",&par) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - parms_PCSetInnerKSize(pc, par); - while(fgetc(fp) != '\n'); -/*--- maximum number of inner iterations ---*/ - if(fscanf(fp,"%d",&its) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - parms_PCSetInnerMaxits(pc, its); - while(fgetc(fp) != '\n'); -/*--- nonsymmetric perm. for interlevel blocks ---*/ - if(fscanf(fp,"%d",&meth[0]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Column Permutations - interlevel blocks ---*/ - if(fscanf(fp,"%d",&meth[1]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Row scaling - interlevel blocks ---*/ - if(fscanf(fp,"%d",&meth[2]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Column scaling - interlevel blocks ---*/ - if(fscanf(fp,"%d",&meth[3]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Nonsymm. Perm. for last level SC. ---*/ - if(fscanf(fp,"%d",&meth[4]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Col. Perm. for last level SC. ---*/ - if(fscanf(fp,"%d",&meth[5]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Row scaling - last level SC. ---*/ - if(fscanf(fp,"%d",&meth[6]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- Column scaling - last level SC. ---*/ - if(fscanf(fp,"%d",&meth[7]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- lfil0 for ilut, iluk, and arms for lfil[0-3] ---*/ - if(fscanf(fp,"%d",&lfil[0]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- lfil for shur ---*/ - if(fscanf(fp,"%d",&lfil[4]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- lfil for ILUT L and U ---*/ - if(fscanf(fp,"%d",&lfil[5]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- droptol0(droptol[0=3], L, U, L^{-1}F, EU^{-1}---*/ - if(fscanf(fp,"%lf",&droptol[0]) != 1){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- droptol4(for schur complements at each level) ---*/ - if(fscanf(fp,"%lf",&droptol[4]) !=1 ){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); -/*--- droptol5(for ILUT in last level Schur Complement) ---*/ - if(fscanf(fp,"%lf",&droptol[5]) !=1 ){ - fprintf(stderr, "Error reading value"); - exit(1); - } - while(fgetc(fp) != '\n'); - - droptol[1] = droptol[2] = droptol[3] = droptol[0]; - droptol[6] = droptol[5]; - - lfil[1] = lfil[2] = lfil[3] = lfil[0]; - lfil[6] = lfil[5]; - - parms_PCSetPermScalOptions(pc, &meth[0], 1); - parms_PCSetPermScalOptions(pc, &meth[4], 0); - - parms_PCSetFill(pc, lfil); - parms_PCSetTol(pc, droptol); - - fclose(fp); - - return 0; -} diff --git a/lib/parms/examples/petsc/parms_opts b/lib/parms/examples/petsc/parms_opts deleted file mode 100755 index f4174dbc7..000000000 --- a/lib/parms/examples/petsc/parms_opts +++ /dev/null @@ -1,26 +0,0 @@ -bj parallel preconditioner (RAS, SCHUR, BJ) -ilu0 local preconditioner (ilu0, iluk, ilut, arms) -0.01 eps(tolerance for inner iteration) -5 nlev(number of levels for ARMS, ILUK) -1 non-symmetric permutation - ddPQ or not (1 = ddPQ) -250 bsize(block size for block independent sets) -0.40 tolind(tolerance used in independent set) -10 kim (krylov subspace size for inner iteration) -10 itsgmr(inner fgmres) -0 nonsymmetric permutations for interlevel blocks (1:yes, 0:no) -0 permutations of columns for interlevel blocks (1:yes, 0:no) -0 row scaling for interlevel blocks (1:yes, 0:no) -0 column scaling for interlevel blocks (1:yes, 0:no) -0 nonsymmetric perm.s for last Schur complement (1:yes, 0:no) -0 permutations of columns for last Schur complement (1:yes, 0:no) -0 row scaling for last Schur complement (1:yes, 0:no) -0 column scaling for last Schur complement (1:yes, 0:no) -20 lfil0(ilut, iluk and arms for lfil[0-3]) -20 lfil4(schur) -20 lfil5(ILUT L, ILUT U) -0.00001 droptol0(droptol[0=3], L, U, L^{-1}F, EU^{-1} -0.001 droptol4(for schur complements at each level) -0.001 droptol5(for ILUT in last level Schur Complement) -## - - diff --git a/lib/parms/examples/petsc/protos.h b/lib/parms/examples/petsc/protos.h deleted file mode 100755 index 84649eb8c..000000000 --- a/lib/parms/examples/petsc/protos.h +++ /dev/null @@ -1,113 +0,0 @@ -#include -#include "petscksp.h" -/* REQUIRED */ - -#define MAX_LINE 100 -#define BUFFLEN 200 -#define STR2U(p, p1) \ - if (1) { \ - p = buf; \ - while (' ' == *p) { \ - p++; \ - } \ - p1 = malloc((strlen(p)+1)*sizeof(*p1)); \ - p2 = p1; \ - while (' ' != *p) { \ - *p2 = toupper(*p); \ - ++p; \ - ++p2; \ - } \ - *p2 = '\0'; \ - } -/* Define context for user-provided preconditioner */ - -typedef struct { - parms_Map map; - parms_Mat A; - parms_PC pc; -}PC_PARMS; - -/* Declare routines for user-provided preconditioner */ - -PetscErrorCode PCCreate_PARMS(PC_PARMS **prec); -PetscErrorCode PCSetUp_PARMS(PC ctx); -PetscErrorCode PCApply_PARMS(PC dummy,Vec b,Vec x); -PetscErrorCode PCDestroy_PARMS(PC dummy); -int parms_SetOptions(parms_PC pc, char *fname); - -/* -THE FUNCTIONS BELOW ARE ONLY REQUIRED BY THE HB FILE FORMAT EXAMPLE "dd-petsc.c". -*/ -#ifndef _AUX_HEADER_INCLUDED_H -#define _AUX_HEADER_INCLUDED_H - -#if defined(FORTRAN_CAPS) -#define wreadmtc_ WREADMTC -#define csrcsc_ CSRCSC -#define aplb_ APLB -#define dse_ DSE -#elif defined(FORTRAN_DOUBLE_UNDERSCORE) -#define wreadmtc_ wreadmtc__ -#define csrcsc_ csrcsc__ -#define aplb_ aplb__ -#define dse_ dse__ -#elif !defined(FORTRAN_UNDERSCORE) -#define wreadmtc_ wreadmtc -#define csrcsc_ csrcsc -#define aplb_ aplb -#define dse_ dse -#endif - -#define BUFLEN 100 - -/* Routine to read test matrix */ -extern int read_matrix(char mname[BUFLEN]); -extern void wreadmtc_(int *nmax, int *nzmax, int *job, char *fname,int - *len, double *a, int *ja, int *ia, double *rhs, - int *nrhs, char *guesol, int *nrow, int *ncol, - int *nnz, char *title, char *key, char *type, - int *ierr); -extern void csrcsc_(int *n, int *job, int *ipos, double *a, int *ja, - int *ia, double *ao, int *jao, int *iao); -extern void aplb_(int *nrow, int *ncol, int *job, double *a, int *ja, - int *ia, double *b, int *jb, int *ib, double *c, int - *jc, int *ic, int *nnzmax, int *iw, int *ierr); - - - -/*---------- complex routines ------------*/ -#if defined(DBL_CMPLX) -#if defined(FORTRAN_CAPS) -#define wreadmtc_ WREADMTC -#define readmtc_ READMTC -#define csrcsc_ CSRCSC -#define aplb_ APLB -#elif defined(FORTRAN_DOUBLE_UNDERSCORE) -#define wreadmtc_ wreadmtc__ -#define readmtc_ readmtc__ -#define csrcsc_ csrcsc__ -#define aplb_ aplb__ -#elif !defined(FORTRAN_UNDERSCORE) -#define wreadmtc_ wreadmtc -#define readmtc_ readmtc -#define csrcsc_ csrcsc -#define aplb_ aplb -#endif -extern void zwreadmtc_(int *nmax, int *nzmax, int *job, char *fname,int - *len, complex double *a, int *ja, int *ia, complex double *rhs, - int *nrhs, char *guesol, int *nrow, int *ncol, - int *nnz, char *title, char *key, char *type, - int *ierr); -extern void zreadmtc_(int *nmax, int *nzmax, int *job, char *fname, complex double - *a, int *ja, int *ia, complex double *rhs, int *nrhs, char - *guesol, int *nrow, int *ncol,int *nnz, char *title, - char *key, char *type, int *ierr); -extern void zcsrcsc_(int *n, int *job, int *ipos, complex double *a, int *ja, - int *ia, complex double *ao, int *jao, int *iao); -extern void zaplb_(int *nrow, int *ncol, int *job, complex double *a, int *ja, - int *ia, complex double *b, int *jb, int *ib, complex double *c, int - *jc, int *ic, int *nnzmax, int *iw, int *ierr); -#endif - -#endif - diff --git a/lib/parms/examples/petsc/readmat.c b/lib/parms/examples/petsc/readmat.c deleted file mode 100755 index 70c0960ea..000000000 --- a/lib/parms/examples/petsc/readmat.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include -#include -#include - -#define BUFLEN 100 - -int read_matrix(char mname[BUFLEN]) -{ - FILE *fmat; - char buf[BUFLEN], *tmpstring; - -/*------- Read a matrix from list of Matrices -------------*/ -#if defined(DBL_CMPLX) - if (NULL == (fmat = fopen("./matfileCmplx", "r"))) { - fprintf(stderr, "cannot open file: matfile\n"); - exit(1); - } -#else - if (NULL == (fmat = fopen("./matfileReal", "r"))) { - fprintf(stderr, "cannot open file: matfile\n"); - exit(1); - } -#endif - if(fgets(buf,BUFLEN,fmat) == NULL){ - fprintf(stderr, "Error reading matrix list"); - exit(1); - } - tmpstring = (char *) strtok(buf, " "); - tmpstring++; - tmpstring[strlen(tmpstring)-1]='\0'; - strcpy(mname, --tmpstring); - - fclose(fmat); - - return 0; -} - diff --git a/lib/parms/examples/petsc/test.c b/lib/parms/examples/petsc/test.c deleted file mode 100755 index f291c6a68..000000000 --- a/lib/parms/examples/petsc/test.c +++ /dev/null @@ -1,214 +0,0 @@ - -static char help[] = "Solves a linear system in parallel with KSP. Also\n\ -illustrates setting a user-defined shell preconditioner and using the\n\ -macro __FUNCT__ to define routine names for use in error handling.\n\ -Input parameters include:\n\ - -user_defined_pc : Activate a user-defined preconditioner\n\n"; - -/*T - Concepts: KSP^basic parallel example - Concepts: PC^setting a user-defined shell preconditioner - Concepts: error handling^Using the macro __FUNCT__ to define routine names; - Processors: n -T*/ - -/* - Include "petscksp.h" so that we can use KSP solvers. Note that this file - automatically includes: - petsc.h - base PETSc routines petscvec.h - vectors - petscsys.h - system routines petscmat.h - matrices - petscis.h - index sets petscksp.h - Krylov subspace methods - petscviewer.h - viewers petscpc.h - preconditioners -*/ -#include -#include -#include -#include -#include "petscksp.h" -#include "parms.h" -#include "protos.h" - -/* - User-defined routines. Note that immediately before each routine below, - we define the macro __FUNCT__ to be a string containing the routine name. - If defined, this macro is used in the PETSc error handlers to provide a - complete traceback of routine names. All PETSc library routines use this - macro, and users can optionally employ it as well in their application - codes. Note that users can get a traceback of PETSc errors regardless of - whether they define __FUNCT__ in application codes; this macro merely - provides the added traceback detail of the application routine names. -*/ - -#undef __FUNCT__ -#define __FUNCT__ "main" -int main(int argc,char **args) -{ - Vec x,b,u; /* approx solution, RHS, exact solution */ - Mat A; /* linear system matrix */ - KSP ksp; /* linear solver context */ - PC pc; /* preconditioner context */ - PetscReal norm; /* norm of solution error */ - PetscScalar v,one = 1.0,none = -1.0; - PetscInt i,j,Ii,J,Istart,Iend,m = 8,n = 7,its; - PetscErrorCode ierr; - PetscTruth user_defined_pc; - - PetscInitialize(&argc,&args,(char *)0,help); - ierr = PetscOptionsGetInt(PETSC_NULL,"-m",&m,PETSC_NULL);CHKERRQ(ierr); - ierr = PetscOptionsGetInt(PETSC_NULL,"-n",&n,PETSC_NULL);CHKERRQ(ierr); - - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Compute the matrix and right-hand-side vector that define - the linear system, Ax = b. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - /* - Create parallel matrix, specifying only its global dimensions. - When using MatCreate(), the matrix format can be specified at - runtime. Also, the parallel partioning of the matrix is - determined by PETSc at runtime. - */ - ierr = MatCreate(PETSC_COMM_WORLD,&A);CHKERRQ(ierr); - ierr = MatSetSizes(A,PETSC_DECIDE,PETSC_DECIDE,m*n,m*n);CHKERRQ(ierr); - ierr = MatSetFromOptions(A);CHKERRQ(ierr); - - /* - Currently, all PETSc parallel matrix formats are partitioned by - contiguous chunks of rows across the processors. Determine which - rows of the matrix are locally owned. - */ - ierr = MatGetOwnershipRange(A,&Istart,&Iend);CHKERRQ(ierr); - - /* - Set matrix elements for the 2-D, five-point stencil in parallel. - - Each processor needs to insert only elements that it owns - locally (but any non-local elements will be sent to the - appropriate processor during matrix assembly). - - Always specify global rows and columns of matrix entries. - */ - for (Ii=Istart; Ii0) {J = Ii - n; ierr = MatSetValues(A,1,&Ii,1,&J,&v,INSERT_VALUES);CHKERRQ(ierr);} - if (i0) {J = Ii - 1; ierr = MatSetValues(A,1,&Ii,1,&J,&v,INSERT_VALUES);CHKERRQ(ierr);} - if (j -pc_type -ksp_monitor -ksp_rtol - These options will override those specified above as long as - KSPSetFromOptions() is called _after_ any other customization - routines. - */ - ierr = KSPSetFromOptions(ksp);CHKERRQ(ierr); - - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Solve the linear system - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - ierr = KSPSolve(ksp,b,x);CHKERRQ(ierr); - - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Check solution and clean up - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - /* - Check the error - */ - ierr = VecAXPY(x,none,u);CHKERRQ(ierr); - ierr = VecNorm(x,NORM_2,&norm);CHKERRQ(ierr); - ierr = KSPGetIterationNumber(ksp,&its);CHKERRQ(ierr); - ierr = PetscPrintf(PETSC_COMM_WORLD,"Norm of error %A iterations %D\n",norm,its);CHKERRQ(ierr); - - /* - Free work space. All PETSc objects should be destroyed when they - are no longer needed. - */ - ierr = KSPDestroy(ksp);CHKERRQ(ierr); - ierr = VecDestroy(u);CHKERRQ(ierr); ierr = VecDestroy(x);CHKERRQ(ierr); - ierr = VecDestroy(b);CHKERRQ(ierr); ierr = MatDestroy(A);CHKERRQ(ierr); - - ierr = PetscFinalize();CHKERRQ(ierr); - return 0; - -} - diff --git a/lib/parms/include/fparms.h b/lib/parms/include/fparms.h deleted file mode 100755 index d49b2bea9..000000000 --- a/lib/parms/include/fparms.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifdef VOID_POINTER_SIZE_4 -#define parms_Map integer*4 -#define parms_Vec integer*4 -#define parms_Mat integer*4 -#define parms_PC integer*4 -#define parms_Solver integer*4 -#define parms_Viewer integer*4 -#define parms_Timer integer*4 -//#elif defined(VOID_POINTER_SIZE_8) -#else -#define parms_Map integer*8 -#define parms_Vec integer*8 -#define parms_Mat integer*8 -#define parms_PC integer*8 -#define parms_Solver integer*8 -#define parms_Viewer integer*8 -#define parms_Timer integer*8 -#endif - integer :: NONINTERLACED, INTERLACED - parameter(NONINTERLACED=1, INTERLACED=0) - integer :: INSERT, ADD - parameter(INSERT=0, ADD=1) - integer :: SAME_NONZERO_STRUCTURE, DIFFERENT_NONZERO_STRUCTURE - parameter(SAME_NONZERO_STRUCTURE=0,DIFFERENT_NONZERO_STRUCTURE=1) - integer :: PCBJ, PCSCHUR, PCRAS, PCSCHURRAS - integer :: PCILU0, PCILUK, PCILUT, PCARMS - parameter(PCBJ=0, PCSCHUR=1, PCRAS=2, PCSCHURRAS=3) - parameter(PCILU0=0, PCILUK=1, PCILUT=2, PCARMS=3) - integer :: SOLFGMRES, SOLGMRES, SOLBICGS, SOLCG, SOLPBICGS, SOLPBICGS_RAS, SOLBICGS_RAS - integer :: MAXITS, KSIZE, DTOL, NEIG - parameter(SOLFGMRES=0,SOLGMRES=1, SOLBICGS=2, SOLCG=3, SOLPBICGS=4, SOLPBICGS_RAS=5, SOLBICGS_RAS=6) - parameter(MAXITS=0,KSIZE=1,DTOL=2,NEIG=3) - diff --git a/lib/parms/include/parms.h b/lib/parms/include/parms.h deleted file mode 100755 index f9e6e01bd..000000000 --- a/lib/parms/include/parms.h +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @file parms.h - * @author Zhongze Li - * @date Sun Oct 22 16:39:37 2006 - * - * @brief This is the main pARMS include file. It should be included - * in all application program. - * - */ - -#ifndef _PARMS_INCLUDED_H_ -#define _PARMS_INCLUDED_H_ - -#include "parms_mem.h" -#include "parms_map.h" -#include "parms_vec.h" -#include "parms_mat.h" -#include "parms_pc.h" -#include "parms_solver.h" -#include "parms_viewer.h" -#include "parms_timer.h" - -#endif diff --git a/lib/parms/include/parms_comm.h b/lib/parms/include/parms_comm.h deleted file mode 100755 index 591aa7cd4..000000000 --- a/lib/parms/include/parms_comm.h +++ /dev/null @@ -1,111 +0,0 @@ -/** - * @file parms_comm.h - * @author Zhongze Li - * @date Tue Oct 17 10:01:17 2006 - * - * @brief The communication handler used for the matrix-vector - * product. - * - * - */ - -#ifndef _PARMS_COMM_H_ -#define _PARMS_COMM_H_ - -#include "parms_sys.h" -#include "parms_viewer.h" - -PARMS_CXX_BEGIN - -typedef struct parms_Comm_ *parms_Comm; - -/** - * Create a parms_Comm object. - * - * @param self A pointer to the parms_Comm object. - * @param comm MPI communicator - * - * @return 0 on success. - */ -extern int parms_CommCreate(parms_Comm *self, MPI_Comm comm); - -/** - * Free the memory for the parms_Comm object. - * - * @param self A pointer to the parms_Comm object. - * - * @return 0 on success. - */ -extern int parms_CommFree(parms_Comm *self); - -/** - * Exchange data for the matrix-vector product. - * - * This functions exchanges the interface variables. The offset - * indicates the distance between start of the data to be exchanged - * and the start of the local vector. This is useful for Schur - * complement based preconditioner since the data parameter is only - * the interface part of the local vector rather than the entire local - * vector. - - * @param self A parms_Comm object. - * @param data The data to be exchanged. - * @param offset The distance between the start of the data and the - * beginning of the local vector. - * @return 0 on success. - */ -extern int parms_CommDataBegin(parms_Comm self, void *data, int - offset); -/** - * Wait for all messages. - * - * After calling this function, you may use data in receive buffer - * safely. - * - * @param self A parms_Comm object. - * - * @return 0 on success. - */ -extern int parms_CommDataEnd(parms_Comm self); - -/** - * Dump the communication handler comm. - * - * @param self A communication handler. - * @param v A parms_Viewer object. - * - * @return 0 on success. - */ -extern int parms_CommView(parms_Comm self, parms_Viewer v); - -/** - * Get the total numbjer of variables received. - * - * @param self A communication handler. - * - * @return The number of variables received. - */ -extern int parms_CommGetNumRecv(parms_Comm self); - -/** - * Get receive buffer. - * - * @param self A communication handler. - * @param rbuf Receive buffer returned. - * - * @return 0 on success. - */ -extern int parms_CommGetRecvBuf(parms_Comm self, FLOAT **rbuf); - -/** - * Get the number of variables sent. - * - * @param self A communication handler. - * - * @return The number of vars sent. - */ -extern int parms_CommGetNumSend(parms_Comm self); - -PARMS_CXX_END - -#endif diff --git a/lib/parms/include/parms_map.h b/lib/parms/include/parms_map.h deleted file mode 100755 index 03ad9ea61..000000000 --- a/lib/parms/include/parms_map.h +++ /dev/null @@ -1,262 +0,0 @@ -/** - * @file parms_map.h - * @author Zhongze Li - * @date Tue Oct 17 10:02:53 2006 - * - * @brief Functions related to the parms_Map object. - * - * parms_Map describes how variables are distributed across - * processors. It is used for creating parms_Mat - * objects. - */ - -#ifndef _PARMS_MAP_H_ -#define _PARMS_MAP_H_ - -#include "parms_sys.h" -#include "parms_viewer.h" - -PARMS_CXX_BEGIN - -typedef struct parms_Map_ *parms_Map; - -/** - * Create a parms_Map object on the local processor. - * - * @param self A pointer to the parms_Map object created. - * @param gsize The size of unknowns on the local processor. - * @param offset The start index. - * - 1 FORTRAN - * - 0 C - * - * @return 0 on success. - * - */ -extern int parms_MapCreateFromLocal(parms_Map *self, int gsize, int - offset); - -/** - * Create a parms_Map object based on the Metis partitioning. - * - * @param self A pointer to the parms_Map object created. - * @param gsize The total number of vertices. - * @param npar An integer array of size gsize. node \f$i\f$ - * resides on PE npar[i]. - * @param comm MPI communicator. - * @param offset The start index. - * - 1 FORTRAN - * - 0 C - * @param dof The number of variables associated with each - * vertex. - * @param VARSTYPE Assuming the variables \f$u_i, v_i\f$ are - * associated with vertex \f$i\f$, two styles of - * numbering variables are as follows: - * - INTERLACED. Variables are numbered in the - * order of \f$u_1, v_1, u_2, v_2, \cdots\f$; - * - NONINTERLACED. Variables are numbered in the - * order of \f$u_1, u_2, u_3,...,v_1, v_2,...\f$. - * - * @return 0 on success. - */ -extern int parms_MapCreateFromGlobal(parms_Map *self, int gsize, int *npar, - MPI_Comm comm, int offset, int dof, - VARSTYPE vtype); - -/** - * Create a parms_Map object based on the output of ParMetis. - * - * @param self A parms_Map object created. - * @param vtxdist An integer array of size np+1, where np is the - * number of PEs. This array indicates the range of - * vertices that are local to each processor. PE i - * stores vertices in the range of [vtxdist[i], - * vtxdist[i+1]). - * @param part An array of size equal to the number of - * locally-stored vertices. part[j] indicates the ID - * of the PE to which the vertex with local index j - * and global index vtxdist[pid]+j belongs (pid is ID - * of local PE). - * @param comm MPI communicator. - * @param offset The start index. - * - 1 FORTRAN - * - 0 C - * @param dof The number of variables associated with each - * vertex. - * @param vtype Assuming the variables u_i, v_i are associated - * with vertex i, two styles of numbering variables - * are as follows: - * - INTERLACED. Variables are numbered in the - * order of \f$u_1, v_1, u_2, v_2, \cdots\f$; - * - NONINTERLACED. Variables are numbered in the - * order of \f$u_1, u_2, u_3,...,v_1, v_2,...\f$. - * - * @return 0 on success. - */ -extern int parms_MapCreateFromDist(parms_Map *self, int *vtxdist, int - *part, MPI_Comm comm, int offset, - int dof, VARSTYPE vtype); - -/** - * Create a parms_Map object with the default partioning strategy in - * PETSc. - * - * @param self A parms_Map object created. - * @param m The local size of variables. - * @param M The global size of variables. - * @param comm MPI communicatior. - * - * @return 0 on success. - */ -extern int parms_MapCreateFromPetsc(parms_Map *self, int m, int M, - MPI_Comm comm); - -/** - * Create a parms_Map object. - * - * @param self A parms_Map object created. - * @param gsize The total number of vertices. - * @param nodes A list of all vertices stored PE by PE. - * @param p2nodes An integer array of size np+1, np is the number of - * PEs. If k1 = p2nodes[i], k2 = p2nodes[i+1] - * , then PE i contains the vertices in the - * range of [[nodes[k1], [nodes[k2-1]]. - * @param comm MPI communication. - * @param dof The number of variables associated with each - * node. - * @param vtype Assuming the variables \f$u_i, v_i\f$ are - * associated with vertex \f$i\f$, two style of - * numbering variables are as follows: - * - INTERLACED. Variables are numbered in the - * order of \f$u_1, v_1, u_2, v_2, \cdots\f$; - * - NONINTERLACED. Variables are numbered in the - * order of \f$u_1, u_2, u_3,...,v_1, v_2,...\f$. - * - * @return 0 on success. - */ -extern int parms_MapCreateFromPtr(parms_Map *self, int gsize, int - *nodes, int *p2nodes, MPI_Comm - comm, int dof, VARSTYPE vtype); - -/** - * Free the memory for the parms_Map object pointed to by self. - * - * @param self A pointer to the parms_Map object. - * - * @return 0 on success. - */ -extern int parms_MapFree(parms_Map *self); - -/** - * Get the size of variables on the local PE. - * - * @param self A parms_Map object. - * - * @return The size of variables on the local PE. - */ -extern int parms_MapGetLocalSize(parms_Map self); - -/** - * Get global size of variables. - * - * @param self A parms_Map object. - * - * @return The global size of variables rather than vertices. - */ -extern int parms_MapGetGlobalSize(parms_Map self); - -/** - * Get PE's ID. - * - * @param self A parms_Map object. - * - * @return The PE's ID. - */ -extern int parms_MapGetPid(parms_Map self); - -/** - * Get the number of PEs. - * - * @param self A parms_Map object. - * - * @return The number of PEs. - */ -extern int parms_MapGetNumProcs(parms_Map self); - -/** - * Dump the parms_Map object. - * - * @param self A parms_Map object. - * @param v A parms_Viewer object. - * - * @return 0 on success. - */ -extern int parms_MapView(parms_Map self, parms_Viewer v); - -/** - * Get local index for a given global index. - * - * Return a pointer to an integer. If it is NULL, then the variable - * with global index gindex doesn't reside on the local - * PE. Otherwise, it points to an address of a variable - * whose value is local index. - * - * @param self A parms_Map object. - * @param gindex A global index. - * - * @return A pointer to an integer whose value is the corresponding - * local index. - */ -extern int *parms_MapGlobalToLocal(parms_Map self, int gindex); - -/** - * Get local variable array - * -*/ -extern int parms_MapGetGlobalIndices(parms_Map self, int *im); - -/** - * - * Fortran Wrapper Functions - * - * -*/ -extern void parms_mapcreatefromlocal_(parms_Map *self, int *gsize, int - *offset, int *ierr); - -extern void parms_mapcreatefromglobal_(parms_Map *self, int *gsize, int - *npar, MPI_Comm *comm, int *offset, - int *dof, VARSTYPE *vtype, int *ierr); - -extern void parms_mapcreatefromdist_(parms_Map *self, int *vtxdist, int - *part, MPI_Comm *comm, int *offset, int - *dof, VARSTYPE *vtype, int *ierr); - -extern void parms_mapcreatefrompetsc_(parms_Map *self, int *m, int *M, - MPI_Comm *comm, int *ierr); - -extern void parms_mapcreatefromptr_(parms_Map *self, int *gsize, int *nodes, - int *p2nodes, MPI_Comm *comm, int *dof, - VARSTYPE *vtype, int *ierr); - -extern void parms_mapfree_(parms_Map *self, int *ierr); - -extern void parms_mapgetglobalsize_(parms_Map *self, int *gsize); - -extern void parms_mapgetlocalsize_(parms_Map *self, int *lsize); - -extern void parms_mapgetnumprocs_(parms_Map *self, int *numpro); - -extern void parms_mapgetpid_(parms_Map *self, int *pid); - -extern void parms_mapview_(parms_Map *self, parms_Viewer *v, int *ierr); - -extern void parms_mapgetglobalindices_(parms_Map *self, int *im, int *ierr); -/* - * - * end Fortran Wrapper Functions - * -*/ - -PARMS_CXX_END - -#endif diff --git a/lib/parms/include/parms_mat.h b/lib/parms/include/parms_mat.h deleted file mode 100755 index 72f1daeec..000000000 --- a/lib/parms/include/parms_mat.h +++ /dev/null @@ -1,337 +0,0 @@ -/** - * @file parms_mat.h - * @author Zhongze Li - * @date Tue Oct 17 10:05:30 2006 - * - * @brief Functions related to the matrix computations. - * - * - */ - -#ifndef _PARMS_MAT_H_ -#define _PARMS_MAT_H_ - -#include "parms_sys.h" -#include "parms_vec.h" -#include "parms_viewer.h" -#include "parms_operator.h" - -PARMS_CXX_BEGIN - -typedef struct parms_Mat_ *parms_Mat; - -/** - * Create a parms_Mat object. - * - * Create a parms_Mat object based on data distribution layout map. - * - * @param self A pointer to the parms_Mat object created. - * @param map A parms_Map object, which describes the data - * distribution among processors. - * - * @return 0 on success. - */ -extern int parms_MatCreate(parms_Mat *self, parms_Map map); - -/** - * Dump parms_Mat object. - * - * @param self A pointer to a parms_Mat object. - * @param v A parms_Viewer object. - * - * @return 0 on success. - */ -extern int parms_MatView(parms_Mat self, parms_Viewer v); - -extern int parms_MatViewCOO(parms_Mat self, parms_Viewer v); - -/** - * Free the parms_Mat object pointed to by self. - * - * @param self A pointer to a parms_Mat object. - * - * @return 0 on success. - */ -extern int parms_MatFree(parms_Mat *self); - -/** - * Perform \f$y = self \times x\f$. - * - * @param self A parms_Mat object. - * @param x A Vector object. - * @param y Another Vector object. - * - * @return 0 on success. - */ -extern int parms_MatVec(parms_Mat self, FLOAT *x, FLOAT *y); - -/** - * Set up parms_Mat object self. - * - * This is the most important functoin for the parms_Mat object. This - * function combines the function bdry and setup in the old version - * of pARMS. The function sets up the data structure needed by the - * distributed matrix-vector multiplication, divides the variables on - * the local processors into two categories: interior and interface - * variables. - * - * @param self A parms_Mat object. - * - * @return 0 on success. - */ -extern int parms_MatSetup(parms_Mat self); - -/** - * Insert/add values to the parms_Mat object self. - * - * @param self A parms_Mat object. - * @param m The number of rows inserted. - * @param im An array of row global indices. - * @param ia An array of pointer to the beginning of each row in - * array ja. - * @param ja An array of column global indices. - * @param values An array of values. - * @param mode Insert value mode: - * - INSERT insert values to parm_Mat self. - * - ADD add values to parm_Mat self. - * - * @return 0 on success. - */ -extern int parms_MatSetValues(parms_Mat self, int m, int *im, int *ia, - int *ja, FLOAT *values, INSERTMODE mode); - -/** - * Insert/add values to the parms_Mat object self. This assumes matrix - * values are being added element-by-element - * - * @param self A parms_Mat object. - * @param m The number of rows inserted. - * @param im An array of row global indices. - * @param ia An array of pointer to the beginning of each row in - * array ja. - * @param ja An array of column global indices. - * @param values An array of values. - * @param mode Insert value mode: - * - INSERT insert values to parm_Mat self. - * - ADD add values to parm_Mat self. - * - * NOTE: New entries are always inserted first, so mode does not - * matter if this is a new entry. Subsequent calls will either - * replace (mode = INSERT) or add (mode = ADD) to the existing - * entries in a particular position - * - * @return 0 on success. - */ -extern int parms_MatSetElementMatrix(parms_Mat self, int m, int *im, int *ia, - int *ja, FLOAT *values, INSERTMODE mode); - -/** - * Assembles the finite element matrix by updating off-processor - * contributions. - * - * @param self A parms_Mat object. - * @return 0 on success. - */ -extern int parms_MatAssembleElementMatrix(parms_Mat self); - -/** - * Insert values to the parms_Mat object self. This assumes matrix - * values for this row have already been set, and are to be replaced - * by the new ones provided as input. - * - * @param self A parms_Mat object. - * @param m The number of rows inserted. - * @param im An array of row global indices. - * @param ia An array of pointer to the beginning of each row in - * array ja. - * @param ja An array of column global indices. - * @param values An array of values. - * @return 0 on success. - */ -extern int parms_MatResetRowValues(parms_Mat self, int m, int *im, int *ia, - int *ja, FLOAT *values); - -/** - * Reset the matrix to be re-used. - * @param self A parms_Mat object. - * @param nonzerostructure The nonzero structure: - * SAME_NONZERO_STRUCTURE - * DIFFERENT_NONZERO_STRUCTURE - * - * @return 0 on success. - */ -extern int parms_MatReset(parms_Mat self, NNZSTRUCT nonzerostructure); - -/** - * Set the communication type. - * - * Set the communication style across processors. - * communication style: - * - P2P point-to-point (data copied to/from auxilliary buffers). - * - DERIVED derived datatype. - * - * @param self A matrix object. - * @param ctype Communication style: - * - P2P point-to-point (data copied to/from - * auxilliary buffers). - * - DERIVED derived datatype. - * - * @return 0 on success. - */ -extern int parms_MatSetCommType(parms_Mat self, COMMTYPE ctype); - -/** - * Get the diagonal part of the local matrix. - * - * @param self A parms_Mat object. - * @param mat The diagonal part of the local matrix. - * - * @return 0 on success. - */ -extern int parms_MatGetDiag(parms_Mat self, void **mat); - - -/** - * Perform \f$z = alpha*self*x + beta*y\f$. - * - * @param self A matrix object. - * @param alpha A scalar. - * @param x A vector object. - * @param beta A scalar. - * @param y A vector object. - * @param z A vector stores the result. - * - * @return 0 on success. - */ -extern int parms_MatMVPY(parms_Mat self, FLOAT alpha, FLOAT *x, FLOAT - beta, FLOAT *y, FLOAT *z); - -/** - * Perform the multiplication of the off-diagonal matrix and the - * external vars. - * - * The local matrix can be written as follows: - * - * \f[ - * \left( - * \begin{array}{ccc} - * B & E & 0\\ - * F & C & M_{ext} - * \end{array} - * \right) - * \f], - * where \f$\left(\begin{array}{cc} - * B & E \\ - * F & C - * \end{array} - * \right)\f$ corresponds to the variables on the local PE. This - * function - * performs - * \f[ - * y[pos..n] = M_{ext} \times x_{ext} - * \f] - * - * @param self A matrix object. - * @param x A vector object. - * @param y A vector object. - * @param pos The offset of x from the beginning of he local vector. - * - * @return 0 on success. - */ -extern int parms_MatVecOffDiag(parms_Mat self, FLOAT *x, FLOAT *y, int - pos); - -/** - * Get the communication handler. - * - * @param self A matrix object. - * @param handler The communication handler returned. - * - * @return 0 on success. - */ -extern int parms_MatGetCommHandler(parms_Mat self, parms_Comm - *handler); - -/** - * Free the memory for the submatrix. - * - * @param self A parms_Mat object. - * @param mat The submatrix to be freed. - * - * @return 0 on success. - */ -extern int parms_MatFreeSubMat(parms_Mat self, void *mat); - -/** - * Get the local matrix. - * - * @param self A matrix object. - * @param mat The submatrix returned in a specific format. - * - * @return 0 on success. - */ -extern int parms_MatGetSubMat(parms_Mat self, void **mat); - -/** - * Extend submatrix by including equations correspond to the - * immediate neighbouring variables. - * - * @param self A matrix object. - * @param handler A communication handler. - * @param start The beginning location of mat in the local matrix. - * @param mat The submatrix to be extended. - * @param n The size of extended matrix returned. - * @param ext_mat The extended matrix created. - * - * @return 0 on success. - */ -extern int parms_MatExtend(parms_Mat self, parms_Comm handler, int - start, void *mat, int *n, void **ext_mat); - -/* - * - * Fortran Wrapper Functions - * -*/ - -extern void parms_matvec_(parms_Mat *self, FLOAT *x, FLOAT *y, int *ierr); - -extern void parms_matcreate_(parms_Mat *self, parms_Map *map, int *ierr); - -extern void parms_matfree_(parms_Mat *self, int *ierr); - -extern void parms_matmvpy_(parms_Mat *self, FLOAT *alpha, FLOAT *x, FLOAT - *beta, FLOAT *y, FLOAT *z, int *ierr); - -extern void parms_matsetcommtype_(parms_Mat *self, COMMTYPE *ctype, int - *ierr); - -extern void parms_matsetvalues_(parms_Mat *self, int *m, int *im, int *ia, - int *ja, FLOAT *values, INSERTMODE *mode, int - *ierr); - -extern void parms_matsetelementmatrix_(parms_Mat *self, int *m, int *im, int *ia, - int *ja, FLOAT *values, INSERTMODE *mode, int *ierr); - -extern void parms_matassembleelementmatrix_(parms_Mat *self, int *ierr); - -extern void parms_matresetrowvalues_(parms_Mat *self, int *m, int *im, int *ia, - int *ja, FLOAT *values, int *ierr); - -extern void parms_matreset_(parms_Mat *self, NNZSTRUCT *nonzerostructure, int *ierr); - -extern void parms_matsetup_(parms_Mat *self, int *ierr); - -extern void parms_matview_(parms_Mat *self, parms_Viewer *v, int *ierr); - -extern void parms_matviewcoo_(parms_Mat *self, parms_Viewer *v, int *ierr); - -/* - * - * end Fortran Wrapper Functions - * -*/ - -PARMS_CXX_END - -#endif diff --git a/lib/parms/include/parms_mem.h b/lib/parms/include/parms_mem.h deleted file mode 100755 index b7dcbc4c0..000000000 --- a/lib/parms/include/parms_mem.h +++ /dev/null @@ -1,82 +0,0 @@ -/** - * @file parms_mem.h - * @author Zhongze Li - * @date Tue Oct 17 11:48:51 2006 - * - * @brief Macros and functions for memory allocation in pARMS. - * - * - PARMS_NEW and PARMS_NEW0 allocate memory for an object of struct - * type. - * - PARMS_NEWARRAY and PARMS_NEWARRYA0 allocate memory for an array. - * - PARMS_ALLOC(n) is used for allocating n bytes, which is useful for - * reducing the number of invoking malloc function. by combining - * multiple malloc calls together. - * - PARMS_RESIZE changes the size of the allocated memory. - * - PARMS_FREE frees the memory allocated. - * - */ - -#ifndef _PARMS_MEM_H_ -#define _PARMS_MEM_H_ - -#include -#include -#include "parms_sys.h" - -PARMS_CXX_BEGIN - -#ifdef __STDC__ -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >=199901L -#define C99 -#elif defined(__STDC_VERSION__) && __STDC_VERSION__>=199409L -#define C89 -#else -#define C89 -#endif -#endif - -#ifdef C99 -extern void *parms_malloc(long size, const int line, const char *func, - const char *fname); -extern void *parms_calloc(long count, long size, int line, - const char *func, const char *fname); -extern void *parms_resize(void *ptr, long size, const int line, - const char *func, const char *fname); -extern void parms_free(void *ptr, const int line, const char *func, - const char *fname); -#define PARMS_ALLOC(n) parms_malloc((long)(n), __LINE__, __func__, __FILE__) -#define PARMS_CALLOC(n, size) parms_calloc((long)(n), (long)(size), __LINE__, __func__, __FILE__) -#define PARMS_NEW(p) ((p) = PARMS_ALLOC(sizeof *(p))) -#define PARMS_NEW0(p) ((p) = PARMS_CALLOC(1, sizeof *(p))) -#define PARMS_NEWARRAY(p, n) ((p) = PARMS_ALLOC((n)*sizeof *(p))) -#define PARMS_NEWARRAY0(p, n) ((p) = PARMS_CALLOC((n), sizeof *(p))) -#define PARMS_RESIZE(p, size) ((p) = parms_resize((p), ((size) * (long)sizeof (*(p))), __LINE__, __func__, __FILE__)) -#define PARMS_FREE(p) (parms_free((p), __LINE__, __func__, __FILE__), (p) = NULL) - - -#else - -extern void *parms_malloc(long size, const int line, const char *fname); -extern void *parms_calloc(long count, long size, int line, - const char *fname); -extern void *parms_resize(void *ptr, long size, const int line, - const char *fname); -extern void parms_free(void *ptr, const int line, const char *fname); - -#define PARMS_ALLOC(n) parms_malloc((long)(n), __LINE__, __FILE__) -#define PARMS_CALLOC(n, size) parms_calloc((long)(n), (long)(size), __LINE__,__FILE__) -#define PARMS_NEW(p) ((p) = PARMS_ALLOC(sizeof *(p))) -#define PARMS_NEW0(p) ((p) = PARMS_CALLOC(1, sizeof *(p))) -#define PARMS_NEWARRAY(p, n) ((p) = PARMS_ALLOC((n)*sizeof *(p))) -#define PARMS_NEWARRAY0(p, n) ((p) = PARMS_CALLOC((n), sizeof *(p))) -#define PARMS_RESIZE(p, size) ((p) = parms_resize((p), ((size) * (long)sizeof (*(p))), __LINE__, __FILE__)) -#define PARMS_FREE(p) (parms_free((p), __LINE__, __FILE__), (p) = NULL) - - -#endif -#define PARMS_MEMCPY(dest, src, size) memcpy((dest), (src), (size)*(long)sizeof *(src)) - - -PARMS_CXX_END - -#endif diff --git a/lib/parms/include/parms_operator.h b/lib/parms/include/parms_operator.h deleted file mode 100755 index 51c2af27d..000000000 --- a/lib/parms/include/parms_operator.h +++ /dev/null @@ -1,167 +0,0 @@ -/** - * @file parms_operator.h - * @author Zhongze Li - * @date Tue Oct 17 11:51:20 2006 - * - * @brief Functions related to the preconditioner operator objects. - * - * A operator created implicitly by invoking a ILU-type function. The - * corresponding destructor is set automatically when the ILU-type - * function is called. - * - */ - -#ifndef _PARMS_OPERATOR_H_ -#define _PARMS_OPERATOR_H_ - -#include "parms_sys.h" -#include "parms_vec.h" -#include "parms_viewer.h" - -PARMS_CXX_BEGIN - -typedef struct parms_Operator_ *parms_Operator; - -typedef int (*opt_apply)(parms_Operator self, FLOAT *y, FLOAT *x); - -/** - * Create an operator object. - * - * @param self A pointer to the operator object created. - * - * @return 0 on success. - */ -extern int parms_OperatorCreate(parms_Operator *self); - -/** - * Free the memory for the operator object pointed to by self. - * - * @param self A pointer to the operator object. - * - * @return 0 on success. - */ -extern int parms_OperatorFree(parms_Operator *self); - -/** - * Dump the operator object. - * - * Output the L and U part of the operator object. - * - * @param self An operator object. - * @param v A parms_Viewer object. - * - * @return 0 on success. - */ -extern int parms_OperatorView(parms_Operator self, parms_Viewer v); - -/** - * Perform \f$x = self^{-1}y\f$. - * - * Assume \f$ A \approx \left( \begin{array}{ll} L & 0 \\ FU^{-1} & - * I\end{array} \right) \left( \begin{array}{ll} U & L^{-1}E \\ 0 & - * S\end{array} \right)\f$, this function performs \f$x = - * \left(\begin{array}{ll} U & L^{-1}E \\ 0 & - * S\end{array} \right)^{-1} - * \left( \begin{array}{ll} L & 0 \\ FU^{-1} & I\end{array} - * \right)^{-1}y\f$. - * - * @param self An operator. - * @param y A right-hand-side vector. - * @param x The solution vector. - * - * @return 0 on success. - */ -extern int parms_OperatorApply(parms_Operator self, FLOAT *y, - FLOAT *x); - -/** - * Perform forward sweep. - * - * Assume \f$ A \approx \left( \begin{array}{ll} L & 0 \\ FU^{-1} & - * I\end{array} \right) \left( \begin{array}{ll} U & L^{-1}E \\ 0 & - * S\end{array} \right)\f$, this function performs \f$x = - * \left( \begin{array}{ll} L & 0 \\ FU^{-1} & I\end{array} - * \right)^{-1}y\f$. - * - * @param self An operator object. - * @param y A right-hand-side vector. - * @param x The solution vector. - * - * @return 0 on success. - */ -extern int parms_OperatorLsol(parms_Operator self, FLOAT *y, FLOAT - *x); - -/** - * Perform \f$x = S^{-1}y\f$. - * - * Assume \f$ A \approx \left( \begin{array}{ll} L & 0 \\ FU^{-1} & - * I\end{array} \right) \left( \begin{array}{ll} U & L^{-1}E \\ 0 & - * S\end{array} \right)\f$, this function performs \f$x = S^{-1}y\f$. - * - * @param self An operator object. - * @param y A right-hand-side vector. - * @param x The solution vector. - * - * @return 0 on success. - */ -extern int parms_OperatorInvS(parms_Operator self, FLOAT *y, FLOAT - *x); - - - -/** - * Get local Schur Complement $S_i$ used by PCSCHURRAS (AF) - * - * @param self An operator object. - * @param mat local Schur_Complement - * - * @return 0 on success. - */ -extern int parms_OperatorGetS(parms_Operator self, void **mat); - -/** - * Perform block backward substitution. - * - * Assume \f$ A \approx \left( \begin{array}{ll} L & 0 \\ FU^{-1} & - * I\end{array} \right) \left( \begin{array}{ll} U & L^{-1}E \\ 0 & - * S\end{array} \right)\f$, this function performs \f$x = - * \left(\begin{array}{ll} U & L^{-1}E \\ 0 & - * I\end{array} \right)^{-1}y\f$. - * - * @param self An operator object. - * @param y A right-hand-side vector. - * @param x The solution vector. - * - * @return 0 on success. - */ -extern int parms_OperatorAscend(parms_Operator self, FLOAT *y, FLOAT - *x); - -/** - * Return the start position of the Schur complement in the local - * matrix. - * - * @param self An operator object. - * - * @return The beginning location of the Schur complement in the - * local system. - */ -extern int parms_OperatorGetSchurPos(parms_Operator self); - -/** - * Get the number of nonzero entries of the original matrix and - * the preconditioning matrix. - * - * @param self An operator object. - * @param nnz_mat A pointer to the number of nonzeros of the original - * matrix. - * @param nnz_pc A pointer to the number of nonzeros of the - * preconditioning matrix. - */ -extern void parms_OperatorGetNnz(parms_Operator self, int *nnz_mat, - int *nnz_pc); - -PARMS_CXX_END - -#endif diff --git a/lib/parms/include/parms_pc.h b/lib/parms/include/parms_pc.h deleted file mode 100755 index a9d43ac5a..000000000 --- a/lib/parms/include/parms_pc.h +++ /dev/null @@ -1,392 +0,0 @@ -/** - * @file parms_pc.h - * @author Zhongze Li - * @date Tue Oct 17 11:52:38 2006 - * - * @brief Preconditioner-related Functions. - * - */ - -#ifndef _PARMS_PC_H_ -#define _PARMS_PC_H_ - -#include "parms_sys.h" -#include "parms_operator.h" -#include "parms_mat.h" -#include "parms_vec.h" -#include "parms_viewer.h" - -PARMS_CXX_BEGIN - -typedef struct parms_PC_ *parms_PC; - -/** - * Solve \f$self z = y\f$ - * - * @param self A preconditioner object. - * @param y A right-hand-side vector. - * @param z The solution vector. - * - * @return 0 on success. - */ -extern int parms_PCApply(parms_PC self, FLOAT *y, FLOAT *z); - -/** - * Set the matrix to create the preconditioning matrix. - * - * @param self A preconditioner object. - * @param A The matrix to be used for creating PC. - * - * @return 0 on success. - */ -extern int parms_PCSetOP(parms_PC self, parms_Mat A); - -/** - * Set up the preconditioner (create the preconditioning matrix). - * - * @param self A preconditioner object. - * - * @return 0 on success. - */ -extern int parms_PCSetup(parms_PC self); - -/** - * Create a preconditioner object based on the matrix A. - * - * @param self A preconditioner object. - * @param A A matrix object. - * - * @return 0 on success. - */ -extern int parms_PCCreate(parms_PC *self, parms_Mat A); - -/** - * Create an abstract preconditioner object. - * - * @param self A pointer to the preconditioner object. - * - * @return 0 on success. - */ -extern int parms_PCCreateAbstract(parms_PC *self); - -/** - * Free the memory for the preconditioner object pointed to by self. - * - * @param self A pointer to the memory for the preconditioner object. - * - * @return 0 on success. - */ -extern int parms_PCFree(parms_PC *self); - -/** - * Dump preconditioner object self. - * - * @param self A preconditioner object. - * @param v A viewer object. - */ -extern void parms_PCView(parms_PC self, parms_Viewer v); - -/** - * Set preconditioner type. - * - * Currently supported preconditioners: - * \f{tabular}{ll} - * PCBJ & block Jacobi \\ - * PCRAS & restricted additive Schwarz \\ - * PCSCHUR & Schur complement - * \f} - * - * @param self A preconditioner object. - * @param pctype The type of preconditioner. - * - PCBJ block Jacobi - * - PCRAS restricted additive Schwarz - * - PCSCHUR Schur complement - * - * @return 0 on success. - */ -extern int parms_PCSetType(parms_PC self, PCTYPE pctype); - -/** - * Set local preconditioner type. - * - * Supported ILU preconditioners: - * - * \f{tabular}{ll} - * PCILU0 & ILU0 \\ - * PCILUK & ILUK \\ - * PCILUT & ILUT in SPARSKIT \\ - * PCARMS & ARMS implemented by Yousef Saad - * \f} - - * @param self A preconditioner object. - * @param pcstype The type of local preconditioner: - * - PCILU0 - * - PCILUK - * - PCILUT - * - PCARMS - * - * @return 0 on success. - */ -extern int parms_PCSetILUType(parms_PC self, PCILUTYPE pcstype); - -/** - * Perform ILU factorization. - * - * @param self A preconditioner object. - * @param param The parameters used for ILU factorization. - * @param mat Matrix to be factored. - * @param op The operator created. - * - * @return 0 on success. - */ -extern int parms_PCILU(parms_PC self, parms_FactParam param, void - *mat, parms_Operator *op); - -/** - * Set parameters for the preconditioner object. - * - * Supported parameters: - * - tol drop tolerance - * - fil fill-in - * - nlev number of levels - * - bsize block size for finding independent sets in ARMS. - * - tolind drop tolerance for finding independent sets. - * - iksize the restart size for the inner GMRES. - * - imax the number of iterations for the inner GMRES. - * - * @param self A preconditioner object. - * @param nflags The number of parameters. - * @param params A pointer to parameters. - * - * @return 0 on success. - */ -extern int parms_PCSetParams(parms_PC self, int nflags, char **params); - -/** - * Set permutation and scaling options for interlevel blocks. - * - * @param self A preconditioner object. - * @param meth Options: - * - meth[0] nonsummetric permutations of 1: yes. affects - * rperm USED FOR LAST SCHUR COMPLEMENT - * - meth[1] permutations of columns 0:no 1: yes. So far - * this is USED ONLY FOR LAST BLOCK [ILUTP - * instead of ILUT]. (so ipar[11] does not matter - * ,enter zero). If ipar[15] is one then ILUTP - * will be used instead of ILUT. Permutation data - * stored in: perm2. - * - meth[2] diag. row scaling. 0:no 1:yes. Data: D1 - * - meth[3] diag. column scaling. 0:no 1:yes. Data: D2 - * @param flag Options: - * - 1 interlevel block. - * - 0 last block. - * - * @return 0 on success. - */ -extern int parms_PCSetPermScalOptions(parms_PC self, int *meth, int - flag); - -/** - * Set fill-in parameter for ILUT and ARMS. - * - * @param self A preconditioner object. - * @param fill A int array of size 7. - * - fill[0] amount of fill-in kept in \f$L_{B}\f$. - * - fill[1] amount of fill-in kept in \f$U_{B}\f$. - * - fill[2] amount of fill-in kept in \f$E L^{-1}\f$. - * - fill[3] amount of fill-in kept in \f$U^{-1}_{B} F\f$. - * - fill[4] amount of fill-in kept in \f$S\f$. - * - fill[5] amount of fill-in kept in \f$L_S\f$. - * - fill[6] amount of fill-in kept in \f$U_S\f$. - * - * @return 0 on success. - */ -extern int parms_PCSetFill(parms_PC self, int *fill); - -/** - * Set the drop tolerance for ILUT preconditioner. - * - * @param self A preconditioner object. - * @param tol A double array of size 7. - * - tol[0] threshold for dropping in L_{B}. - * - tol[1] threshold for dropping in U_{B}. - * - tol[2] threshold for dropping in L^{-1} F - * - tol[3] threshold for dropping in E U^{-1} - * - tol[4] threshold for dropping in Schur complement - * - tol[5] threshold for dropping in L in last block - * - tol[6] threshold for dropping in U in last block - * - * @return 0 on success. - */ -extern int parms_PCSetTol(parms_PC self, double *tol); - -/** - * Set the number of levels for ILUK and ARMS. - * - * @param self A preconditioner object. - * @param nlevel The number of levels. - * - * @return 0 on success. - */ -extern int parms_PCSetNlevels(parms_PC self, int nlevel); - -/** - * Set the type of permutation in ARMS - * - * @param self A preconditioner object. - * @param type Permutation type. - * - 1 non-symmetric permutaion. - * - 0 symmetric permutation. - * - * @return 0 on success. - */ -extern int parms_PCSetPermType(parms_PC self, int type); - -/** - * Set the block size for ARMS. - * - * @param self A preconditioner object. - * @param bsize The block size for ARMS. - * - * @return 0 on success. - */ -extern int parms_PCSetBsize(parms_PC self, int bsize); - -/** - * Set the restart size for the inner GMRES. - * - * @param self A preconditioner object. - * @param im The restart size of the inner GMRES. - * - * @return 0 on success. - */ -extern int parms_PCSetInnerKSize(parms_PC self, int im); - - -/** - * Set the maximum iteration counts for the inner GMRES. - * - * @param self A preconditioner object. - * @param imax The maximum iteration counts. - * - * @return 0 on success. - */ -extern int parms_PCSetInnerMaxits(parms_PC self, int imax); - -/** - * Set the convergence tolerance for the inner GMRES. - * - * @param self A preconditioner object. - * @param eps The convergence tolerance. - * - * @return 0 on success. - */ -extern int parms_PCSetInnerEps(parms_PC self, REAL eps); - -/** - * Set the tolerance for finding independent sets. - * - * @param self A preconditioner object. - * @param tolind The drop tolerance for finding independent sets. - * - * @return 0 on success. - */ -extern int parms_PCSetTolInd(parms_PC self, REAL tolind); - -/** - * Get the ratio of the number of nonzero entries of the - * preconditioning matrix to that of the original matrix. - * - * @param self A preconditioner. - * @param ratio A pointer to the ratio. - * - * @return 0 on success. - */ -extern int parms_PCGetRatio(parms_PC self, double *ratio); - -/** - * Return the name of a preconditioner. - * - * @param self A preconditioner. - * @param name The name of preconditioner. - * - * @return 0 on success. - */ -extern int parms_PCGetName(parms_PC self, char **name); - -/** - * Return the name of a local preconditioner. - * - * @param self A preconditioner. - * @param iluname The name of local ILU preconditioner. - * - * @return 0 on success. - */ -extern int parms_PCILUGetName(parms_PC self, char **iluname); - -/* - * - * Fortran Wrapper Functions - * -*/ - -extern void parms_pccreate_(parms_PC *self, parms_Mat *A, int *ierr); - -extern void parms_pcfree_(parms_PC *self, int *ierr); - -extern void parms_pcgetratio_(parms_PC *self, double *ratio, int *ierr); - -extern void parms_pcsetbsize_(parms_PC *self, int *bsize, int *ierr); - -extern void parms_pcsetfill_(parms_PC *self, int *fill, int *ierr); - -extern void parms_pcsetilutype_(parms_PC *self, PCILUTYPE *pcstype, int - *ierr); - -extern void parms_pcsetinnereps_(parms_PC *self, REAL *eps, int *ierr); - -extern void parms_pcsetinnerksize_(parms_PC *self, int *im, int *ierr); - -extern void parms_pcsetinnermaxits_(parms_PC *self, int *imax, int *ierr); - -extern void parms_pcsetnlevels_(parms_PC *self, int *nlevel, int *ierr); - -extern void parms_pcsetparams_(parms_PC *self, int *nflags, char **params, - int *ierr); - -extern void parms_pcsettol_(parms_PC *self, double *tol, int *ierr); - -extern void parms_pcsettolind_(parms_PC *self, REAL *tolind, int *ierr); - -extern void parms_pcsettype_(parms_PC *self, PCTYPE *pctype, int *ierr); - -extern void parms_pcsetup_(parms_PC *self, int *ierr); - -extern void parms_pcsolve_(parms_PC *self, FLOAT *y, FLOAT *z, int *ierr); - -extern void parms_pcview_(parms_PC *self, parms_Viewer *v, int *ierr); - -extern void parms_pcgetname_(parms_PC *self, char *name, int *size, int - *ierr, int len); - -extern void parms_pcilugetname_(parms_PC *self, char *name, int *size, int - *ierr, int len); - -extern void parms_pccreateabstract_(parms_PC *self, int *ierr); - -extern void parms_pcsetpermscaloptions_(parms_PC *self, int *meth, int - *flag, int *ierr); - -extern void parms_pcsetpermtype_(parms_PC *self, int *type, int *ierr); - -extern void parms_pcsetop_(parms_PC *self, parms_Mat *A, int *ierr); - -/* - * - * end Fortran Wrapper Functions - * -*/ - -PARMS_CXX_END - -#endif diff --git a/lib/parms/include/parms_solver.h b/lib/parms/include/parms_solver.h deleted file mode 100755 index dd4241c51..000000000 --- a/lib/parms/include/parms_solver.h +++ /dev/null @@ -1,194 +0,0 @@ -/** - * @file parms_solver.h - * @author Zhongze Li - * @date Tue Oct 17 11:58:22 2006 - * - * @brief Functions related to the Krylov subspace methods. Only - * FGMRES is supported. - * - */ - -#ifndef _PARMS_SOLVER_H_ -#define _PARMS_SOLVER_H_ - -#include "parms_vec.h" -#include "parms_mat.h" -#include "parms_viewer.h" -#include "parms_pc.h" -#include "parms_operator.h" - -PARMS_CXX_BEGIN - -typedef struct parms_Solver_ *parms_Solver; - -/** - * Get the matrix of the linear system. - * - * @param self A parms_Solver object. - * @param A A pointer to the matrix returned. - * - * @return 0 on success. - */ -extern int parms_SolverGetMat(parms_Solver self, parms_Mat *A); - -/** - * Get the preconditioning matrix. - * - * @param self A parms_Solver object. - * @param PC A pointer to the preconditioning matrix. - * - * @return 0 on success. - */ -extern int parms_SolverGetPC(parms_Solver self, parms_PC *PC); - -/** - * Solve the equation \f$Ax = y\f$. - * - * @param self A parms_Solver object. - * @param x The solution vector. - * @param y The right-hand-side vector. - * - * @return 0 on success. - */ -extern int parms_SolverApply(parms_Solver self, FLOAT *x, FLOAT *y); - -/** - * Compute the local residual \f$ r = y - Ax \f$. - * - * @param self A parms_Solver object. - * @param x The solution vector. - * @param y The right-hand-side vector. - * @param r The computed residual vector. - * - * @return 0 on success. - */ -extern int parms_SolverGetResidual(parms_Solver self, FLOAT *y, FLOAT *x, FLOAT *r); - -/** - * Compute the local residual 2-norm \f$ ||r = y - Ax|| \f$. - * - * @param self A parms_Solver object. - * @param x The solution vector. - * @param y The right-hand-side vector. - * @param rnorm The 2-norm of the residual vector. - * - * @return 0 on success. - */ -extern int parms_SolverGetResidualNorm2(parms_Solver self, FLOAT *y, FLOAT *x, REAL *rnorm); - - -/** - * Set the type of the solver. - * - * Only FGMRES solver is available in the package. - * - * @param self A parms_Solver object. - * @param stype The type of Krylov subspace. - * - SOLFGMRES - * - SOLDGMRES - * - * @return 0 on success. - */ -extern int parms_SolverSetType(parms_Solver self, SOLVERTYPE stype); - -/** - * Create a parms_Solver object. - * - * @param self A pointer to the parms_Solver object created. - * @param A The matrix of the linear system. - * @param pc The preconditioner. - * - * @return 0 on success. - */ -extern int parms_SolverCreate(parms_Solver *self, parms_Mat A, parms_PC pc); - -/** - * Free the memory for the parms_Solver object. - * - * @param self A pointer to the parms_Solver object to be freed. - * - * @return 0 on success. - */ -extern int parms_SolverFree(parms_Solver *self); - -/** - * Dump te solver object. - * - * @param self A parms_Solver object. - * @param v A parms_Viewer object. - */ -extern int parms_SolverView(parms_Solver self, parms_Viewer v); - -/** - * Set parameter for the solver. - * - * Set the maximum iteration counts, the restart size of GMRES, and - * the convergence tolerance. - * - * @param self A parms_Solver object. - * @param ptype The type of parameter. - * - MAXITS maximum iteration counts. - * - KSIZE restart size of GMRES. - * - DTOL converence tolerance. - * - NEIG number of eigenvectors. - * @param param Parameters for the solver. - */ -extern void parms_SolverSetParam(parms_Solver self, PARAMTYPE ptype, - char *param); - -/** - * Get the iteration counts. - * - * @param self A parms_Solver object. - * - * @return The iteration counts. - */ -extern int parms_SolverGetIts(parms_Solver self); - - -/* - * - * Fortran Wrapper Functions - * -*/ - -extern void parms_solverapply_(parms_Solver *self, FLOAT *x, FLOAT - *y, int *ierr); - -extern void parms_solvercreate_(parms_Solver *self, parms_Mat *A, parms_PC - *pc, int *ierr); - -extern void parms_solverfree_(parms_Solver *self, int *ierr); - -extern void parms_solvergetits_(parms_Solver *self, int *its, int *ierr); - -extern void parms_solvergetmat_(parms_Solver *self, parms_Mat *A, int *ierr); - -extern void parms_solversetparam_(parms_Solver *self, PARAMTYPE *ptype, char - *param, int *ierr); - -extern void parms_solversettype_(parms_Solver *self, SOLVERTYPE *stype, int - *ierr); - -extern void parms_solvergetpc_(parms_Solver *self, parms_PC *PC, int - *ierr); - -extern void parms_solvergetresidual_(parms_Solver *self, FLOAT *y, FLOAT *x, - FLOAT *r, int *ierr); - -extern void parms_solvergetresidualnorm2_(parms_Solver *self, FLOAT *y, FLOAT *x, - REAL *rnorm, int *ierr); - -extern void parms_solverview_(parms_Solver *self, parms_Viewer *v, int - *ierr); - -/* - * - * End Fortran Wrapper Functions - * -*/ - - -PARMS_CXX_END - -#endif diff --git a/lib/parms/include/parms_sys.h b/lib/parms/include/parms_sys.h deleted file mode 100755 index d49f25ef6..000000000 --- a/lib/parms/include/parms_sys.h +++ /dev/null @@ -1,196 +0,0 @@ -/** - * @file parms_sys.h - * @author Zhongze Li - * @date Tue Oct 17 11:59:09 2006 - * - * @brief Macros and typedef needed by all other header files. - * - */ - -#ifndef _PARMS_SYS_H_ -#define _PARMS_SYS_H_ - -#ifdef __STDC__ -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >=199901L -#define C99 -#elif defined(__STDC_VERSION__) && __STDC_VERSION__>=199409L -#define C89 -#else -#define C89 -#endif -#endif - -#ifdef USE_MPI -#define PARMS_ABORT(error_number) \ - if (true) { \ - int flag; \ - MPI_Initialized(&flag); \ - if (flag) { \ - MPI_Abort(MPI_COMM_WORLD, (error_number)); \ - } \ - else { \ - exit((error_number)); \ - } \ - } else -#else -#define PARMS_ABORT(error_number) \ - if (true) { \ - exit((error_number)); \ - } else -#endif - -#include -#ifdef C99 -#include -#endif -#include "mpi.h" - -#if defined(__cplusplus) -#define PARMS_CXX_BEGIN extern "C" { -#define PARMS_CXX_END } -#else -#define PARMS_CXX_BEGIN -#define PARMS_CXX_END -#endif - -PARMS_CXX_BEGIN - -typedef enum VARSTYPE {INTERLACED, NONINTERLACED} VARSTYPE; -typedef enum INSERTMODE {INSERT, ADD} INSERTMODE; -typedef enum NNZSTRUCT {SAME_NONZERO_STRUCTURE, DIFFERENT_NONZERO_STRUCTURE} NNZSTRUCT; -typedef enum COMMTYPE {P2P, DERIVED} COMMTYPE; -typedef enum SOLVERTYPE {SOLFGMRES, SOLGMRES, SOLBICGS, SOLCG, SOLPBICGS, SOLPBICGS_RAS, SOLBICGS_RAS} SOLVERTYPE; -typedef enum PARAMTYPE {MAXITS, KSIZE, DTOL, NEIG} PARAMTYPE; -typedef enum MATTYPE {MAT_NULL=-1, MAT_VCSR=0, MAT_CSR=1} MATTYPE; -typedef enum PCTYPE {PCBJ, PCSCHUR, PCRAS, PCSCHURRAS} PCTYPE; -typedef enum PCILUTYPE {PCILU0, PCILUK, PCILUT, PCARMS} PCILUTYPE; - -#ifdef C99 -typedef _Bool BOOL; -#else -typedef int BOOL; -#define true 1 -#define false 0 -#endif - -#define PARMS_COUT "parms_cout" -#define PARMS_CERR "parms_cerr" - -typedef struct parms_FactParam { - int schur_start; /* start position of the Schur complement */ - int start; /* start row index of the submatrix in - the whole matrix */ - int n; /* the size of the local matrix */ - /* - mc = multicoloring or not (0-not, 1-yes). - ipar[0:17] = integer array to store parameters for both - arms construction (arms2) and iteration (armsol2). - - ipar[0]:=nlev. number of levels (reduction processes). see - also "on return" below. - - ipar[1]:= level-reordering option to be used. - if ipar[1]==0 ARMS uses a block independent set ordering - with a sort of reverse cutill Mc Kee ordering to build - the blocks. This yields a symmetric ordering. - in this case the reordering routine called is indsetC - if ipar[1] == 1, then a nonsymmetric ordering is used. - In this case, the B matrix is constructed to be as - diagonally dominant as possible and as sparse as possble. - in this case the reordering routine called is ddPQ. - - ipar[2]:=bsize. Dimension of the blocks. In this version, bsize - is only a target block size. The size of each block can vary - and is >= bsize. - - ipar[3]:=iout if (iout > 0) statistics on the run are printed - to FILE *ft - - The following are not used by arms2 -- but should set before - calling the preconditionning operation armsol2: - - ipar[4]:= Krylov subspace dimension for last level - ipar[4] == 0 means only backward/forward solve is performed - on last level. - ipar[5]:= maximum # iterations on last level - - ipar[6-9] NOT used [reserved for later use] - must be - set to zero. [see however a special use of ipar[5] in fgmresC.] - - The following set method options for arms2. Their default - values can all be set to zero if desired. - - ipar[10-13] == meth[0:3] = method flags for interlevel blocks - ipar[14-17] == meth[0:3] = method flags for last level block - - with the following meaning - meth[0] permutations of rows 0:no 1: yes. affects rperm - NOT USED IN THIS VERSION ** enter 0.. Data: rperm - meth[1] permutations of columns 0:no 1: yes. So far this is - USED ONLY FOR LAST BLOCK [ILUTP instead of ILUT]. (so - ipar[11] does no matter - enter zero). If ipar[15] is one - then ILUTP will be used instead of ILUT. Permutation data - stored in: perm2. - meth[2] diag. row scaling. 0:no 1:yes. Data: D1 - meth[3] diag. column scaling. 0:no 1:yes. Data: D2 - similarly for meth[14], ..., meth[17] all transformations - related to parametres in meth[*] (permutation, - scaling,..) are applied to the matrix before processing - it - - droptol = Threshold parameters for dropping elements in ILU - factorization. - droptol[0:4] = related to the multilevel block factorization - droptol[5:5] = related to ILU factorization of last block. - This flexibility is more than is really needed. one can use a - single parameter for all. it is preferable to use one value - for droptol[0:4] and another (smaller) for droptol[5:6] - droptol[0] = threshold for dropping in L [B]. See piluNEW.c: - droptol[1] = threshold for dropping in U [B]. - droptol[2] = threshold for dropping in L^{-1} F - droptol[3] = threshold for dropping in E U^{-1} - droptol[4] = threshold for dropping in Schur complement - droptol[5] = threshold for dropping in L in last block [see - ilutpC.c] - droptol[6] = threshold for dropping in U in last block [see - ilutpC.c] - - lfil = lfil[0:6] is an array containing the fill-in parameters. - similar explanations as above, namely: - lfil[0] = amount of fill-in kept in L [B]. - lfil[1] = amount of fill-in kept in U [B]. - etc.. - - tolind = tolerance parameter used by the indset function. - a row is not accepted into the independent set if the *relative* - diagonal tolerance is below tolind. see indset function for - details. Good values are between 0.05 and 0.5 -- larger values - tend to be better for harder problems. - - Note: The comments above are for arms. The first element for every - array is for other preconditioner: ilut, iluk - */ - int mc; - int lfil[7]; - int ipar[18]; - double droptol[7]; - double pgfpar[2]; - double tolind; - BOOL isalloc; -} *parms_FactParam; - -#define ZERO 0.0 -#define EPSILON 1.0e-20 -#define EPSMAC 1.0e-16 - -/* Compile real or complex version of code */ -#if defined(DBL_CMPLX) -#include "parms_sys_cmplx.h" -#elif defined(DBL) -#include "parms_sys_dbl.h" -#else -#include "parms_sys_dbl.h" -#endif - -PARMS_CXX_END - -#endif diff --git a/lib/parms/include/parms_sys_cmplx.h b/lib/parms/include/parms_sys_cmplx.h deleted file mode 100755 index eb5805615..000000000 --- a/lib/parms/include/parms_sys_cmplx.h +++ /dev/null @@ -1,172 +0,0 @@ -/** - * @file parms_sys_cmplx.h - * @author Zhongze Li - * @date Tue Oct 17 11:59:09 2006 - * - * @brief Macros and typedef needed by all other header files. - * This is used when the complex version of the code is compiled - * (with the -DDBL_CMPLX flag) - * - * DOK - * - */ - -#include - -typedef struct{ - double real, imag; -}complex_type; - -/* Define complex data-type for MPI */ -#ifdef USE_MPI -MPI_Datatype MPI_CMPLX; -MPI_Op MPI_CMPLX_SUM; -#endif - -/* external BLAS */ -#if defined(DBL_CMPLX) -#define FLOAT complex double -#define ABS_VALUE(a) (cabs(a)) /* definition of absolute value */ -#define GDOTC ZDOTC -#define GDOTU ZDOTU -#define GCOPY ZCOPY -#define GSCAL ZSCAL -#define GAXPY ZAXPY -#define GNRM2 DZNRM2 -#define GGEMV ZGEMV -#define GGEMM ZGEMM -#define GGETRF ZGETRF -#define GGETRS ZGETRS -#define GGETRI ZGETRI -#define GGESVD ZGESVD -#if defined(FORTRAN_CAPS) -#define zdotc_ ZDOTC -#define zdotu_ ZDOTU -#define zcopy_ ZCOPY -#define zscal_ ZSCAL -#define zaxpy_ ZAXPY -#define dznrm2_ DZNRM2 -#define zgemv_ ZGEMV -#define zgemm_ ZGEMM -#define zgetrf_ ZGETRF -#define zgetrs_ ZGETRS -#define zgetri_ ZGETRI -#define zgesvd_ ZGESVD -#elif defined(FORTRAN_DOUBLE_UNDERSCORE) -#define zdotc_ zdotc__ -#define zdotu_ zdotu__ -#define zcopy_ zcopy__ -#define zscal_ zscal__ -#define zaxpy_ zaxpy__ -#define dznrm2_ dznrm2__ -#define zgemv_ zgemv__ -#define zgemm_ zgemm__ -#define zgetrf_ zgetrf__ -#define zgetrs_ zgetrs__ -#define zgetri_ zgetri__ -#define zgesvd_ zgesvd__ -#elif !defined(FORTRAN_UNDERSCORE) -#define BLAS_C_INTERFACE -#include -#define zdotc_ zdotc -#define zdotu_ zdotu -#define zcopy_ zcopy -#define zscal_ zscal -#define zaxpy_ zaxpy -#define dznrm2_ dznrm2 -#define zgemv_ zgemv -#define zgemm_ zgemm -#define zgetrf_ zgetrf -#define zgetrs_ zgetrs -#define zgetri_ zgetri -#define zgesvd_ zgesvd -#endif - -/* FORTRAN subroutines */ -#ifndef BLAS_C_INTERFACE -#define ZDOTC(n,x,incx,y,incy) zdotc_(&(n),(x),&(incx),(y),&(incy)) -#define ZDOTU(n,x,incx,y,incy) zdotu_(&(n),(x),&(incx),(y),&(incy)) -#define ZCOPY(n,x,incx,y,incy) zcopy_(&(n),(x),&(incx),(y),&(incy)) -#define ZSCAL(n,alpha,x,incx) zscal_(&(n),&(alpha),(x), &(incx)) -#define ZAXPY(n,alpha,x,incx,y,incy) zaxpy_(&(n), &(alpha), (x), \ - &(incx), y, &(incy)) -#define DZNRM2(n, x, incx) dznrm2_(&(n), (x), &(incx)) -#define ZGEMV(transa, m, n, alpha, a, lda, x, incx, beta, y, incy) \ - zgemv_((transa), &(m), &(n), &(alpha), (a), &(lda), (x), &(incx), \ - &(beta), (y), &(incy)) -#define ZGEMM(transa,transb,l,n,m,alpha,a,lda,b,ldb,beta,c,ldc) \ - zgemm_((transa), (transb), &(l), &(n), &(m), &(alpha), (a), \ - &(lda), b, &(ldb), &(beta), (c), &(ldc)) -#define ZGETRF(m, n, a, lda, ipvt, info) \ - zgetrf_(&(m), &(n), (a), &(lda), (ipvt), &(info)) -#define ZGETRS(trans, n, nrhs, a, lda, ipiv, b, ldb, info) \ - zgetrs_((trans), &(n), &(nrhs), (a), &(lda), (ipiv), (b), &(ldb), &(info)) -#define ZGETRI(n, a, lda, ipvt, work, lwork, info) \ - zgetri_(&(n), (a), &(lda), (ipvt), (work), &(lwork), &(info)) - -extern complex double zdotc_(int *n, complex double *x, int *incx, complex double *y, int - *incy); -extern complex double zdotu_(int *n, complex double *x, int *incx, complex double *y, int - *incy); -extern void zcopy_(int *n, complex double *x, int *incx, complex double *y, int - *incy); -extern void zscal_(int *n, complex double *alpha, complex double *x, int *incx); -extern void zaxpy_(int *n, complex double *alpha, complex double *x, int *incx, - complex double *y, int *incy); -extern double dznrm2_(int *n, complex double *x, int *incx); -extern void zgemv_(char *transa, int *m, int *n, complex double *alpha, - complex double *a, int *lda, complex double *x, int *incx, complex double - *beta, complex double *y, int *incy); -extern void zgemm_(char *transa, char *transb, int *l, int *m, int - *n, complex double *alpha, complex double *a, int *lda, complex double - *b, int *ldb, complex double *beta, complex double *c, int *ldc); -extern void zgetrf_(int *m, int *n, complex double *a, int *lda, int *ipvt, - int *info); -extern void zgetrs_(char *trans, int *n, int *nrhs, complex double *a, int - *lda, int *ipiv, complex double *b, int *ldb, int - *info); -extern void zgetri_(int *n, complex double *a, int *lda, int *ipvt, complex double - *work, int *lwork, int *info); -#else -#define ZDOTC(n,x,incx,y,incy) zdotc_((n), (x), (incx), (y), (incy)) -#define ZDOTU(n,x,incx,y,incy) zdotu_((n), (x), (incx), (y), (incy)) -#define ZCOPY(n,x,incx,y,incy) zcopy_((n), (x), (incx), (y), \ - (incy)) -#define ZSCAL(n,alpha,x,incx) zscal_((n), (alpha), (x), (incx)) -#define ZAXPY(n,alpha,x,incx,y,incy) zaxpy_((n), (alpha), (x), (incx), \ - (y), (incy)) -#define DZNRM2(n,x,incx) dznrm2_((n), (x), (incx)) - -#define ZGEMV(transa,m,n,alpha,a,lda,x,incx,beta,y,incy) \ - zgemv_((transa), (m), (n), \ - (alpha), (a), (lda), (x), (incx), \ - (beta), (y), (incy)) - -#define ZGEMM(transa,transb,l,n,m,alpha,a,lda,b,ldb,beta,c,ldc) \ - zgemm_((transa),(transb), \ - (l),(n),(m),(alpha),(a), \ - (lda),(b),(ldb),(beta),(c),(ldc)) -#define ZGETRF(m, n, a, lda, ipvt, info) \ - zgetrf_((m), (n), (a), (lda), (ipvt), &(info)) -#define ZGETRS(trans, n, nrhs, a, lda, ipiv, b, ldb, info) \ - zgetrs_((trans), (n), (nrhs), (a), (lda), (ipiv), (b), (ldb), &(info)) -#define ZGETRI(n, a, lda, ipvt, work, lwork, info) \ - zgetri_((n), (a), (lda), (ipvt), (work), (lwork), &(info)) -#endif - -extern void zgesvd_(char*, char*, int*, int*, complex double*, int*, double*, - complex double *, int*, complex double*, int*, complex double*, int*, - double*, int*); - -/* givens rotations*/ -double sgn(double x, double y); -double sign(double x); -double abs1(complex double x); -double abssq(complex double x); -extern void zclartg(complex double f, complex double g, double *cs, complex double *sn, complex double *rot); - -/*---- initialize complex datatypes and ops --- */ -extern void parms_InitComplex(); -void complex_sum(complex_type *in, complex_type *inout, int *len, MPI_Datatype *data_ptr); - -#endif diff --git a/lib/parms/include/parms_sys_dbl.h b/lib/parms/include/parms_sys_dbl.h deleted file mode 100755 index c36554ac7..000000000 --- a/lib/parms/include/parms_sys_dbl.h +++ /dev/null @@ -1,148 +0,0 @@ -/** - * @file parms_sys_dbl.h - * @author Zhongze Li - * @date Tue Oct 17 11:59:09 2006 - * - * @brief Macros and typedef needed by all other header files. - * This is used when the real version of the code is compiled - * (with the -DDBL flag) - * - * DOK - * - */ - -/* external BLAS */ -#if defined(DBL) -#define FLOAT double -#define ABS_VALUE(a) (fabs(a)) /* definition of absolute value */ -#define GDOT DDOT -#define GCOPY DCOPY -#define GSCAL DSCAL -#define GAXPY DAXPY -#define GNRM2 DNRM2 -#define GDMIN IDMIN -#define GGEMV DGEMV -#define GGEMM DGEMM -#define GGETRF DGETRF -#define GGETRS DGETRS -#define GGETRI DGETRI -#define GGESVD DGESVD -#if defined(FORTRAN_CAPS) -#define ddot_ DDOT -#define dcopy_ DCOPY -#define dscal_ DSCAL -#define daxpy_ DAXPY -#define dnrm2_ DNRM2 -#define idmin_ IDMIN -#define dgemv_ DGEMV -#define dgemm_ DGEMM -#define dgetrf_ DGETRF -#define dgetrs_ DGETRS -#define dgetri_ DGETRI -#define dgesvd_ DGESVD -#elif defined(FORTRAN_DOUBLE_UNDERSCORE) -#define ddot_ ddot__ -#define dcopy_ dcopy__ -#define dscal_ dscal__ -#define daxpy_ daxpy__ -#define dnrm2_ dnrm2__ -#define idmin_ idmin__ -#define dgemv_ dgemv__ -#define dgemm_ dgemm__ -#define dgetrf_ dgetrf__ -#define dgetrs_ dgetrs__ -#define dgetri_ dgetri__ -#define dgesvd_ dgesvd__ -#elif !defined(FORTRAN_UNDERSCORE) -#define BLAS_C_INTERFACE -#define ddot_ ddot -#define dcopy_ dcopy -#define dscal_ dscal -#define daxpy_ daxpy -#define dnrm2_ dnrm2 -#define idmin_ idmin -#define dgemv_ dgemv -#define dgemm_ dgemm -#define dgetrf_ dgetrf -#define dgetrs_ dgetrs -#define dgetri_ dgetri -#define dgesvd_ dgesvd -#endif - -/* FORTRAN subroutines */ -#ifndef BLAS_C_INTERFACE -#define DDOT(n,x,incx,y,incy) ddot_(&(n),(x),&(incx),(y),&(incy)) -#define DCOPY(n,x,incx,y,incy) dcopy_(&(n),(x),&(incx),(y),&(incy)) -#define DSCAL(n,alpha,x,incx) dscal_(&(n),&(alpha),(x), &(incx)) -#define DAXPY(n,alpha,x,incx,y,incy) daxpy_(&(n), &(alpha), (x), \ - &(incx), y, &(incy)) -#define DNRM2(n, x, incx) dnrm2_(&(n), (x), &(incx)) -#define IDMIN(n, sx, incx) idmin_((&(n), (sx), &(incx)) -#define DGEMV(transa, m, n, alpha, a, lda, x, incx, beta, y, incy) \ - dgemv_((transa), &(m), &(n), &(alpha), (a), &(lda), (x), &(incx), \ - &(beta), (y), &(incy)) -#define DGEMM(transa,transb,l,n,m,alpha,a,lda,b,ldb,beta,c,ldc) \ - dgemm_((transa), (transb), &(l), &(n), &(m), &(alpha), (a), \ - &(lda), b, &(ldb), &(beta), (c), &(ldc)) -#define DGETRF(m, n, a, lda, ipvt, info) \ - dgetrf_(&(m), &(n), (a), &(lda), (ipvt), &(info)) -#define DGETRS(trans, n, nrhs, a, lda, ipiv, b, ldb, info) \ - dgetrs_((trans), &(n), &(nrhs), (a), &(lda), (ipiv), (b), &(ldb), &(info)) -#define DGETRI(n, a, lda, ipvt, work, lwork, info) \ - dgetri_(&(n), (a), &(lda), (ipvt), (work), &(lwork), &(info)) - -extern double ddot_(int *n, double *x, int *incx, double *y, int - *incy); -extern void dcopy_(int *n, double *x, int *incx, double *y, int - *incy); -extern void dscal_(int *n, double *alpha, double *x, int *incx); -extern void daxpy_(int *n, double *alpha, double *x, int *incx, - double *y, int *incy); -extern double dnrm2_(int *n, double *x, int *incx); -extern void idmin_(int *n, double *sx, int *incx); -extern void dgemv_(char *transa, int *m, int *n, double *alpha, - double *a, int *lda, double *x, int *incx, double - *beta, double *y, int *incy); -extern void dgemm_(char *transa, char *transb, int *l, int *m, int - *n, double *alpha, double *a, int *lda, double - *b, int *ldb, double *beta, double *c, int *ldc); -extern void dgetrf_(int *m, int *n, double *a, int *lda, int *ipvt, - int *info); -extern void dgetrs_(char *trans, int *n, int *nrhs, double *a, int - *lda, int *ipiv, double *b, int *ldb, int - *info); -extern void dgetri_(int *n, double *a, int *lda, int *ipvt, double - *work, int *lwork, int *info); -#else -#define DDOT(n,x,incx,y,incy) ddot_((n), (x), (incx), (y), (incy)) -#define DCOPY(n,x,incx,y,incy) dcopy_((n), (x), (incx), (y), \ - (incy)) -#define DSCAL(n,alpha,x,incx) dscal_((n), (alpha), (x), (incx)) -#define DAXPY(n,alpha,x,incx,y,incy) daxpy_((n), (alpha), (x), (incx), \ - (y), (incy)) -#define DNRM2(n,x,incx) dnrm2_((n), (x), (incx)) - -#define IDMIN(n,sx,incx) idmin_((n), (sx), (incx)) -#define DGEMV(transa,m,n,alpha,a,lda,x,incx,beta,y,incy) \ - dgemv_((transa), (m), (n), \ - (alpha), (a), (lda), (x), (incx), \ - (beta), (y), (incy)) - -#define DGEMM(transa,transb,l,n,m,alpha,a,lda,b,ldb,beta,c,ldc) \ - dgemm_((transa),(transb), \ - (l),(n),(m),(alpha),(a), \ - (lda),(b),(ldb),(beta),(c),(ldc)) -#define DGETRF(m, n, a, lda, ipvt, info) \ - dgetrf_((m), (n), (a), (lda), (ipvt), &(info)) -#define DGETRS(trans, n, nrhs, a, lda, ipiv, b, ldb, info) \ - dgetrs_((trans), (n), (nrhs), (a), (lda), (ipiv), (b), (ldb), &(info)) -#define DGETRI(n, a, lda, ipvt, work, lwork, info) \ - dgetri_((n), (a), (lda), (ipvt), (work), (lwork), &(info)) -#endif - -extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, - double *, int*, double*, int*, double*, int*, - int*); - -#endif - diff --git a/lib/parms/include/parms_table.h b/lib/parms/include/parms_table.h deleted file mode 100755 index b01c55365..000000000 --- a/lib/parms/include/parms_table.h +++ /dev/null @@ -1,99 +0,0 @@ -/** - * @file parms_table.h - * @author Zhongze Li - * @date Tue Oct 17 11:59:56 2006 - * - * @brief The hash table functions. - * - * - */ - -#ifndef _PARMS_TABLE_HEADER_H -#define _PARMS_TABLE_HEADER_H - -#include "parms_sys.h" - -PARMS_CXX_BEGIN - -typedef unsigned int (*HashFcn)(int key); -typedef struct parms_Table_ *parms_Table; - -/** - * Create a hash table. - * - * Create a hash table with hf as the hash function. - * - * @param newT A pointer to the hash table created. - * @param hf The hash function. If hf is null, the default hash - * function is used. - * @param size The number of entries stored in the table. - * - * @return 0 on success. - */ -extern int parms_TableCreate(parms_Table *newT, HashFcn hf, int size); - -/** - * Free the memory for the table object pointed by self. - * - * @param self A pointer to the parms_Table object. - * - * @return 0 on success. - */ -extern int parms_TableFree(parms_Table *self); - -/** - * Get the corresponding value for a given key. - * - * If return NULL, then the entry with key is not in the table, - * otherwise return a pointer to the value. - * - * @param self A parms_Table object. - * @param key The key value. - * - * @return A pointer to the value. - */ -extern void *parms_TableGet(parms_Table self, int key); - -/** - * Put the pair (key, value) into the table. - * - * @param self A parms_Table object. - * @param key The key of the pair. - * @param val The value of the pair. - * - * @return 0 on success. - */ -extern int parms_TablePut(parms_Table self, int key, int val); - -/** - * Put the slot corresponding to the key from the table. - * - * @param table A parms_Table object. - * @param key The key of the pair to be removed. - * - * @return 0 on success. - */ -extern int parms_TableRemoveFromLast(parms_Table self, int key); - -/** - * Swap value of key1 with that of key2. - * - * @param table A parms_Table object. - * @param key1, key2 keys to be swapped. - * - * @return 0 on success. - */ -extern int parms_TableSwap(parms_Table self, int key1, int key2); - -/** - * Get the number of entries stored in the table. - * - * @param self A hash table object. - * - * @return The number of entries stored in the table. - */ -extern int parms_TableGetSize(parms_Table self); - -PARMS_CXX_END - -#endif diff --git a/lib/parms/include/parms_timer.h b/lib/parms/include/parms_timer.h deleted file mode 100755 index 537228684..000000000 --- a/lib/parms/include/parms_timer.h +++ /dev/null @@ -1,125 +0,0 @@ -/** - * @file parms_timer.h - * @author Zhongze Li - * @date Tue Oct 17 12:00:25 2006 - * - * @brief Functions related to the parms_Timer object. - * - * - */ - -#ifndef _PARMS_TIMER_H_ -#define _PARMS_TIMER_H_ - -#include "parms_sys.h" -#include "parms_viewer.h" - -PARMS_CXX_BEGIN - -typedef struct parms_Timer_ *parms_Timer; - -/** - * Create a parms_Timer object. - * - * @param self A pointer to the parms_Timer object created. - */ -extern void parms_TimerCreate(parms_Timer *self); - -/** - * Reset the parms_Timer object self. - * - * @param self A parms_Timer object. - * - * @return 0 on success. - */ -extern int parms_TimerReset(parms_Timer self); - -/** - * Reset the elapsed time of self to delay. - * - * @param self A parms_Timer object. - * @param delay Reset the elapsed time to delay seconds. - * - * @return 0 on success. - */ -extern int parms_TimerResetDelay(parms_Timer self, double delay); - -/** - * Pause the parms_Timer object self. - * - * @param self A parms_Timer object. - * - * @return 0 on success. - */ -extern int parms_TimerPause(parms_Timer self); - -/** - * Restart the timer. - * - * @param self A parms_Timer object. - * - * @return 0 on success. - */ -extern int parms_TimerRestart(parms_Timer self); - -/** - * Return The wall-clock time since the last call to - * parms_TimerReset, parms_TimerResetDelay, parms_TimerRestart. - * - * @param self A parms_Timer object. - * - * @return The elapsed wall-clock time in seconds. - */ -extern double parms_TimerGet(parms_Timer self); - -/** - * Free the memory for the parms_Timer object - * - * @param self A pointer to the parms_Timer object - * - * @return 0 on success. - */ -extern int parms_TimerFree(parms_Timer *self); - -/** - * Dump parms_Timer self via parms_Viewer object v. - * - * @param self A parms_Timer object. - * @param v A parms_Viewer object. - * - * @return 0 on success. - */ -extern int parms_TimerView(parms_Timer self, parms_Viewer v); - -/* - * - * Fortran Wrapper Functions - * -*/ - -extern void parms_timercreate_(parms_Timer *self, int *ierr); - -extern void parms_timerfree_(parms_Timer *self, int *ierr); - -extern void parms_timerget_(parms_Timer *self, double *t, int *ierr); - -extern void parms_timerpause_(parms_Timer *self, int *ierr); - -extern void parms_timerreset_(parms_Timer *self, int *ierr); - -extern void parms_timerresetdelay_(parms_Timer *self, double *delay, int - *ierr); - -extern void parms_timerrestart_(parms_Timer *self, int *ierr); - -extern void parms_timerview_(parms_Timer *self, parms_Viewer *v, int *ierr); - -/* - * - * End Fortran Wrapper Functions - * -*/ - -PARMS_CXX_END - -#endif diff --git a/lib/parms/include/parms_vec.h b/lib/parms/include/parms_vec.h deleted file mode 100755 index 733e98773..000000000 --- a/lib/parms/include/parms_vec.h +++ /dev/null @@ -1,289 +0,0 @@ -/** - * @file parms_vec.h - * @author Zhongze Li - * @date Tue Oct 17 12:01:25 2006 - * - * @brief Functions related to the parms_Vec object. - * - * - */ - -#ifndef _PARMS_VECTOR_H_ -#define _PARMS_VECTOR_H_ - -#include "parms_sys.h" -#include "parms_map.h" -#include "parms_viewer.h" -#include "parms_comm.h" - -PARMS_CXX_BEGIN - -/** - * Return the 2-norm of the vector. - * - * @param self A vector object. - * @param value The 2-norm returned. - * - * @return 0 on success. - */ -extern int parms_VecGetNorm2(FLOAT *self, REAL *value, parms_Map map); - -/** - * Scale a vector. - * - * All components of vector self on the local processor times scalar. - * \f$self = scalar \times self\f$. - * - * @param self A vector object. - * @param scalar A scalar. - * - * @return 0 on success. - */ -extern int parms_VecScale(FLOAT *self, FLOAT scalar, parms_Map map); - -/** - * Perform \f$self := scalar \times x + self\f$. - * - * @param self A vector object. - * @param x Another vector object. - * @param scalar A scalar. - * - * @return 0 on success. - */ -extern int parms_VecAXPY(FLOAT *self, FLOAT *x, FLOAT scalar, parms_Map map); - -/** - * Perform \f$self = scalar \times self + x\f$. - * - * @param self A vector object. - * @param x Another vector object. - * @param scalar A scalar. - * - * @return 0 on success. - */ -extern int parms_VecAYPX(FLOAT *self, FLOAT *x, FLOAT scalar, parms_Map map); - -/** - * Perform the (global) inner product of two vectors. - * - * value = self x^{T}. If self - * - * - * @param self A vector object. - * @param x Another vector object. - * @param value The inner product returned. - * - * @return 0 on success. - */ -extern int parms_VecDOT(FLOAT *self, FLOAT *x, FLOAT *value, parms_Map map); - -/** - * Perform the (global) inner product of two vectors. - * - * If self and x are real vectors, value = self x^{T}. If self - * and x are complex vectors, value = self \overline{x}^{T}. - * - * @param self A vector object. - * @param x Another vector object. - * @param value The inner product returned. - * - * @return 0 on success. - */ -int parms_VecDOTC(FLOAT *self, FLOAT *x, REAL *value, parms_Map is); - -/** - * Perform the inner product between self and an array of parms_Vec - * objects. - * - * The pseudo code: - * - * \f{verbatim} - * for (i = 0; i < n; i++) { - * result[i] = self * vecarray[i]; - * } - * \f} - * - * @param self A vector object. - * @param n The size of vecarray. - * @param vecarray An array of vector objects. - * @param aux An auxiliary array. - * @param result An array of size n to store inner products. - * - * @return 0 on success. - */ -extern int parms_VecDotArray(FLOAT *self, int n, FLOAT - **vecarray, FLOAT *result, parms_Map map); -/** - * Permute the vector object self. - * - * If the parms_Vec object and the parms_Mat object are created based - * on the same parms_Map object. Once matrix object is setup, - * variables on each processor are divided into two cateries: internal - * unknowns and interface unknowns. The parms_Vec should be permuted - * accordingly. The user needn't call self function directly. - * - * @param self A vector object. - * - * @return 0 on success. - */ -extern int parms_VecPerm(FLOAT *self, parms_Map map); - -/** - * Inverse permutation of the vector object self. - * The user needn't call this function directly. - * - * @param self A vector object. - * - * @return 0 on success. - */ -extern int parms_VecInvPerm(FLOAT *self, parms_Map map); - -/** - * Permute the vector object self into the vector aux. - * - * This uses the local permutation based on the local map object. - * The user needn't call self function directly. - * - * @param self A vector object. - * @param aux A vector object containing the permuted vector on return. - * - * @return 0 on success. - */ -extern int parms_VecPermAux(FLOAT *self, FLOAT *aux, parms_Map map); - -/** - * Inverse permutation the vector object self into the vector aux. - * - * This uses the local permutation based on the local map object. - * The user needn't call self function directly. - * - * @param self A vector object. - * @param aux A vector object containing the (inverse) permuted vector on return. - * - * @return 0 on success. - */ -extern int parms_VecInvPermAux(FLOAT *self, FLOAT *aux, parms_Map map); - -/** - * Insert or add values to vector object self. - * - * A pseudo code from the global point of view: - * - * \f{verbatim} - * for (i = 0; i < m; i++) { - * self[im[i]] = values[i]; - * } - * \f} - * - * @param self A vector object. - * @param m The number of variables to be inserted. - * @param im An array of global variable indices. - * @param value An array of values to be inserted to self.s - * @param mode The style of set values: - * -ADD add values to parms_Vec object self. - * -INSERT assign values to parms_Vec object self. - * - * @return 0 on success. - */ -extern int parms_VecSetValues(FLOAT *self, int m, int *im, FLOAT - *values, INSERTMODE mode, parms_Map map); - -/** - * Insert values to parms_Vec object self. This assumes the vector - * values are being set element-by-element. A call to parms_vecsetupElements - * is required to complete the vector once all entries have been added. - * - * A pseudo code from the global point of view: - * - * \f{verbatim} - * for (i = 0; i < m; i++) { - * self[im[i]] = values[i]; - * } - * \f} - * - * @param self A vector object. - * @param m The number of variables to be inserted. - * @param im An array of global variable indices. - * @param value An array of values to be inserted to self.s - * @param mode The style of set values: - * -ADD add values to parms_Vec object self. - * -INSERT assign values to parms_Vec object self. - * - * @return 0 on success. - */ -int parms_VecSetElementVector(FLOAT *self, int m, int *im, FLOAT - *values, INSERTMODE mode, parms_Map map); - -/** - * Completes setting up values for the distributed vector - * - * @param self A vector object. - * @param map A pARMS map object - * - * @return 0 on success. - */ -int parms_VecAssembleElementVector(FLOAT *self, parms_Map map); - -/** - * Gather distributed vector to a global array. - * - * @param self The distributed vector. - * @param ga A global vector. - * - * @return 0 on success. - */ -extern int parms_VecGather(FLOAT *self, FLOAT *ga, parms_Map map); - -/* - * - * Fortran Wrapper Functions - * -*/ - -extern void parms_vecaxpy_(FLOAT *self, FLOAT *x, FLOAT *scalar, parms_Map *map, int - *ierr); - -extern void parms_vecaypx_(FLOAT *self, FLOAT *x, FLOAT *scalar, parms_Map *map, int - *ierr); - -extern void parms_vecdot_(FLOAT *self, FLOAT *x, FLOAT *value, parms_Map *map, int - *ierr); - -extern void parms_vecdotc_(FLOAT *self, FLOAT *x, REAL *value, parms_Map *map, int - *ierr); - -extern void parms_vecdotarray_(FLOAT *self, int *n, FLOAT **vecarray, - FLOAT *result, parms_Map *map, int *ierr); - -extern void parms_vecgetnorm2_(FLOAT *self, REAL *value, parms_Map *map, int *ierr); - -extern void parms_vecscale_(FLOAT *self, FLOAT *scalar, parms_Map *map, int *ierr); - -extern void parms_vecsetvalues_(FLOAT *self, int *m, int *im, FLOAT *values, - INSERTMODE *mode, parms_Map *map, int *ierr); - -extern void parms_vecsetelementvector_(FLOAT *self, int *m, int *im, FLOAT *values, - INSERTMODE *mode, parms_Map *map, int *ierr); - -extern void parms_vecassembleelementvector_(FLOAT *self, parms_Map *map, int *ierr); - -extern void parms_vecperm_(FLOAT *self, parms_Map *map, int *ierr); - -extern void parms_vecinvperm_(FLOAT *self, parms_Map *map, int *ierr); - -extern void parms_vecpermaux_(FLOAT *self, FLOAT *aux, parms_Map *map, int *ierr); - -extern void parms_vecinvpermaux_(FLOAT *self, FLOAT *aux, parms_Map *map, int *ierr); - -extern void parms_vecgather_(FLOAT *self, FLOAT *ga, parms_Map *map, int *ierr); - - -/* - * - * End Fortran Wrapper Functions - * -*/ - -PARMS_CXX_END - -#endif diff --git a/lib/parms/include/parms_viewer.h b/lib/parms/include/parms_viewer.h deleted file mode 100755 index 582093212..000000000 --- a/lib/parms/include/parms_viewer.h +++ /dev/null @@ -1,100 +0,0 @@ -/** - * @file parms_viewer.h - * @author Zhongze Li - * @date Tue Oct 17 12:01:47 2006 - * - * @brief Functions related to parms_Viewer objects. - * - */ - -#ifndef _PARMS_VIEWER_H_ -#define _PARMS_VIEWER_H_ - -#include -#include -#include "parms_sys.h" - -PARMS_CXX_BEGIN - -typedef struct parms_Viewer_ *parms_Viewer; - -/** - * Create a parms_Viewer object. - * - * If PARMS_COUT and PARMS_CERR are input as fname, they stand for - * standard output and standard error, respectively. Otherwise, each - * PE create a file "fnameID.dat". ID stands for ID of PE. - * - * @param self A pointer to the parms_Viewer object. - * @param fname A file name to store data. - * - * @return 0 on success. - */ -extern int parms_ViewerCreate(parms_Viewer *self, char *fname); - -/** - * Free the memory of the parms_Viewer object. - * - * @param self A pointer to the parms_Viewer object. - * - * @return 0 on success. - */ -extern int parms_ViewerFree(parms_Viewer *self); - -/** - * Get a pointer to file pointer. - * - * @param self A parms_Viewer object. - * @param fp A pointer to the file pointer. - * - * @return 0 on success. - */ -extern int parms_ViewerGetFP(parms_Viewer self, FILE **fp); - -/** - * Store fp to the parms_Viewer object. - * - * @param self A parms_Viewer object. - * @param fp A file pointer. - * - * @return 0 on success. - */ -extern int parms_ViewerStoreFP(parms_Viewer self, FILE *fp); - -/** - * Retrieve the file name. - * - * @param self A parms_Viewer object. - * @param fname The file name retrieved. - * - * @return 0 on success. - */ -extern int parms_ViewerGetFname(parms_Viewer self, char **fname); - -/* - * - * Fortran Wrapper Functions - * -*/ - -extern void parms_viewercreate_(parms_Viewer *self, char *fname, int *ierr, - int len); - -extern void parms_viewerfree_(parms_Viewer *self, int *ierr); - -extern void parms_viewergetfp_(parms_Viewer *self, FILE **fp, int *ierr); - -extern void parms_viewergetfname_(parms_Viewer *self, char **fname, int - *ierr); - -extern void parms_viewerstorefp_(parms_Viewer *self, FILE *fp, int *ierr); - -/* - * - * End Fortran Wrapper Functions - * -*/ - -PARMS_CXX_END - -#endif diff --git a/lib/parms/src/DDPQ/MatOps.c b/lib/parms/src/DDPQ/MatOps.c deleted file mode 100755 index c8917427f..000000000 --- a/lib/parms/src/DDPQ/MatOps.c +++ /dev/null @@ -1,732 +0,0 @@ -#include -#include -#if defined(C99) -#include -#else -#include -#endif -#include "protos.h" - -void matvec( csptr mata, FLOAT *x, FLOAT *y ) -{ - /*--------------------------------------------------------------------- - | This function does the matrix vector product y = A x. - |---------------------------------------------------------------------- - | on entry: - | mata = the matrix (in SparRow form) - | x = a vector - | - | on return - | y = the product A * x - |--------------------------------------------------------------------*/ - /* local variables */ - int i, k, *ki; - FLOAT *kr; - - for (i=0; in; i++) { - y[i] = 0.0; - kr = mata->pa[i]; - ki = mata->pj[i]; - for (k=0; knnzrow[i]; k++) - y[i] += kr[k] * x[ki[k]]; - } - return; -} - -void invsp(int start, ilutptr ilusch, FLOAT *y, FLOAT *x) -{ - /*--------------------------------------------------------------------- - | - | This routine does the backward solve U x = y, where U is upper or - | bottom part of local upper triangular matrix - | - | Can be done in place. - | - | Zhongze Li, Aug. 17th, 2001 - | - |---------------------------------------------------------------------- - | on entry: - | start = the index of the first component - | n = one ore than the index owned by the processor - | y = a vector - | ilusch = the LU matrix as provided from the ILU routines. - | - | on return - | x = the product U^{-1} * y - | - |---------------------------------------------------------------------*/ - /* local variables */ - int i, k, *ki, n; - FLOAT *kr, t; - - n = ilusch->L->n; - - for (i=start; i < n; i++) { - t = y[i-start]; - if ( ilusch->L->nnzrow[i] > 0 ) { - kr = ilusch->L->pa[i]; - ki = ilusch->L->pj[i]; - for (k=0; kL->nnzrow[i]; k++) - if(ki[k] >= start && ki[k] < n) { - t -= kr[k]*y[ki[k]-start]; - } - } - x[i-start] = t; - } - - for (i=n-1; i>= start; i--) { - kr = ilusch->U->pa[i]; - ki = ilusch->U->pj[i]; - t = x[i-start]; - for (k=1; kU->nnzrow[i]; k++) - t -= kr[k] * x[ki[k]-start]; - x[i-start] = t*kr[0]; - } - return; -} - -void arms_Lsol(csptr mata, FLOAT *b, FLOAT *x) -{ - /*--------------------------------------------------------------------- - | This function does the forward solve L x = b. - | Can be done in place. - |---------------------------------------------------------------------- - | on entry: - | mata = the matrix (in SparRow form) - | b = a vector - | - | on return - | x = the solution of L x = b - |--------------------------------------------------------------------*/ - /* local variables */ - int i, k; - FLOAT *kr; - int *ki; - for (i=0; in; i++) { - x[i] = b[i]; - if ( mata->nnzrow[i] > 0 ) { - kr = mata->pa[i]; - ki = mata->pj[i]; - for (k=0; knnzrow[i]; k++) - x[i] -= kr[k]*x[ki[k]]; - } - } - return; -} -/*---------------end of arms_Lsol----------------------------------------- -----------------------------------------------------------------------*/ -void arms_Usol(csptr mata, FLOAT *b, FLOAT *x) -{ - /*--------------------------------------------------------------------- - | This function does the backward solve U x = b. - | Can be done in place. - |---------------------------------------------------------------------- - | on entry: - | mata = the matrix (in SparRow form) - | b = a vector - | - | on return - | x = the solution of U * x = b - | - |---------------------------------------------------------------------*/ - /* local variables */ - int i, k, *ki; - FLOAT *kr; - for (i=mata->n-1; i>=0; i--) { - kr = mata->pa[i]; - ki = mata->pj[i]; - x[i] = b[i] ; - for (k=1; knnzrow[i]; k++) - x[i] -= kr[k] * x[ki[k]]; - x[i] *= kr[0]; - } - return; -} -/*----------------end of arms_Usol---------------------------------------- -----------------------------------------------------------------------*/ - -int descend(p4ptr levmat, FLOAT *x, FLOAT *wk) -{ -/*--------------------------------------------------------------------- -| This function does the (block) forward elimination in ARMS -| new old -| | | | | | | -| | L 0 | | wx1 | | x1 | -| | | | | = | | -| | EU^{-1} I | | wx2 | | x2 | -| | | | | | | -| x used and not touched -- or can be the same as wk. -|--------------------------------------------------------------------*/ -/* local variables */ - int j, len=levmat->n, lenB=levmat->nB, *iperm=levmat->rperm; - FLOAT *work = levmat->wk; -/*------------------------------------------------------ -| apply permutation P to rhs -|-----------------------------------------------------*/ - for (j=0; jL, work, wk); /* sol: L x = x */ - arms_Usol(levmat->U, wk, work); /* sol: U work(2) = work */ -/*-------------------- compute x[lenb:.] = x [lenb:.] - E * work(1) */ - matvecz (levmat->E, work, &work[lenB], &wk[lenB]) ; - return 0; -} -/*----end-of-descend--------------------------------------------------- -|---------------------------------------------------------------------- -|--------------------------------------------------------------------*/ -int ascend (p4ptr levmat, FLOAT *x, FLOAT *wk) -{ - /*--------------------------------------------------------------------- - | This function does the (block) backward substitution: - | - | | | | | | | - | | U L^{-1}F | | wk1 | | x1 | - | | | | | = | | - | | 0 S | | wk2 | | x2 | <<-- x2 already computed. - | | | | | | | and we need x1 - | - | with x2 = S^{-1} wk2 [assumed to have been computed ] - |--------------------------------------------------------------------*/ - /*-------------------- local variables */ - int j, len=levmat->n, lenB=levmat->nB, *qperm=levmat->perm; - FLOAT *work = levmat->wk; - /*-------------------- copy x onto wk */ - matvec(levmat->F, &x[lenB], work); /* work = F * x_2 */ - arms_Lsol(levmat->L, work, work); /* work = L \ work */ - for (j=0; jU, work, work); /* wk1 = U \ wk1 */ - memcpy(&work[lenB],&x[lenB],(len-lenB)*sizeof(FLOAT)); - /*--------------------------------------- - | apply reverse permutation - |--------------------------------------*/ - for (j=0; jn; i++) { - kr = mata->pa[i]; - ki = mata->pj[i]; - t = y[i] ; - for (k=0; knnzrow[i]; k++) - t -= kr[k] * x[ki[k]]; - z[i] = t; - } - return; -} -/*---------------end of matvecz---------------------------------------- - *--------------------------------------------------------------------*/ - -p4ptr Lvsol2(FLOAT *x, int nlev, p4ptr levmat, ilutptr ilusch, int - flag) -{ - /* Macro L-solve -- corresponds to left (L) part of arms - | preconditioning operation -- - | on entry : - | x = right- hand side to be operated on by the preconditioner - | on return : x is overwritten - | x = output result of operation - | - | Note : in-place operation -- b and x can occupy the same space.. - | --------------------------------------------------------------------*/ - /*-------------------- local variables */ - int nloc=levmat->n, first, lenB; - p4ptr last=levmat; - /*-------------------- take care of special cases : nlev==0 --> lusol */ - if (nlev == 0) { - SchLsol(ilusch,x); - return (last); - } - first = 0; - /*-------------------- descend */ - while (levmat) { - nloc=levmat->n; - lenB =levmat->nB; - /*-------------------- left scaling */ - if (levmat->D1 != NULL) - dscale(nloc,levmat->D1, &x[first], &x[first]); - /*-------------------- RESTRICTION/ DESCENT OPERATION */ - if (lenB) - descend (levmat, &x[first], &x[first]); - first += lenB; - last = levmat; - levmat = levmat->next; - /*--------------------------------------------------------------------- - | next level - +--------------------------------------------------------------------*/ - } - if (flag) { - SchLsol(ilusch,&x[first]); - } - return last; -} - -int Uvsol2(FLOAT *x, int nlev, int n, p4ptr levmat, - ilutptr ilusch) -{ - /* Macro U-solve -- corresponds to left (L) part of arms - | preconditioning operation -- - | on entry : - | b = right- hand side to be operated on by the preconditioner - | on return = x has been overwritten = - | x = output result of operation - | - | Note : in-place operation -- b and x can occupy the same space.. - | --------------------------------------------------------------------*/ - - /*-------------------- local variables */ - int nloc, lenB, first; - /*-------------------- work array */ - /*-------------------- take care of special cases : nlev==0 --> lusol */ - /*-------------------- case of zero levels */ - if (nlev == 0) { - SchUsol(ilusch, x); - return(0); - } - /*-------------------- general case */ - nloc=levmat->n; - lenB=levmat->nB; - first = n - nloc; - /*-------------------- last level */ - first += lenB; - SchUsol(ilusch, &x[first]); - /*-------------------- other levels */ - while (levmat) { - nloc = levmat->n; - first -= levmat->nB; - if (levmat->n) - ascend(levmat, &x[first],&x[first]); - /*-------------------- right scaling */ - if (levmat->D2 != NULL) - dscale(nloc, levmat->D2, &x[first], &x[first]) ; - levmat = levmat->prev; - } - return 0; - /*-------------------- PROLONGATION/ ASCENT OPERATION */ -} - -int armsol2(FLOAT *x, arms Prec) -{ - /* | combined preconditioning operation -- combines the - | left and right actions. - | - | on entry : - | x = right- hand side to be operated on by the preconditioner - | on return : x is overwritten - - | x = output result of operation - | - | Note : in-place operation -- b and x can occupy the same space.. - | --------------------------------------------------------------------*/ - /*-------------------- local variables */ - p4ptr levmat = Prec->levmat; - ilutptr ilusch = Prec->ilus; - int nlev = Prec->nlev; - int n = levmat->n; - p4ptr last; - if (nlev == 0) { - n = ilusch->n; - SchLsol(ilusch, x); - SchUsol(ilusch, x); - return 0; - } - last = Lvsol2(x, nlev, levmat, ilusch, 1) ; - Uvsol2(x, nlev, n, last, ilusch) ; - return 0; -} - -void SchLsol(ilutptr ilusch, FLOAT *y) -{ - /*--------------------------------------------------------------------- - | Forward solve for Schur complement part = - |---------------------------------------------------------------------- - | on entry: - | ilusch = the LU matrix as provided from the ILU functions. - | y = the right-hand-side vector - | - | on return - | y = solution of LU x = y. [overwritten] - |---------------------------------------------------------------------*/ - /*-------------------- local variables */ - int n = ilusch->n, j, *perm = ilusch->rperm; - FLOAT *work = ilusch->wk; - /*-------------------- begin: right scaling */ - if (ilusch->D1 != NULL) - dscale(n, ilusch->D1, y, y); - /*-------------------- ONE SIDED ROW PERMS */ - if (perm != NULL) { - for (j=0; jL, work, y); - } else - arms_Lsol(ilusch->L, y, y); - /*---------------end of SchLsol--------------------------------------- - ----------------------------------------------------------------------*/ -} - -void SchUsol(ilutptr ilusch, FLOAT *y) -{ - /*--------------------------------------------------------------------- - | U-solve for Schur complement - - |---------------------------------------------------------------------- - | on entry: - | ilusch = the LU matrix as provided from the ILU functions. - | y = the right-hand-side vector - | - | on return - | y = solution of U x = y. [overwritten on y] - |----------------------------------------------------------------------*/ - int n = ilusch->n, j, *perm = ilusch->perm, *cperm; - FLOAT *work = ilusch->wk; - /* -------------------- begin by U-solving */ - /*-------------------- CASE: column pivoting used (as in ILUTP) */ - if (ilusch->perm2 != NULL) { - arms_Usol(ilusch->U, y, y); - cperm = ilusch->perm2; - for (j=0; jU, y, work); - /*-------------------- generic permutation */ - if (perm != NULL) { - for (j=0; jD2 !=NULL) - dscale(n, ilusch->D2, y, y); -} -/*---------------end of SchUsol--------------------------------------- -----------------------------------------------------------------------*/ - -int lusolC( FLOAT *y, FLOAT *x, iluptr lu ) -{ - /*---------------------------------------------------------------------- - * performs a forward followed by a backward solve - * for LU matrix as produced by iluk - * y = right-hand-side - * x = solution on return - * lu = LU matrix as produced by iluk. - *--------------------------------------------------------------------*/ - int n = lu->n, i, j, nnzrow, *ja; - FLOAT *D; - csptr L, U; - - L = lu->L; - U = lu->U; - D = lu->D; - - /* Block L solve */ - for( i = 0; i < n; i++ ) { - x[i] = y[i]; - nnzrow = L->nnzrow[i]; - ja = L->pj[i]; - for( j = 0; j < nnzrow; j++ ) { - x[i] -= x[ja[j]] * L->pa[i][j]; - } - } - /* Block -- U solve */ - for( i = n-1; i >= 0; i-- ) { - nnzrow = U->nnzrow[i]; - ja = U->pj[i]; - for( j = 0; j < nnzrow; j++ ) { - x[i] -= x[ja[j]] * U->pa[i][j]; - } - x[i] *= D[i]; - } - return (0); -} - -int rpermC(csptr mat, int *perm) -{ - /*---------------------------------------------------------------------- - | - | This subroutine permutes the rows of a matrix in SparRow format. - | rperm computes B = P A where P is a permutation matrix. - | The permutation P is defined through the array perm: for each j, - | perm[j] represents the destination row number of row number j. - | - |----------------------------------------------------------------------- - | on entry: - |---------- - | (amat) = a matrix stored in SparRow format. - | - | - | on return: - | ---------- - | (amat) = P A stored in SparRow format. - | - | integer value returned: - | 0 --> successful return. - | 1 --> memory allocation error. - |---------------------------------------------------------------------*/ - int **addj, *nnz, i, size=mat->n; - FLOAT **addm; - addj = (int **)Malloc( size*sizeof(int *), "rpermC" ); - addm = (FLOAT **) Malloc( size*sizeof(FLOAT *), "rpermC" ); - nnz = (int *) Malloc( size*sizeof(int), "rpermC" ); - for (i=0; ipj[i]; - addm[perm[i]] = mat->pa[i]; - nnz[perm[i]] = mat->nnzrow[i]; - } - for (i=0; ipj[i] = addj[i]; - mat->pa[i] = addm[i]; - mat->nnzrow[i] = nnz[i]; - } - free(addj); - free(addm); - free(nnz); - return 0; -} - -int cpermC(csptr mat, int *perm) -{ - /*---------------------------------------------------------------------- - | - | This subroutine permutes the columns of a matrix in SparRow format. - | cperm computes B = A P, where P is a permutation matrix. - | that maps column j into column perm(j), i.e., on return - | The permutation P is defined through the array perm: for each j, - | perm[j] represents the destination column number of column number j. - | - |----------------------------------------------------------------------- - | on entry: - |---------- - | (mat) = a matrix stored in SparRow format. - | - | - | on return: - | ---------- - | (mat) = A P stored in SparRow format. - | - | integer value returned: - | 0 --> successful return. - | 1 --> memory allocation error. - |---------------------------------------------------------------------*/ - int i, j, *newj, size=mat->n, *aja; - newj = (int *) Malloc( size*sizeof(int), "cpermC" ); - for (i=0; ipj[i]; - for (j=0; jnnzrow[i]; j++) - newj[j] = perm[aja[j]]; - - for (j=0; jnnzrow[i]; j++) - aja[j] = newj[j]; - mat->pj[i] = aja; - } - free(newj); - return 0; -} - -int dpermC(csptr mat, int *perm) -{ - /*---------------------------------------------------------------------- - | - | This subroutine permutes the rows and columns of a matrix in - | SparRow format. dperm computes B = P^T A P, where P is a permutation - | matrix. - | - |----------------------------------------------------------------------- - | on entry: - |---------- - | (amat) = a matrix stored in SparRow format. - | - | - | on return: - | ---------- - | (amat) = P^T A P stored in SparRow format. - | - | integer value returned: - | 0 --> successful return. - | 1 --> memory allocation error. - |---------------------------------------------------------------------*/ - if (rpermC(mat, perm)) return 1; - if (cpermC(mat, perm)) return 1; - return 0; -} - -int CSparTran( csptr amat, csptr bmat, CompressType *compress ) -{ - /*---------------------------------------------------------------------- - | Finds the compressed transpose of a matrix stored in SparRow format. - | Patterns only. - |----------------------------------------------------------------------- - | on entry: - |---------- - | (amat) = a matrix stored in SparRow format. - | (compress) = quotient graph of matrix amat - | - | on return: - | ---------- - | (bmat) = the compressed transpose of (mata) stored in SparRow - | format. - | - | integer value returned: - | 0 --> successful return. - | 1 --> memory allocation error. - |---------------------------------------------------------------------*/ - int i, j, *ind, nnzrow, pos, size=amat->n, *aja; - ind = bmat->nnzrow; - - for (i=0; ipj[i]; - nnzrow = amat->nnzrow[i]; - for (j=0; j < nnzrow; j++) { - pos = aja[j]; - if( compress[pos].grp == -1 ) { - ind[pos]++; - } - } - } - - /*-------------------- allocate space */ - for (i=0; ipj[i] = NULL; - continue; - } - bmat->pj[i] = (int *)Malloc( ind[i]*sizeof(int), "CSparTran" ); - ind[i] = 0; /* indicate next available position of each row */ - } - /*-------------------- now do the actual copying */ - for (i=0; ipj[i]; - nnzrow = amat->nnzrow[i]; - for (j = 0; j < nnzrow; j++) { - pos = aja[j]; - if( compress[pos].grp == -1 ) { - bmat->pj[pos][ind[pos]] = i; - ind[pos]++; - } - } - } - return 0; -} - -int condestArms(arms armspre, FLOAT *y, FILE *fp ) -{ - /*-------------------- simple estimate of cond. number of precon */ - int n = armspre->n, i; - double norm = 0.0; - - for( i = 0; i < n; i++ ) - y[i] = 1.0; - armsol2(y, armspre) ; - for( i = 0; i < n; i++ ) { - norm = max( norm, ABS_VALUE(y[i]) ); - } - fprintf( fp, "ARMS inf-norm lower bound = : %16.2f\n", norm ); - if( norm > 1e30 ) { - return -1; - } - return 0; -} - -void Lsolp(int start, csptr mata, FLOAT *b, FLOAT *y) -{ - /*--------------------------------------------------------------------- - | - | This routine does the forward solve L y = b. where L is upper or - | bottom part of local low triangular matrix - | - | Can be done in place. - | - | Zhongze Li, Aug. 17th, 2001 - | - |---------------------------------------------------------------------- - | on entry: - | start = the index of the first component - | n = one ore than the index owned by the processor - | b = a vector - | mata = the matrix (in SparRow form) - | - | on return - | y = the product L^{-1} * b - | - |--------------------------------------------------------------------*/ - /* local variables */ - int i, k; - FLOAT *kr; - int *ki, n; - - n = mata->n; - for (i=0; innzrow[i] > 0 ) { - kr = mata->pa[i]; - ki = mata->pj[i]; - for (k=0; knnzrow[i]; k++) - if(ki[k] < start) { - y[i] -= kr[k]*y[ki[k]]; - } - } - } -} - -void Usolp(int start, csptr mata, FLOAT *y, FLOAT *x) -{ - /*--------------------------------------------------------------------- - | - | This routine does the backward solve U x = y, where U is upper or - | bottom part of local upper triangular matrix - | - | Can be done in place. - | - | Zhongze Li, Aug. 17th, 2001 - | - |---------------------------------------------------------------------- - | on entry: - | start = the index of the first component - | n = one ore than the index owned by the processor - | y = a vector - | mata = the matrix (in SparRow form) - | - | on return - | x = the product U^{-1} * y - | - |---------------------------------------------------------------------*/ - /* local variables */ - int i, k, *ki; - FLOAT *kr, t; - - for (i=start-1; i>= 0; i--) { - kr = mata->pa[i]; - ki = mata->pj[i]; - t = y[i]; - for (k=1; knnzrow[i]; k++) - t -= kr[k] * x[ki[k]]; - x[i] = t*kr[0]; - } -} diff --git a/lib/parms/src/DDPQ/PQ.c b/lib/parms/src/DDPQ/PQ.c deleted file mode 100755 index 12538b90e..000000000 --- a/lib/parms/src/DDPQ/PQ.c +++ /dev/null @@ -1,448 +0,0 @@ -#include -#include -#include -#if defined(C99) -#include -#else -#include -#endif -#include "protos.h" - -#define ALPHA 0.00001 - -#define DBL_EPSILON 2.2204460492503131e-16 // double epsilon - -int PQperm(csptr mat, int *Pord, int *Qord, int *nnod, - double tol, int nbnd) -{ -/*--------------------------------------------------------------------- -| algorithm for nonsymmetric block selection - -|---------------------------------------------------------------------- -| Input parameters: -| ----------------- -| (mat) = matrix in SparRow format -| -| tol = a tolerance for excluding a row from B block -| -| bsize not used here - it is used in arms2.. -| -| nbnd = number of interior variables. -| -| Output parameters: -| ------------------ -| Pord = row permutation array. Row number i will become row number -| Pord[i] in permuted matrix. (Old to new labels) -| Qord = column permutation array. Column number j will become -| row number Qord[j] in permuted matrix. -| [destination lists] - i.e., old to new arrays= -| -| nnod = number of elements in the B-block -| -|---------------------------------------------------------------------*/ -/*-------------------- local variables */ - int *icor, *jcor, *row; - int i, j, ii, k, col, jj, rnz, nzi, n=mat->n, count, numnode; - double aij, rn; - FLOAT *mrow; -/*-----------------------------------------------------------------------*/ - for (j=0; jpj[ii]; - mrow = mat->pa[ii]; - nzi = mat->nnzrow[ii] ; -/*-------------------- rnz = already assigned cols (either in L or F) */ - rn = ABS_VALUE(mrow[0]); - rnz = (nzi-1) ; - for (k=0; k < nzi; k++) { - aij = ABS_VALUE(mrow[k]); - col = row[k]; - if (Qord[col] >=0 ) { - rn -= aij; - rnz-- ; - } - else if (Qord[col] == -2) { - rnz--; - } - } - if (rn < 0.0) continue; - Pord[ii] = numnode; - Qord[jj] = numnode; - numnode++; -/*-------------------- acceptance test among others */ - for (k=0; k < nzi; k++) { - col = row[k]; - if (Qord[col] != -1) continue; - aij = ABS_VALUE(mrow[k]); - if (rnz*aij > rn) - Qord[col] = -2; - else - rn -= aij; - rnz--; - } - } - /*-------------------- number of B nodes */ - *nnod = numnode; - /* printf(" nnod found = %d \n",*nnod); */ -/*-------------------------------------------------- -| end-main-loop - complete permutation arrays -|-------------------------------------------------*/ - for (i=0; i \n") ; - check_perm(n, Pord) ; - check_perm(n, Qord) ; - */ -/*-------------------------------------------------- -| clean up before returning -|-------------------------------------------------*/ - free(icor); - free(jcor); - return 0; -} -/*--------------------------------------------------------------------- -|-----end-of-PQperm-------------------------------------------------- -|--------------------------------------------------------------------*/ - -int add2is(int *last, int nod, int *iord, int *riord) -{ -/*---------------------------------------------------------------------- -| adds element nod to independent set -|---------------------------------------------------------------------*/ - (*last)++; - iord[nod] = *last; - riord[*last] = nod; - return 0; -} -/*--------------------------------------------------------------------- -|---- end of add2is --------------------------------------------------- -|--------------------------------------------------------------------*/ -int add2com(int *nback, int nod, int *iord, int *riord) -{ -/*---------------------------------------------------------------------- -| adds element nod to independent set -|---------------------------------------------------------------------*/ - iord[nod] = *nback; - riord[*nback] = nod; - (*nback)--; - return 0; -} -/*--------------------------------------------------------------------- -|---- end of add2com -------------------------------------------------- -|--------------------------------------------------------------------*/ -int indsetC(csptr mat, int bsize, int *iord, int *nnod, double tol, - int nbnd) -{ -/*--------------------------------------------------------------------- -| greedy algorithm for independent set ordering -- -|---------------------------------------------------------------------- -| Input parameters: -| ----------------- -| (mat) = matrix in SparRow format -| -| bsize = integer (input) the target size of each block. -| each block is of size >= bsize. -| -| w = weight factors for the selection of the elements in the -| independent set. If w(i) is small i will be left for the -| vertex cover set. -| -| tol = a tolerance for excluding a row from independent set. -| -| nbnd = number of interior variables. -| -| Output parameters: -| ------------------ -| iord = permutation array corresponding to the independent set -| ordering. Row number i will become row number iord[i] in -| permuted matrix. -| -| nnod = (output) number of elements in the independent set. -| -|----------------------------------------------------------------------- -| the algorithm searches nodes in lexicographic order and groups -| the (BSIZE-1) nearest nodes of the current to form a block of -| size BSIZE. The current algorithm does not use values of the matrix. -|---------------------------------------------------------------------*/ -/* local variables */ - int nod, jcount, lastlev, begin, last0, last, nback, mid, - j1, j2, jcol, inod, jnod, j, k, jcount0, begin0, *rowj; - int prog, n=mat->n, *riord; - double *w; - csptr matT,gmat; - -/*-----------------------------------------------------------------------*/ - riord = (int *) Malloc(n*sizeof(int), "indsetC:1" ); - w = (double *) Malloc(n*sizeof(double), "indsetC:2" ); - matT = (csptr) Malloc(sizeof(SparMat), "indsetC:3" ); -/* call weights to compute the weights for input matrix.. */ - setupCS(matT, mat->n,1); - SparTran(mat, matT, 1, 0); - SparTran(matT, mat, 1, 1); - weightsC(mat, w); -/*---------------------------------------------------------------------- -| scan all nodes first to eliminate those not satisfying DD criterion -+----------------------------------------------------------------------*/ - nback = n-1; - nod = 0; - for(j=0; j=nbnd; j--) { - add2com(&nback, j, iord, riord); - } - for(j=0; j< nbnd; j++) { - if (w[j] < tol) { - add2com(&nback, j, iord, riord); - nod++; - } - } - last = -1; - for (nod=0; nod= mat->n) goto label50; - } -/*-------------------- initialize level-set - contains nod (only)*/ - add2is(&last, nod, iord, riord); - begin = last; - begin0 = begin; - lastlev = begin; - jcount = 1; -/*---------------------------------------------------------------------- -| put all the nearest neighbor nodes of the current node into -| the block until the number is BSIZE. -|---------------------------------------------------------------------*/ - prog = 1; - while (jcount < bsize && prog) { -/*-------------------- traverse all the current level-set */ - last0 = last; - jcount0 = jcount; - for (inod=begin; inod<=last0; inod++) { - jnod = riord[inod]; -/*-------------------- This assumes A is not symmetric. */ - gmat = mat; - for (k=0; k<2; k++) { - rowj = gmat->pj[jnod]; - for (j=0; jnnzrow[jnod]; j++) { - jcol = rowj[j]; - if (iord[jcol] == -1 ) { - add2is(&last, jcol, iord, riord); - jcount++; - } - } - gmat = matT; - } - } - prog = jcount > jcount0 ? 1 : 0; - lastlev = begin; - begin = last0+1; - } -/*----------------------------------------------------------------------- -| the neighbors of elements of last level go to the complement -| gmat loop over original matrix and its transpose -+-----------------------------------------------------------------------*/ - gmat = mat; - for (k=0; k<2; k++) { - for (inod=lastlev; inod<=last; inod++) { - jnod = riord[inod]; - rowj = gmat->pj[jnod]; - for (j=0; jnnzrow[jnod]; j++){ - jcol = rowj[j]; - if (iord[jcol] == -1) - add2com(&nback, jcol, iord, riord); - } - } - gmat = matT; - } -/* reverse ordering for this level */ - mid = (begin0+last) / 2; - for (inod=begin0; inod<=mid; inod++) { - j = last - inod + begin0; - jnod = riord[inod]; - riord[inod] = riord[j]; - riord[j] = jnod; - } - } -/*-------------------------------------------------- -| end-main-loop -|-------------------------------------------------*/ -/*-------------------- relabel nodes of vertex cover */ -label50: - *nnod = last; - j1 = *nnod; - for (j2=*nnod+1; j2 -1) { - if (++j1 != j2) { - j = riord[j2]; - riord[j2] = riord[j1]; - riord[j1] = j; - } - } - } -/*-------------------- obtain reverse permutation array */ - for (j=0; jn, *kj, kz; - double tdia, wmax=0.0, tnorm; - FLOAT *kr; - for (irow=0; irownnzrow[irow]; - kr = mat->pa[irow]; - kj = mat->pj[irow]; - tnorm = 0.0; - tdia = 0.0; - for (k=0; k 0.0) - tnorm = tdia / tnorm; - w[irow] = tnorm; - if (tnorm > wmax) wmax = tnorm; - } - for (irow=0; irownnzrow; - weight = (double *) malloc(nbnd*sizeof(double)); - nzi = (int *)malloc(nbnd*sizeof(int)); - if ( weight==NULL) return 1; - /*-------------------- compute max entry for each row */ - wmax = 0.0; - - for (i=0; ipj[i]; - mrow = mat->pa[i]; - tmax = 0.0; - kmax = 0; - rownorm = 0.0; - nzi[i] = 0; - for (k = 0; k DBL_EPSILON*ABS_VALUE(t)) { - rownorm += t; - if (tmax < t) { - tmax = t; - kmax = k; - } - } - } - } - jmax = jcol[kmax]; - jcor[i] = jmax; - if (job && kmax != 0) { - t1 = mrow[kmax]; - mrow[kmax] = mrow[0]; - mrow[0] = t1; - jcol[kmax] = jcol[0]; - jcol[0] = jmax; - } -/*-------------------- save max diag. dominance ratio */ - t = tmax / rownorm; - if (wmax < t) wmax = t; - weight[i] = t; - /* remove!! ALREADY ASSIGNED */ - jcor[i] = jmax; - } - -/*-------------------- now select according to tol */ - countL = 0; - for (i=0; i< nbnd; i++) { - t = weight[i] ; - col = jcor[i]; - if (t < wmax*tol) continue ; - weight[countL] = t /((double) nzi[i]) ; - icor[countL] = i; - jcor[countL] = col; - countL++; - } -/*-------------------- sort them */ - qsortR2I(weight, icor, jcor, 0, countL-1); - *count = countL; - free(weight); - free(nzi); - return 0; -} -/*--------------------------------------------------------------------- -|---- end of preSel --------------------------------------------------- -|--------------------------------------------------------------------*/ - diff --git a/lib/parms/src/DDPQ/arms2.c b/lib/parms/src/DDPQ/arms2.c deleted file mode 100755 index 5dad20138..000000000 --- a/lib/parms/src/DDPQ/arms2.c +++ /dev/null @@ -1,690 +0,0 @@ -/*---------------------------------------------------------------------- - * Parallel Multi-Level Block ILUT PRECONDITIONER - * arms2 : parallel arms2 - *--------------------------------------------------------------------*/ - -#include -#include -#include -#if defined(C99) -#include -#else -#include -#endif -#define PERMTOL 0.99 /* 0 --> no permutation 0.01 to 0.1 good */ -#include "protos.h" - -static int arms_free_vcsr(parms_Operator *self) -{ - parms_arms_data data; - - data = (parms_arms_data)(*self)->data; - cleanARMS(data); - PARMS_FREE(data); - return 0; -} - -static int arms_view_vcsr(parms_Operator self, parms_Viewer v) -{ -/* So far, only available for viewing last level operator */ - - parms_arms_data data; - ilutptr LU; - int i, j, n, nnz, *pj; - FLOAT *pa; - FILE *fp; - - parms_ViewerGetFP(v, &fp); - data = (parms_arms_data)self->data; - LU = data->ilus; - n = LU->n; - -/* L part */ - fprintf(fp, "L part of the last level matrix:\n"); - fprintf(fp, "n = %d\n", n); -#if defined(DBL_CMPLX) - for (i = 0; i < n; i++) { - nnz = LU->L->nnzrow[i]; - pj = LU->L->pj[i]; - pa = LU->L->pa[i]; - fprintf(fp, "nnzrow[%d]=%d\n", i, nnz); - for (j = 0; j < nnz; j++) { - fprintf(fp, "(%d,%d,%f %f) ", i, pj[j], creal(pa[j]), cimag(pa[j])); - } - } -#else - for (i = 0; i < n; i++) { - nnz = LU->L->nnzrow[i]; - pj = LU->L->pj[i]; - pa = LU->L->pa[i]; - fprintf(fp, "nnzrow[%d]=%d\n", i, nnz); - for (j = 0; j < nnz; j++) { - fprintf(fp, "(%d,%d,%f) ", i, pj[j], pa[j]); - } - } -#endif -/* U part */ - fprintf(fp, "U part of the matrix:\n"); - fprintf(fp, "n = %d\n", n); -#if defined(DBL_CMPLX) - for (i = 0; i < n; i++) { - nnz = LU->U->nnzrow[i]; - pj = LU->U->pj[i]; - pa = LU->U->pa[i]; - fprintf(fp, "nnzrow[%d]=%d\n", i, nnz); - for (j = 0; j < nnz; j++) { - fprintf(fp, "(%d,%d,%f %f) ", i, pj[j], creal(pa[j]), cimag(pa[j])); - } - } -#else - for (i = 0; i < n; i++) { - nnz = LU->U->nnzrow[i]; - pj = LU->U->pj[i]; - pa = LU->U->pa[i]; - fprintf(fp, "nnzrow[%d]=%d\n", i, nnz); - for (j = 0; j < nnz; j++) { - fprintf(fp, "(%d,%d,%f) ", i, pj[j], pa[j]); - } - } -#endif - parms_ViewerStoreFP(v, fp); - - return 0; -} - -static void parms_arms_nnz(parms_Operator self, int *nnz_mat, int - *nnz_pc) -{ - parms_arms_data data; - - data = (parms_arms_data)self->data; - *nnz_mat = data->nnz_mat; - *nnz_pc = data->nnz_prec; -} - -static int parms_arms_lsol_vcsr(parms_Operator self, FLOAT *y, FLOAT - *x) -{ - parms_arms_data data; - p4ptr levmat; - ilutptr ilus; - int *ipar, schur_start, nlev, n; - - data = (parms_arms_data)self->data; - levmat = data->levmat; - ilus = data->ilus; - ipar = data->ipar; - schur_start = data->schur_start; - - if (ipar[0] == 0) { - Lsolp(schur_start, ilus->L, y, x); - return 0; - } - nlev = data->nlev; - n = data->n; - PARMS_MEMCPY(x, y, n); - Lvsol2(x, nlev, levmat, ilus, 0) ; - return 0; -} - -static int parms_arms_sol_vcsr(parms_Operator self, FLOAT *y, - FLOAT *x) -{ - parms_arms_data data; - int n; - - data = (parms_arms_data)self->data; - - n = data->n; - - PARMS_MEMCPY(x, y, n); - - armsol2(x, data); - - return 0; -} - -static int parms_arms_invs_vcsr(parms_Operator self, FLOAT *y, FLOAT - *x) -{ - parms_arms_data data; - ilutptr ilus; - int *ipar, schur_start, n; - - data = (parms_arms_data)self->data; - schur_start = data->schur_start; - ilus = data->ilus; - ipar = data->ipar; - - n = ilus->n; - if (y == NULL || x == NULL) { - return 0; - } - if (ipar[0] == 0) { - invsp(schur_start, ilus, y, x); - } - else { - PARMS_MEMCPY(x, y, n); - SchLsol(ilus, x); - SchUsol(ilus, x); - - } - - return 0; -} - -static int parms_arms_ascend_vcsr(parms_Operator self, FLOAT *y, FLOAT - *x) -{ - parms_arms_data data; - p4ptr levmat=NULL, last=NULL; - ilutptr ilus; - int *ipar, schur_start, n, nloc, lenB, first; - - data = (parms_arms_data)self->data; - levmat = data->levmat; - ilus = data->ilus; - schur_start = data->schur_start; - ipar = data->ipar; - n = data->n; - - if (ipar[0] == 0) { - Usolp(schur_start, ilus->U, y, x); - return 0; - } - - while (levmat) { - last = levmat; - levmat = levmat->next; - } - levmat = last; - - nloc=levmat->n; - lenB=levmat->nB; - first = n - nloc; - /*-------------------- last level */ - first += lenB; - /*-------------------- other levels */ - while (levmat) { - nloc = levmat->n; - first -= levmat->nB; - if (levmat->n) - ascend(levmat, &x[first],&x[first]); - /*-------------------- right scaling */ - if (levmat->D2 != NULL) - dscale(nloc, levmat->D2, &x[first], &x[first]) ; - levmat = levmat->prev; - } - return 0; -} - -static int parms_arms_getssize_vcsr(parms_Operator self) -{ - parms_arms_data data; - - data = (parms_arms_data)self->data; - return data->schur_start; -} - -static struct parms_Operator_ops parms_arms_sol_vptr = { - parms_arms_sol_vcsr, /* apply */ - parms_arms_lsol_vcsr, /* lsol */ - parms_arms_invs_vcsr, /* invs */ - NULL, /* getu !!! WARNING, UNASSIGNED !!! */ - parms_arms_ascend_vcsr, /* ascend */ - parms_arms_getssize_vcsr, /* getssize */ - parms_arms_nnz, /* getnnz */ - arms_free_vcsr, /* operator_free */ - arms_view_vcsr /* operator_view */ -}; - - -int parms_arms_vcsr(parms_Mat self, parms_FactParam param, void *mat, - parms_Operator *op) -{ -/*--------------------------------------------------------------------- -| MULTI-LEVEL BLOCK ILUT PRECONDITIONER. -| ealier version: June 23, 1999 BJS -- -| version2: Dec. 07th, 2000, YS [reorganized ] -| version 3 (latest) Aug. 2005. [reorganized + includes ddpq] -+---------------------------------------------------------------------- -| ON ENTRY: -| ========= -| ( Amat ) = original matrix A stored in C-style Compressed Sparse -| Row (CSR) format -- -| see LIB/heads.h for the formal definition of this format. -| -| ipar[0:17] = integer array to store parameters for -| arms construction (arms2) -| -| ipar[0]:=nlev. number of levels (reduction processes). -| see also "on return" below. -| -| ipar[1]:= level-reordering option to be used. -| if ipar[1]==0 ARMS uses a block independent set ordering -| with a sort of reverse cutill Mc Kee ordering to build -| the blocks. This yields a symmetric ordering. -| in this case the reordering routine called is indsetC -| if ipar[1] == 1, then a nonsymmetric ordering is used. -| In this case, the B matrix is constructed to be as -| diagonally dominant as possible and as sparse as possble. -| in this case the reordering routine called is ddPQ. -| -| ipar[2]:=bsize. for indset Dimension of the blocks. -| bsize is only a target block size. The size of -| each block can vary and is >= bsize. -| for ddPQ: this is only the smallest size of the -| last level. arms will stop when either the number -| of levels reaches nlev (ipar[0]) or the size of the -| next level (C block) is less than bsize. -| -| ipar[3]:=iout if (iout > 0) statistics on the run are -| printed to FILE *ft -| -| ipar[4]:= Krylov subspace dimension for last level -| ipar[4] == 0 means only backward/forward solve -| is performed on last level. -| ipar[5]:= maximum # iterations on last level -| -| ipar[6-9] NOT used [reserved for later use] - set to zero. -| -| The following set method options for arms2. Their default values can -| all be set to zero if desired. -| -| ipar[10-13] == meth[0:3] = method flags for interlevel blocks -| ipar[14-17] == meth[0:3] = method flags for last level block - -| with the following meaning in both cases: -| meth[0] nonsummetric permutations of 1: yes. affects rperm -| USED FOR LAST SCHUR COMPLEMENT -| meth[1] permutations of columns 0:no 1: yes. So far this is -| USED ONLY FOR LAST BLOCK [ILUTP instead of ILUT]. -| (so ipar[11] does no matter - enter zero). If -| ipar[15] is one then ILUTP will be used instead -| of ILUT. Permutation data stored in: perm2. -| meth[2] diag. row scaling. 0:no 1:yes. Data: D1 -| meth[3] diag. column scaling. 0:no 1:yes. Data: D2 -| all transformations related to parametres in meth[*] (permutation, -| scaling,..) are applied to the matrix before processing it -| -| ft = file for printing statistics on run -| -| droptol = Threshold parameters for dropping elements in ILU -| factorization. -| droptol[0:4] = related to the multilevel block factorization -| droptol[5:6] = related to ILU factorization of last block. -| This flexibility is more than is really needed. one can use -| a single parameter for all. it is preferable to use one value -| for droptol[0:4] and another (smaller) for droptol[5:6] -| droptol[0] = threshold for dropping in L [B]. See piluNEW.c: -| droptol[1] = threshold for dropping in U [B]. -| droptol[2] = threshold for dropping in L^{-1} F -| droptol[3] = threshold for dropping in E U^{-1} -| droptol[4] = threshold for dropping in Schur complement -| droptol[5] = threshold for dropping in L in last block -| [see ilutpC.c] -| droptol[6] = threshold for dropping in U in last block -| [see ilutpC.c] -| This provides a rich selection - though in practice only 4 -| parameters are needed [which can be set to be the same - actually] -- indeed it makes sense to take -| droptol[0] = droptol[1], droptol[2] = droptol[3], -| and droptol[4] = droptol[5] -| -| lfil = lfil[0:6] is an array containing the fill-in parameters. -| similar explanations as above, namely: -| lfil[0] = amount of fill-in kept in L [B]. -| lfil[1] = amount of fill-in kept in U [B]. -| lfil[2] = amount of fill-in kept in E L\inv -| lfil[3] = amount of fill-in kept in U \inv F -| lfil[4] = amount of fill-in kept in S . -| lfil[5] = amount of fill-in kept in L_S . -| lfil[6] = amount of fill-in kept in U_S -| -| tolind = tolerance parameter used by the indset function. -| a row is not accepted into the independent set if -| the *relative* diagonal tolerance is below tolind. -| see indset function for details. Good values are -| between 0.05 and 0.5 -- larger values tend to be better -| for harder problems. -| -| ON RETURN: -|============= -| -| (PreMat) = arms data structure which consists of two parts: -| levmat and ilsch. -| -| ++(levmat)= permuted and sorted matrices for each level of the block -| factorization stored in PerMat4 struct. Specifically -| each level in levmat contains the 4 matrices in: -| -| -| |\ | | -| | \ U | | -| | \ | F | -| | L \ | | -| | \ | | -| |----------+-------| -| | | | -| | E | | -| | | | -| -| plus a few other things. See LIB/heads.h for details. -| -| ++(ilsch) = IluSpar struct. If the block of the last level is: -| -| | B F | -| A_l = | | -| | E C | -| -| then IluSpar contains the block C and an ILU -| factorization (matrices L and U) for the last -| Schur complement [S ~ C - E inv(B) F ] -| (modulo dropping) see LIB/heads.h for details. -| -| ipar[0] = number of levels found (may differ from input value) -| -+---------------------------------------------------------------------*/ - /* local matrix object */ - parms_Operator newOpt; - parms_arms_data data; - parms_vcsr Amat; - parms_Map is; -/*-------------------- function prototyping done in LIB/protos.h */ -/*-------------------- move above to protos.h */ - p4ptr levp=NULL, levc=NULL, levn=NULL, levmat=NULL; - csptr schur=NULL, B=NULL, F=NULL, E=NULL, C=NULL; - ilutptr ilsch=NULL; -/*-------------------- local variables (initialized) */ - double *dd1, *dd2; - double *droptol, tolind; - int nlev, bsize, iout, ierr = 0; - int *ipar, *lfil; - int methL[4], methS[4]; -/*-------------------- local variables (not initialized) */ - int nA, nB, nC, j, n, ilev, symperm, schur_start, nbnd, i; - FILE *ft; -/*-------------------- work arrays: */ - int *iwork, *uwork; -/* timer arrays: */ -/* double *symtime, *unstime, *factime, *tottime;*/ -/*---------------------------BEGIN ARMS-------------------------------*/ -/* schur matrix starts being original A */ - -/*-------------------- begin */ - n = param->n; - nbnd = schur_start = param->schur_start; - is = self->is; - if (schur_start == -1) { - nbnd = is->schur_start; - } - - if (!param->isalloc) { - parms_OperatorCreate(&newOpt); - PARMS_MEMCPY(newOpt->ops, &parms_arms_sol_vptr, 1); - PARMS_NEW(data); - PARMS_NEW(data->levmat); - levmat = data->levmat; - PARMS_NEW(data->ilus); - ilsch = data->ilus; - newOpt->data = data; - *op = newOpt; - } - else { - data = (*op)->data; - } - - Amat = (parms_vcsr)mat; - - /* compute the number of nonzero entries in mat */ - data->nnz_mat = 0; - for (i = 0; i < Amat->n; i++) { - data->nnz_mat += Amat->nnzrow[i]; - } - - /* retrieve data from input objects */ - ipar = param->ipar; - lfil = param->lfil; - droptol = param->droptol; - tolind = param->tolind; - - nlev = ipar[0]; - bsize = ipar[2]; - iout = ipar[3]; - - if (iout > 0 ) { - ft = stdout; - } - else{ - ft = NULL; - } - - ierr = 0; -/*--------------------------------------------------------------------- -| The matrix (a,ja,ia) plays role of Schur compl. from the 0th level. -+--------------------------------------------------------------------*/ - - nC = nA = n = Amat->n; - if (bsize >= n) bsize = n-1; - levmat->n = n; levmat->nB = 0; - schur = Amat; - levc = levmat; - /*--------------------------------------- */ - levc->prev = levc->next = levp = NULL; - levc->n = 0; - - memcpy(methL, &ipar[10], 4*sizeof(int)); - memcpy(methS, &ipar[14], 4*sizeof(int)); - /*--------------------------------------------------------------------- - | The preconditioner construction is divided into two parts: - | 1st part: construct and store multi-level L and U factors; - | 2nd part: construct the ILUT factorization for the coarsest level - +--------------------------------------------------------------------*/ - if ( (iout > 0) && (nlev > 0) ) { - fprintf(ft," \n"); - fprintf(ft,"Level Total Unknowns B-block Coarse set\n"); - fprintf(ft,"===== ============== ======= ==========\n"); - } - /*--------------------------------------------------------------------- - | main loop to construct multi-level LU preconditioner. Loop is on the - | level ilev. nA is the dimension of matrix A_l at each level. - +--------------------------------------------------------------------*/ - for (ilev = 0; ilev < nlev; ilev++) { - /*-------------------- new nA is old nC -- from previous level */ - nA = nC; - if ( nA <= bsize ) goto label1000; - /*-------------------- allocate work space */ - iwork = (int *) Malloc(nA*sizeof(int), "arms2:2.5" ); - symperm = 0; /* 0nly needed in cleanP4 */ - if (ipar[1] == 1) - uwork = (int *) Malloc(nA*sizeof(int), "arms2:2.6" ); - else{ - symperm = 1; - uwork = iwork; - } - /*-------------------- SCALING*/ - dd1 = NULL; - dd2 = NULL; - if (methL[2]) { - dd1 = (double *) Malloc(nA*sizeof(double), "arms2:3" ); - j=roscalC(schur, dd1,1); - if (j) printf("ERROR in roscalC - row %d is a zero row\n",j); - } - - if (methL[3]) { - dd2 = (double *) Malloc(nA*sizeof(double), "arms2:4" ); - j=coscalC(schur, dd2,1); - if (j) printf("ERROR in coscalC - column %d is a zero column\n",j); - } - /*--------------------independent-sets-permutation------------------- - | do reordering -- The matrix and its transpose are used. - +--------------------------------------------------------------------*/ - /* if (SHIFTTOL > 0.0) shiftsD(schur,SHIFTTOL); */ - if (ipar[1] == 1) - PQperm(schur, uwork, iwork, &nB, tolind, nbnd) ; - else - indsetC (schur, bsize, iwork, &nB, tolind, nbnd) ; - /*--------------------------------------------------------------------- - | nB is the total number of nodes in the independent set. - | nC : nA - nB = the size of the reduced system. - +--------------------------------------------------------------------*/ - nC = nA - nB; - nbnd -= nB; - /* if the size of B or C is zero , exit the main loop */ - /* printf (" nB %d nC %d \n",nB, nC); */ - if ( nB == 0 || nC == 0 ) goto label1000; - /*--------------------------------------------------------------------- - | The matrix for the current level is in (schur). - | The permutations arrays are in iwork and uwork (row). - | The routines rpermC, cpermC permute the matrix in place. - *-----------------------------------------------------------------------*/ - /* DEBUG : SHOULD THIS GO BEFORE GOTO LABEL1000 ?? */ - rpermC(schur,uwork); - cpermC(schur,iwork); - /* prtC(schur, ilev) ; print matrix - debugging */ - /*----------------------------------------------------------------------- - | If this is the first level, the permuted matrix is stored in - | (levc) = (levmat). Otherwise, the next level is created in (levc). - +--------------------------------------------------------------------*/ - if (ilev > 0) { - /*- delete C matrix of any level except last one (no longer needed) */ - cleanCS(C); - levn = (p4ptr) Malloc(sizeof(Per4Mat), "arms2:6" ); - /* levc->prev = levp; */ - levc->next = levn; - levp = levc; - levc = levn; - levc->prev = levp; - } - /*-------------------- p4ptr struct for current schur complement */ - B = (csptr) Malloc(sizeof(SparMat), "arms2:7" ); - E = (csptr) Malloc(sizeof(SparMat), "arms2:8" ); - F = (csptr) Malloc(sizeof(SparMat), "arms2:9" ); - C = (csptr) Malloc(sizeof(SparMat), "arms2:10" ); - csSplit4(schur, nB, nC, B, F, E, C); - setupP4(levc, nB, nC, F, E); - /*-------------------- copy a few pointers ---- */ - levc->perm = iwork; - levc->rperm = uwork; - levc->symperm = symperm; - levc->D1=dd1; - levc->D2=dd2; - /*--------------------------------------------------------------------- - | a copy of the matrix (schur) has been permuted. Now perform the - | block factorization: - | - | | B F | | L 0 | | U L^-1 F | - | | | = | | X | | = L x U - | | E C | | E U^-1 I | | 0 A1 | - | - | The factors E U^-1 and L^-1 F are discarded after the factorization. - | - +--------------------------------------------------------------------*/ - if (iout > 0) - fprintf(ft,"%3d %13d %13d %10d\n", ilev+1,nA,nB,nC); - /*--------------------------------------------------------------------- - | PILUT constructs one level of the block ILU fact. The permuted matrix - | is in (levc). The L and U factors will be stored in the p4mat struct. - | destroy current Schur complement - no longer needed - and set-up new - | one for next level... - +--------------------------------------------------------------------*/ - cleanCS(schur); - schur = (csptr) Malloc(sizeof(SparMat), "arms2:11" ); - setupCS(schur, nC,1); - /*---------------------------------------------------------------------- - | calling PILU to construct this level block factorization - | ! core dump in extreme case of empty matrices. - +----------------------------------------------------------------------*/ - ierr = pilu(levc, B, C, droptol, lfil, schur) ; - /* prtC(levc->L, ilev) ; */ - if (ierr) { - fprintf(ft," ERROR IN PILU -- IERR = %d\n", ierr); - return(1); - } - cleanCS(B); - } - /*--------------------------------------------------------------------- - | done with the reduction. Record the number of levels in ipar[0] - |********************************************************************** - +--------------------------------------------------------------------*/ - label1000: - /* printf (" nnz_Schur %d \n",cs_nnz (schur)); */ - levc->next = NULL; - ipar[0] = ilev; - data->nlev = ilev; - data->n = n; - nC = schur->n; - setupILUT(ilsch,nC); - /*--------------------------------------------------------------------*/ - /* define C-matrix (member of ilsch) to be last C matrix */ - if (ilev > 0) ilsch->C=C; - /*-------------------- for ilut fact of schur matrix */ - /* SCALING */ - - ilsch->D1 = NULL; - if (methS[2]) { - ilsch->D1 = (double *) Malloc(nC*sizeof(double), "arms2:iluschD1" ); - j=roscalC(schur, ilsch->D1, 1); - if (j) printf("ERROR in roscalC - row %d is a zero row\n",j); - } - - ilsch->D2 = NULL; - if (methS[3]) { - ilsch->D2 = (double *) Malloc(nC*sizeof(double), "arms2:iluschD1" ); - j =coscalC(schur, ilsch->D2, 1); - if (j) printf("ERROR in coscalC - column %d is a zero column\n",j); - } - - /*--------------------------------------------------------------------- - | get ILUT factorization for the last reduced system. - +--------------------------------------------------------------------*/ - uwork = NULL; - iwork = NULL; - if (methS[0]) { - iwork = (int *) Malloc(nC*sizeof(int), "arms2:3" ); - uwork = (int *) Malloc(nC*sizeof(int), "arms2:3.5" ); - tolind = 0.0; - PQperm(schur, uwork, iwork, &nB, tolind, nbnd) ; - rpermC(schur,uwork); - cpermC(schur,iwork); - } - ilsch->rperm = uwork; - ilsch->perm = iwork; - - /* printf(" lf : %d %d %d %d %d %d %d \n",lfil[0], - lfil[1], lfil[2], lfil[3], lfil[4], lfil[5], lfil[6]) ; */ - - ilsch->perm2 = NULL; - - if (methS[1] == 0) - ierr = ilutD(schur, droptol, lfil, ilsch); - else { - ilsch->perm2 = (int *) Malloc(nC*sizeof(int), "arms2:ilutpC" ); - for (j=0; jperm2[j] = j; - ierr = ilutpC(schur, droptol, lfil, PERMTOL, nC, ilsch); - } - /*---------- OPTIMIZATRION: NEED TO COMPOUND THE TWO - RIGHT PERMUTATIONS -- CHANGES HERE AND IN - USCHUR SOLVE == compound permutations */ - if (ierr) { - fprintf(ft," ERROR IN ILUT -- IERR = %d\n", ierr); - return(1); - } - /* Last Schur complement no longer needed */ - cleanCS(schur); - data->nnz_prec = nnz_arms(data); - data->ind = n - ilsch->n; - if (ilev) { - data->schur_start = n - ilsch->n; - } - else { - is = self->is; - data->schur_start = is->schur_start; - } - - PARMS_MEMCPY(data->ipar, param->ipar, 18); - PARMS_MEMCPY(data->pgfpar, param->pgfpar, 2); - return 0; -}/*-----end-of-ARMS2---------------------------------------------------- - +--------------------------------------------------------------------*/ - diff --git a/lib/parms/src/DDPQ/globheads.h b/lib/parms/src/DDPQ/globheads.h deleted file mode 100755 index cd334880f..000000000 --- a/lib/parms/src/DDPQ/globheads.h +++ /dev/null @@ -1,151 +0,0 @@ -#ifndef __VBLOCK_HEADER_H__ -#define __VBLOCK_HEADER_H__ - -#if defined(C99) -#include -#else -#include -#endif -#include "parms_mat_impl.h" -#include "parms_opt_impl.h" - -#define MAX_BLOCK_SIZE 100 - -/* FORTRAN style vblock format, compatible for many FORTRAN routines */ -#define DATA(a,row,i,j) (a[(j)*(row)+(i)]) - -/* the dimension of ith Block */ -#define B_DIM(bs,i) (bs[i+1]-bs[i]) - -#if 0 -typedef struct SparRow { -/*--------------------------------------------- -| C-style CSR format - used internally -| for all matrices in CSR format -|---------------------------------------------*/ - int n; - int *nnzrow; /* length of each row */ - int **ja; /* pointer-to-pointer to store column indices */ - double **ma; /* pointer-to-pointer to store nonzero entries */ -} SparMat, *csptr; -#endif - -typedef struct parms_vcsr SparMat; -typedef parms_vcsr csptr; - -typedef struct ILUfac { - int n; - csptr L; /* L part elements */ - FLOAT *D; /* diagonal elements */ - csptr U; /* U part elements */ - int *work; /* working buffer */ -} ILUSpar, LDUmat, *iluptr; - - -typedef struct PerMat4 *p4ptr; -typedef struct PerMat4 { -/*------------------------------------------------------------ -| struct for storing the block LU factorization -| contains all the block factors except the -| data related to the last block. -| n = size of current block -| symperm = whether or not permutations are symmetric. -| used only in cleanP4.. -| nB = size of B-block -| L, U = ILU factors of B-block -| F, E = sparse matrices in (1,2) and (2,1) -| parts of matrix. -| perm = (symmetric) permutation used in factorization -| comes from the independent set ordering -| rperm = unsymmetric permutation (rows) not used in this -| version -- but left here for compatibility.. -| D1, D2 = diagonal matrices (left, right) used for scaling -| if scaling option is turned on. Note that the -| method works by scaling the whole matrix first -| (at any level) before anything else is done. -| wk = a work vector of length n needed for various tasks -| [reduces number of calls to malloc] -|----------------------------------------------------------*/ - int n; - int nB; - int symperm; -/* LU factors */ - csptr L; - csptr U; -/* E, F blocks */ - csptr E; - csptr F; - int *rperm; /* row permutation */ - int *perm; /* col. permutation */ - double *D1 ; /* diagonal scaling row */ - double *D2 ; /* diagonal scaling columns*/ - FLOAT *wk; /* work array */ -/* pointer to next and previous struct */ - p4ptr prev; - p4ptr next; -} Per4Mat; -/* -------------------------------------------------------------------*/ -typedef struct ILUTfac *ilutptr; -typedef struct ILUTfac { -/*------------------------------------------------------------ -| struct for storing data related to the last schur complement -| we need to store the C matrix associated with the last block -| and the ILUT factorization of the related Schur complement. -| -| n = size of C block = size of Schur complement -| C = C block of last level matrix. -| L, U = ILU factors of last schur complement. -| -| meth[4] = parameters for defining variants in factorization -| - see function readin for details -| rperm = row permutation used for very nonsymmetric matrices -| [such as bottleneck transversal] -- NOT IN THIS VERSION -| perm2 = unsymmetric permutation (columns) - used primarily -| for the ILUTP version of ILUT/.. -| D1, D2 = diagonal matrices (left, right) used for scaling -| if scaling option is turned on. Note that the -| method works by scaling the whole matrix first -| (at any level) before anything else is done. -| wk = a work vector of length n needed for various tasks -| [reduces number of calls to malloc] -|-----------------------------------------------------------*/ - int n; - /*-------------------- C matrix of last block */ - csptr C; - /* LU factorization */ - csptr L; - csptr U; - /*-------------------- related to variants and methods */ - /* int meth[4]; */ - int *rperm; /* row single-sinded permutation */ - int *perm; /* column perm . */ - int *perm2; /* column permutation coming from pivoting in ILU */ - double *D1; - double *D2; - FLOAT *wk; -} IluSpar; - -typedef struct parms_arms_data { - int n; /* dimension of matrix */ - int nlev; /* number of levels */ - ilutptr ilus; - p4ptr levmat; - int ipar[18]; - double pgfpar[2]; - int schur_start; - int ind; - int nnz_mat; - int nnz_prec; -} *parms_arms_data; - -typedef struct parms_arms_data armsMat; -typedef parms_arms_data arms; - -typedef struct __CompressType -{ - int grp; /* -1: begin new group, >=0: belong to grp-th row */ - int count; /* block size, valid only if grp = -1 */ -} CompressType; - -#endif /* __VBLOCK_HEADER_H__ */ - diff --git a/lib/parms/src/DDPQ/ilutpC.c b/lib/parms/src/DDPQ/ilutpC.c deleted file mode 100755 index d158168d8..000000000 --- a/lib/parms/src/DDPQ/ilutpC.c +++ /dev/null @@ -1,753 +0,0 @@ -#include -#include -#include -#if defined(C99) -#include -#else -#include -#endif -#include "protos.h" - -#define DBL_EPSILON 2.2204460492503131e-16 // double epsilon - -int ilutpC(csptr amat, double *droptol, int *lfil, double permtol, - int mband, ilutptr ilusch) -{ -/*---------------------------------------------------------------------- -| ILUTP -- ILUT with column pivoting -- adapted from ILUTP [Sparskit] -| Converted to C so that dynamic memory allocation may be implememted. -| All indexing is in C format. -|---------------------------------------------------------------------- -| ILUT factorization with dual truncation. -|---------------------------------------------------------------------- -| -| on entry: -|========== -| ( amat ) = Matrix stored in SparRow struct. -| -| lfil[5] = number nonzeros in L-part -| lfil[6] = number nonzeros in U-part ( lfil >= 0 ) -| -| droptol[5] = threshold for dropping small terms in L during -| factorization. -| droptol[6] = threshold for dropping small terms in U. -| -| permtol = tolerance ratio used to determine whether or not to permute -| two columns. At step i columns i and j are permuted when -| -| abs(a(i,j))*permtol > abs(a(i,i)) -| -| [0 --> never permute; good values 0.1 to 0.01] -| -| mband = permuting is done within a band extending to mband -| diagonals only. -| mband = 0 --> no pivoting. -| mband = n --> pivot is searched in whole column -| -| -| On return: -|=========== -| -| (ilusch) = Contains L and U factors in an LUfact struct. -| Individual matrices stored in SparRow structs. -| On return matrices have C (0) indexing. -| -| iperm = reverse permutation array. -| -| integer value returned: -| -| 0 --> successful return. -| 1 --> Error. Input matrix may be wrong. (The -| elimination process has generated a -| row in L or U whose length is > n.) -| 2 --> Memory allocation error. -| 5 --> Illegal value for lfil. -| 6 --> zero row encountered. -|----------------------------------------------------------------------- -| work arrays: -|============= -| jw, jwrev = integer work arrays of length n -| w = real work array of length n. -|----------------------------------------------------------------------- -| All processing is done using C indexing. -|--------------------------------------------------------------------*/ - int i, ii, j, jj, jcol, jpos, jrow, k, *jw=NULL, *jwrev=NULL; - int len, lenu, lenl, rmax, *iprev=NULL, fil5=lfil[5], fil6=lfil[6]; - double tnorm, drop5=droptol[5], drop6=droptol[6]; - int nnzrow, rowz, *rowj=NULL, imax, icut, *iperm=ilusch->perm2; - double xmax, xmax0, t1; - FLOAT *rowm=NULL, t, s, fact, *w=NULL, tmp; -#if defined(DBL_CMPLX) - double shf, ti, sgny; - int nnzS = 0; - -/*----------get nnz of amat ---------*/ - for(j = 0; jn; j++) - nnzS += amat->nnzrow[j]; -#endif - - int dec, decnt=0; - -#ifdef ILUTIME - int tt, t22=22, t23=23, t24=24, t25=25, t26=26, t27=27, t28=28, - t29=29, t30=30, t31=31, t32=32, t33=33, t34=34, t35=35; - for (tt=22; tt<36; tt++) - msg_timer_clear(&tt); -#endif - ilusch->n = rmax = amat->n; - if (rmax == 0) return(0); - if (rmax > 0) { - jw = (int *) Malloc(rmax*sizeof(int), "ilutpC:1" ); - w = (FLOAT *) Malloc(rmax*sizeof(FLOAT), "ilutpC:2" ); - jwrev = (int *) Malloc(rmax*sizeof(int), "ilutpC:3" ); - iprev = (int *) Malloc(rmax*sizeof(int), "ilutpC:4" ); - } - if (fil5<0 || fil6<0 || rmax<=0) goto label998; -/*--------------------------------------------------------------------- -| beginning of main loop - L, U calculations -|--------------------------------------------------------------------*/ - for (j=0; jpj[ii]; - rowm = amat->pa[ii]; - rowz = amat->nnzrow[ii]; - nnzrow = rowz; - tnorm = 0.0; - for (k=0; k DBL_EPSILON) goto label40; - goto label9991; - -#ifdef ILUTIME -msg_timer_stop(&t22); -msg_timer_start(&t23); -#endif -/*--------------------------------------------------------------------- -| unpack amat in arrays w, jw, jwrev -| WE ASSUME THERE IS A DIAGONAL ELEMENT -|--------------------------------------------------------------------*/ -label40: - lenu = 1; - lenl = 0; - w[ii] = 0.0; - jw[ii] = ii; - jwrev[ii] = ii; - for (j=0; jU->pa[jrow]; - fact = w[jj] * rowm[0]; - if ( ABS_VALUE(fact) > drop5 ) { /* DROPPING IN L */ -#ifdef ILUTIME -msg_timer_start(&t25); -#endif - rowj = ilusch->U->pj[jrow]; - rowz = ilusch->U->nnzrow[jrow]; -/*--------------------------------------------------------------------- -| combine current row and row jrow -|--------------------------------------------------------------------*/ - for (k=1; k= ii) { -/*--------------------------------------------------------------------- -| this is a fill-in element -|--------------------------------------------------------------------*/ - if (jpos == -1) { - if (lenu > rmax) goto label994; - i = ii + lenu; - jw[i] = j; - jwrev[j] = i; - w[i] = - s; - lenu++; - } -/*--------------------------------------------------------------------- -| this is not a fill-in element -|--------------------------------------------------------------------*/ - else - w[jpos] -= s; - } -/*--------------------------------------------------------------------- -| dealing with L -|--------------------------------------------------------------------*/ - else { -/*--------------------------------------------------------------------- -| this is a fill-in element -|--------------------------------------------------------------------*/ - if (jpos == -1) { - if (lenl > rmax) goto label994; - jw[lenl] = j; - jwrev[j] = lenl; - w[lenl] = - s; - lenl++; - } -/*--------------------------------------------------------------------- -| this is not a fill-in element -|--------------------------------------------------------------------*/ - else - w[jpos] -= s; - } - } -/*--------------------------------------------------------------------- -| store this pivot element -|--------------------------------------------------------------------*/ -#ifdef ILUTIME -msg_timer_stop(&t25); -#endif - w[len] = fact; - jw[len] = jrow; - len++; - } - } -/*--------------------------------------------------------------------- -| reset nonzero indicators -|--------------------------------------------------------------------*/ -#ifdef ILUTIME -msg_timer_start(&t26); -#endif - for (j=0; j fil5 ? fil5 : lenl; - ilusch->L->nnzrow[ii] = len; - if (lenl > len) - qsplitCF(w, jw, lenl, len); -/* - printf(" row %d length of L = %d",ii,len); -*/ - if (len > 0) { - ilusch->L->pj[ii] = (int *) Malloc(len*sizeof(int), "ilutpC:5" ); - ilusch->L->pa[ii] = (FLOAT *) Malloc(len*sizeof(FLOAT), "ilutpC:6"); - memcpy(ilusch->L->pa[ii], w, len*sizeof(FLOAT)); - for (j=0; jL->pj[ii][j] = iperm[jw[j]]; - } -/*--------------------------------------------------------------------- -| apply dropping strategy to U (entries after diagonal) -|--------------------------------------------------------------------*/ -#ifdef ILUTIME -msg_timer_stop(&t27); -msg_timer_start(&t28); -#endif - len = 0; - for (j=1; j drop6*tnorm ) { - len++; - w[ii+len] = w[ii+j]; - jw[ii+len] = jw[ii+j]; - } - } - lenu = len+1; - len = lenu > fil6 ? fil6 : lenu; - ilusch->U->nnzrow[ii] = len; - if (lenu > len+1) - qsplitCF(&w[ii+1], &jw[ii+1], lenu-1, len); - ilusch->U->pa[ii] = (FLOAT *) Malloc(len*sizeof(FLOAT), "ilutpC:7" ); - ilusch->U->pj[ii] = (int *) Malloc(len*sizeof(int), "ilutpC:8" ); -/*--------------------------------------------------------------------- -| determine next pivot -|--------------------------------------------------------------------*/ -/* HERE - all lines with dec included for counting pivots */ - imax = ii; - xmax = ABS_VALUE(w[imax]); - xmax0 = xmax; - /* icut = ii - 1 + mband - ii % mband; */ - icut = ii-1 + mband; - dec = 0; - for (k=ii+1; k xmax) && (t1*permtol > xmax0) && (jw[k] <= icut) ) { - imax = k; - xmax = t1; - dec = 1; - } - } - if (dec == 1) decnt++; -/*--------------------------------------------------------------------- -| exchange w's -|--------------------------------------------------------------------*/ - tmp = w[ii]; - w[ii] = w[imax]; - w[imax] = tmp; -/*--------------------------------------------------------------------- -| update iperm and reverse iperm -|--------------------------------------------------------------------*/ - j = jw[imax]; - i = iperm[ii]; - iperm[ii] = iperm[j]; - iperm[j] = i; - iprev[iperm[ii]] = ii; - iprev[iperm[j]] = j; -/*--------------------------------------------------------------------- -| now store U in original coordinates -|--------------------------------------------------------------------*/ - - if(ABS_VALUE(w[ii]) <= DBL_EPSILON) w[ii] = tnorm;//(0.0001+drop6)*nnzrow*tnorm; - -#if defined(DBL_CMPLX) -/*---------- Add a complex shift for complex case ------------ */ - -/*------ shift based on droptol -- tau-based shift ----*/ - shf = drop6*tnorm*nnzrow; // recall that tnorm = rownorm / nnzrow -/* ----- shift based on diagonal dominance gap -- dd-based shift -----*/ -// shf = rmax*(tnorm*nnzrow - 2*cabs(w[ii]))/(double)nnzS; //rownorm includes diagonal - ti = cimag(w[ii]) ; - if (ti<=0) - sgny = -ti - sqrt(ti*ti+shf*shf) ; - else - sgny = -ti + sqrt(ti*ti+shf*shf) ; - - w[ii] = w[ii] + sgny*I; -#endif - - ilusch->U->pa[ii][0] = 1.0 / w[ii]; - ilusch->U->pj[ii][0] = ii; - memcpy(&ilusch->U->pa[ii][1], &w[ii+1], (len-1)*sizeof(FLOAT)); - lenu = 0; - for (k=ii+1; kU->pj[ii][++lenu] = iperm[jw[k]]; -#ifdef ILUTIME -msg_timer_stop(&t28); -#endif - } - cpermC(ilusch->U, iprev); - cpermC(ilusch->L, iprev); -/* DO NOT PERMUTE ORIGINAL MATRIX - cpermC(amat, iprev); -*/ -#ifdef ILUTIME - tnorm = 0; - for (tt=22; tt<29; tt++) { - printf(" loop %d %e \n",tt,msg_timer(&tt)); - tnorm += msg_timer(&tt); - } - printf(" total %e \n",tnorm); -#endif -/*--------------------------------------------------------------------- -| end main loop - now do clean up -|--------------------------------------------------------------------*/ - if (rmax > 0) { - free(jw); - free(w); - free(jwrev); - free(iprev); - } -/* printf("There were %d pivots\n",decnt); */ -/*--------------------------------------------------------------------- -| done -- correct return -|--------------------------------------------------------------------*/ - return(0); -label994: -/* Incomprehensible error. Matrix must be wrong. */ - return(1); -/* label997: - Memory allocation error. - return(2); */ -label998: -/* illegal value for lfil or last entered */ - return(5); -label9991: -/* zero row encountered */ - return(6); -} -/*--------------------------------------------------------------------- -| end of ilutpC -|--------------------------------------------------------------------*/ - -int ilutD(csptr amat, double *droptol, int *lfil, ilutptr ilusch) -{ -/*---------------------------------------------------------------------- -| ILUT - -| Converted to C so that dynamic memory allocation may be implememted. -| All indexing is in C format. -|---------------------------------------------------------------------- -| ILUT factorization with dual truncation. -| -| March 1, 2000 - dropping in U: keep entries > tau * diagonal entry -|---------------------------------------------------------------------- -| -| on entry: -|========== -| ( amat ) = Matrix stored in SparRow struct. -| (ilusch) = Pointer to ILUTfac struct -| -| lfil[5] = number nonzeros in L-part -| lfil[6] = number nonzeros in U-part ( lfil >= 0 ) -| -| droptol[5] = threshold for dropping small terms in L during -| factorization. -| droptol[6] = threshold for dropping small terms in U. -| -| On return: -|=========== -| -| (ilusch) = Contains L and U factors in an LUfact struct. -| Individual matrices stored in SparRow structs. -| On return matrices have C (0) indexing. -| -| integer value returned: -| -| 0 --> successful return. -| 1 --> Error. Input matrix may be wrong. (The -| elimination process has generated a -| row in L or U whose length is > n.) -| 2 --> Memory allocation error. -| 5 --> Illegal value for lfil or last. -| 6 --> zero row encountered. -|----------------------------------------------------------------------- -| work arrays: -|============= -| jw, jwrev = integer work arrays of length n -| w = real work array of length n. -|----------------------------------------------------------------------- -| All processing is done using C indexing. -|--------------------------------------------------------------------*/ - int i, ii, j,jj, jcol, jpos, jrow, k, *jw=NULL, *jwrev=NULL; - int len, lenu, lenl, rmax, fil5=lfil[5], fil6=lfil[6]; - double tnorm, drop5=droptol[5], drop6=droptol[6]; - int rowz, *rowj=NULL; - FLOAT t, s, fact, *w=NULL, *rowm = NULL; -#if defined(DBL_CMPLX) - double shf, ti, sgny; - int nnzS = 0; - -/*----------get nnz of amat ---------*/ - for(j = 0; jn; j++) - nnzS += amat->nnzrow[j]; -#endif - - ilusch->n = rmax = amat->n; - if (rmax == 0) return(0); - if (rmax > 0) { - jw = (int *) Malloc(rmax*sizeof(int), "ilutD:1" ); - w = (FLOAT *) Malloc(rmax*sizeof(FLOAT), "ilutD:2" ); - jwrev = (int *) Malloc(rmax*sizeof(int), "ilutD:3" ); - } - if (fil5<0 || fil6<0 || rmax<=0) goto label9995; -/*--------------------------------------------------------------------- -| beginning of first main loop - L, U, L^{-1}F calculations -|--------------------------------------------------------------------*/ - for (j=0; jpj[ii]; - rowm = amat->pa[ii]; - rowz = amat->nnzrow[ii]; - for (k=0; k DBL_EPSILON) goto label40; - goto label9996; -/*--------------------------------------------------------------------- -| unpack amat in arrays w, jw, jwrev -| WE ASSUME THERE IS A DIAGONAL ELEMENT -|--------------------------------------------------------------------*/ -label40: - lenu = 1; - lenl = 0; - w[ii] = 0.0; - jw[ii] = ii; - jwrev[ii] = ii; - tnorm = 0.0; - for (j=0; jU->pa[jrow]; - fact = w[jj] * rowm[0]; - if ( ABS_VALUE(fact) > drop5 ) { /* DROPPING IN L */ - rowj = ilusch->U->pj[jrow]; - rowz = ilusch->U->nnzrow[jrow]; -/*--------------------------------------------------------------------- -| combine current row and row jrow -|--------------------------------------------------------------------*/ - for (k=1; k= ii) { -/*--------------------------------------------------------------------- -| this is a fill-in element -|--------------------------------------------------------------------*/ - if (jpos == -1) { - if (lenu > rmax) goto label9991; - i = ii + lenu; - jw[i] = j; - jwrev[j] = i; - w[i] = - s; - lenu++; - } -/*--------------------------------------------------------------------- -| this is not a fill-in element -|--------------------------------------------------------------------*/ - else - w[jpos] -= s; - } -/*--------------------------------------------------------------------- -| dealing with L -|--------------------------------------------------------------------*/ - else { -/*--------------------------------------------------------------------- -| this is a fill-in element -|--------------------------------------------------------------------*/ - if (jpos == -1) { - if (lenl > rmax) goto label9991; - jw[lenl] = j; - jwrev[j] = lenl; - w[lenl] = - s; - lenl++; - } -/*--------------------------------------------------------------------- -| this is not a fill-in element -|--------------------------------------------------------------------*/ - else - w[jpos] -= s; - } - } -/*--------------------------------------------------------------------- -| store this pivot element -|--------------------------------------------------------------------*/ - w[len] = fact; - jw[len] = jrow; - len++; - } - } -/*--------------------------------------------------------------------- -| reset nonzero indicators -|--------------------------------------------------------------------*/ - for (j=0; j fil5 ? fil5 : len; - ilusch->L->nnzrow[ii] = lenl; - if (len > lenl) - qsplitCF(w, jw, len, lenl); - if (len > 0) { - ilusch->L->pj[ii] = (int *) Malloc(lenl*sizeof(int), "ilutD:4" ); - ilusch->L->pa[ii] = (FLOAT *) Malloc(lenl*sizeof(FLOAT), "ilutD:5"); - memcpy(ilusch->L->pj[ii], jw, lenl*sizeof(int)); - memcpy(ilusch->L->pa[ii], w, lenl*sizeof(FLOAT)); - } -/*--------------------------------------------------------------------- -| store the diagonal element of U -| -| dropping in U if size is less than drop1 * diagonal entry -|--------------------------------------------------------------------*/ - t = w[ii]; - len = 0; - for (j=1; j drop6*tnorm ) { - w[len] = w[ii+j]; - jw[len] = jw[ii+j]; - len++; - } - } - lenu = len+1 > fil6 ? fil6 : len+1; - ilusch->U->nnzrow[ii] = lenu; - jpos = lenu-1; - if (len > jpos) - qsplitCF(w, jw, len, jpos); - ilusch->U->pa[ii] = (FLOAT *) Malloc(lenu*sizeof(FLOAT), "ilutD:6" ); - ilusch->U->pj[ii] = (int *) Malloc(lenu*sizeof(int), "ilutD:7" ); - if(ABS_VALUE(t) <= DBL_EPSILON) t= tnorm; - ilusch->U->pa[ii][0] = 1.0 / t; - ilusch->U->pj[ii][0] = ii; -/*--------------------------------------------------------------------- -| copy the rest of U -|--------------------------------------------------------------------*/ - memcpy(&ilusch->U->pj[ii][1], jw, jpos*sizeof(int)); - memcpy(&ilusch->U->pa[ii][1], w, jpos*sizeof(FLOAT)); - } -/*--------------------------------------------------------------------- -| end main loop - now do clean up -|--------------------------------------------------------------------*/ - if (rmax > 0) { - free(jw); - free(w); - free(jwrev); - } -/*--------------------------------------------------------------------- -| done -- correct return -|--------------------------------------------------------------------*/ - return(0); -label9991: -/* Incomprehensible error. Matrix must be wrong. */ - return(1); -/* label9992: - Memory allocation error. - return(2); */ -label9995: -/* illegal value for lfil or last entered */ - return(5); -label9996: -/* zero row encountered */ - return(6); -} -/*--------------------------------------------------------------------- -| end of ilutNEW -|--------------------------------------------------------------------*/ diff --git a/lib/parms/src/DDPQ/misc.c b/lib/parms/src/DDPQ/misc.c deleted file mode 100755 index 955af1f98..000000000 --- a/lib/parms/src/DDPQ/misc.c +++ /dev/null @@ -1,485 +0,0 @@ -#include -#include -#include -#if defined(C99) -#include -#else -#include -#endif -#include "protos.h" - -#define DBL_EPSILON 2.2204460492503131e-16 // double epsilon - -int SparTran(csptr amat, csptr bmat, int job, int flag) -{ -/*---------------------------------------------------------------------- -| Finds the transpose of a matrix stored in SparRow format. -| -|----------------------------------------------------------------------- -| on entry: -|---------- -| (amat) = a matrix stored in SparRow format. -| -| job = integer to indicate whether to fill the values (job.eq.1) -| of the matrix (bmat) or only the pattern. -| -| flag = integer to indicate whether the matrix has been filled -| 0 - no filled -| 1 - filled -| -| on return: -| ---------- -| (bmat) = the transpose of (mata) stored in SparRow format. -| -| integer value returned: -| 0 --> successful return. -| 1 --> memory allocation error. -|---------------------------------------------------------------------*/ - int i, j, *ind, pos, size=amat->n, *aja; - FLOAT *ama=NULL; - ind = (int *) Malloc(size*sizeof(int), "SparTran:1" ); - for (i=0; ipj[i]; - for (j=0; jnnzrow[i]; j++) - ind[aja[j]]++; - } -/*-------------------- allocate space */ - for (i=0; ipj[i] = (int *) Malloc(ind[i]*sizeof(int), "SparTran:2" ); - bmat->nnzrow[i] = ind[i]; - if (job == 1) { - bmat->pa[i] = (FLOAT *) Malloc(ind[i]*sizeof(FLOAT), "SparTran:3" ); - } - ind[i] = 0; - } - } -/*-------------------- now do the actual copying */ - for (i=0; ipj[i]; - if (job == 1) - ama = amat->pa[i]; - for (j=0; jnnzrow[i]; j++) { - pos = aja[j]; - bmat->pj[pos][ind[pos]] = i; - if (job == 1) - bmat->pa[pos][ind[pos]] = ama[j]; - ind[pos]++; - } - } - free(ind); - return 0; -} -/*-------------- end of SparTran --------------------------------------- -|---------------------------------------------------------------------*/ - -void swapj(int v[], int i, int j) -{ - int temp; - temp = v[i]; - v[i] = v[j]; - v[j] = temp; -} -void swapm(FLOAT v[], int i, int j) -{ - FLOAT temp; - temp = v[i]; - v[i] = v[j]; - v[j] = temp; -} - -void dswapm(double v[], int i, int j) -{ - double temp; - - temp = v[i]; - v[i] = v[j]; - v[j] = temp; -} - -int roscalC(csptr mata, double *diag, int nrm) -{ -/*--------------------------------------------------------------------- -| -| This routine scales each row of mata so that the norm is 1. -| -|---------------------------------------------------------------------- -| on entry: -| mata = the matrix (in SparRow form) -| nrm = type of norm -| 0 (\infty), 1 or 2 -| -| on return -| diag = diag[j] = 1/norm(row[j]) -| -| 0 --> normal return -| j --> row j is a zero row -|--------------------------------------------------------------------*/ -/* local variables */ - int i, k; - FLOAT *kr; - double scal; - - for (i=0; in; i++) { - scal = 0.0; - kr = mata->pa[i]; - if (nrm == 0) { - for (k=0; knnzrow[i]; k++) - if (ABS_VALUE(kr[k]) > fabs(scal)) scal = ABS_VALUE(kr[k]); - } - else if (nrm == 1) { - for (k=0; knnzrow[i]; k++) - scal += ABS_VALUE(kr[k]); - } - else { /* nrm = 2 */ - for (k=0; knnzrow[i]; k++) - scal += ABS_VALUE(kr[k]*kr[k]); - } - if (nrm == 2) scal = sqrt(scal); - if(ABS_VALUE(scal-DBL_EPSILON) <= DBL_EPSILON*ABS_VALUE(scal)){ - scal = 1.0; - /* YS. return i+1; */ - } - else - scal = 1.0 / scal; - diag[i] = scal; - for (k=0; knnzrow[i]; k++) - kr[k] = kr[k] * scal; - } - return 0; -} -/*---------------end of roscalC----------------------------------------- -----------------------------------------------------------------------*/ -int coscalC(csptr mata, double *diag, int nrm) -{ -/*--------------------------------------------------------------------- -| -| This routine scales each column of mata so that the norm is 1. -| -|---------------------------------------------------------------------- -| on entry: -| mata = the matrix (in SparRow form) -| nrm = type of norm -| 0 (\infty), 1 or 2 -| -| on return -| diag = diag[j] = 1/norm(row[j]) -| -| 0 --> normal return -| j --> column j is a zero column -|--------------------------------------------------------------------*/ -/* local variables */ - int i, j, k; - FLOAT *kr; - int *ki; - - for (i=0; in; i++) - diag[i] = 0.0; -/*--------------------------------------- -| compute the norm of each column -|--------------------------------------*/ - for (i=0; in; i++) { - kr = mata->pa[i]; - ki = mata->pj[i]; - if (nrm == 0) { - for (k=0; knnzrow[i]; k++) { - j = ki[k]; - if (ABS_VALUE(kr[k]) > diag[j]) diag[j] = ABS_VALUE(kr[k]); - } - } - else if (nrm == 1) { - for (k=0; knnzrow[i]; k++) - diag[ki[k]] += ABS_VALUE(kr[k]); - } - else { /* nrm = 2 */ - for (k=0; knnzrow[i]; k++) - diag[ki[k]] += ABS_VALUE(kr[k]*kr[k]); - } - } - if (nrm == 2) { - for (i=0; in; i++) - diag[i] = sqrt(diag[i]); - } -/*--------------------------------------- -| invert -|--------------------------------------*/ - for (i=0; in; i++) { - if(ABS_VALUE(diag[i]-DBL_EPSILON) <= DBL_EPSILON*ABS_VALUE(diag[i])) - /* return i+1;*/ - diag[i] = 1.0; - else - diag[i] = 1.0 / diag[i]; - } -/*--------------------------------------- -| C = A * D -|--------------------------------------*/ - for (i=0; in; i++) { - kr = mata->pa[i]; - ki = mata->pj[i]; - for (k=0; knnzrow[i]; k++) - kr[k] = kr[k] * diag[ki[k]]; - } - return 0; -} -/*---------------end of coscalC----------------------------------------- -----------------------------------------------------------------------*/ -void dscale(int n, double *dd, FLOAT *x, FLOAT * y) -{ -/* Computes y == DD * x */ -/* scales the vector x by the diagonal dd - output in y */ - int k; - - for (k=0; k= right) return; - if (abval) { - swapj(ja, left, (left+right)/2); - swapm(ma, left, (left+right)/2); - last = left; - for (i=left+1; i<=right; i++) { - if (ABS_VALUE(ma[i]) > ABS_VALUE(ma[left])) { - swapj(ja, ++last, i); - swapm(ma, last, i); - } - } - swapj(ja, left, last); - swapm(ma, left, last); - qsortC(ja, ma, left, last-1, abval); - qsortC(ja, ma, last+1, right, abval); - } - else { - swapj(ja, left, (left+right)/2); - swapm(ma, left, (left+right)/2); - last = left; - for (i=left+1; i<=right; i++) { - if (ABS_VALUE(ma[i]) > ABS_VALUE(ma[left])) { - swapj(ja, ++last, i); - swapm(ma, last, i); - } - } - swapj(ja, left, last); - swapm(ma, left, last); - qsortC(ja, ma, left, last-1, abval); - qsortC(ja, ma, last+1, right, abval); - } -} - -void printmat(FILE *ft, csptr A, int i0, int i1){ -/*-------------------------------------------------------------+ -| to dump rows i0 to i1 of matrix for debugging purposes | -|--------------------------------------------------------------*/ - int i, k, nzi; - int *row; - FLOAT *rowm; - for (i=i0; innzrow[i]; - row = A->pj[i]; - rowm = A->pa[i]; -#if defined(DBL_CMPLX) - for (k=0; k< nzi; k++){ - fprintf(ft," row %d a (%e, %e) ja %d \n", i+1, creal(rowm[k]), cimag(rowm[k]), row[k]+1); - } -#else - for (k=0; k< nzi; k++){ - fprintf(ft," row %d a %e ja %d \n", i+1, rowm[k], row[k]+1); - } -#endif - } -} - -void qsortR2I(double *wa, int *cor1, int *cor2, int left, int right){ -/*---------------------------------------------------------------------- -| -| qqsort: sort wa[left]...wa[right] into decreasing order -| from Kernighan & Ritchie -| -|---------------------------------------------------------------------*/ - int i, last; -/* - void swapj(int *, int, int); - void dswapm(double *, int, int); -*/ - if (left >= right) return; - - dswapm(wa, left, (left+right)/2); - swapj(cor1, left, (left+right)/2); - swapj(cor2, left, (left+right)/2); - last = left; - for (i=left+1; i<=right; i++) { - if (wa[i] > wa[left]) { - dswapm(wa, ++last, i); - swapj(cor1, last, i); - swapj(cor2, last, i); - } - } - dswapm(wa, left, last); - swapj(cor1, left, last); - swapj(cor2, left, last); - qsortR2I(wa, cor1, cor2, left, last-1); - qsortR2I(wa, cor1, cor2, last+1, right); -} - - -void qsort2C(int *ja, FLOAT *ma, int left, int right, int abval){ -/*---------------------------------------------------------------------- -| -| qqsort: sort ma[left]...ma[right] into increasing order -| from Kernighan & Ritchie -| -| ja holds the column indices -| abval = 1: consider absolute values -| 0: values -| -|---------------------------------------------------------------------*/ - int i, last; - if (left >= right) return; - if (abval) { - swapj(ja, left, (left+right)/2); - swapm(ma, left, (left+right)/2); - last = left; - for (i=left+1; i<=right; i++) { - if (ABS_VALUE(ma[i]) < ABS_VALUE(ma[left])) { - swapj(ja, ++last, i); - swapm(ma, last, i); - } - } - swapj(ja, left, last); - swapm(ma, left, last); - qsort2C(ja, ma, left, last-1, abval); - qsort2C(ja, ma, last+1, right, abval); - } - - else { - swapj(ja, left, (left+right)/2); - swapm(ma, left, (left+right)/2); - last = left; - for (i=left+1; i<=right; i++) { - if (ABS_VALUE(ma[i]) < ABS_VALUE(ma[left])) { - swapj(ja, ++last, i); - swapm(ma, last, i); - } - } - swapj(ja, left, last); - swapm(ma, left, last); - qsort2C(ja, ma, left, last-1, abval); - qsort2C(ja, ma, last+1, right, abval); - } -} - -void qqsort(int *ja, FLOAT *ma, int left, int right){ -/*---------------------------------------------------------------------- -| -| qqsort: sort ja[left]...ja[right] into increasing order -| from Kernighan & Ritchie -| -| ma holds the real values -| -|---------------------------------------------------------------------*/ - int i, last; - if (left >= right) return; - swapj(ja, left, (left+right)/2); - swapm(ma, left, (left+right)/2); - last = left; - for (i=left+1; i<=right; i++) { - if (ja[i] < ja[left]) { - swapj(ja, ++last, i); - swapm(ma, last, i); - } - } - swapj(ja, left, last); - swapm(ma, left, last); - qqsort(ja, ma, left, last-1); - qqsort(ja, ma, last+1, right); -} - -void hilosort(csptr mat, int abval, int hilo){ -/*---------------------------------------------------------------------- -| -| This routine sorts the entries in each row of a matrix from hi to low. -| -|----------------------------------------------------------------------- -| on entry: -|---------- -| (mat) = a matrix stored in SparRow format. -| -| abval = 1: use absolute values of entries -| 0: use values -| -| hilo = 1: sort in decreasing order -| 0: sort in increasing order -| -| -| on return: -| ---------- -| (mat) = (mat) where each row is sorted. -| -|---------------------------------------------------------------------*/ - int j, n=mat->n, *nnz=mat->nnzrow; -/* - void qsortC(int *, FLOAT *, int, int, int); - void qsort2C(int *, FLOAT *, int, int, int); -*/ - if (hilo) - for (j=0; jpj[j], mat->pa[j], 0, nnz[j]-1, abval); - - else - for (j=0; jpj[j], mat->pa[j], 0, nnz[j]-1, abval); - - return; -} -/*------- end of hilosort ---------------------------------------------- -|---------------------------------------------------------------------*/ - -void qsort3i(int *wa, int *cor1, int *cor2, int left, int right) -/*---------------------------------------------------------------------- -| -| qqsort: sort wa[left]...wa[right] into increasing order -| from Kernighan & Ritchie -| -|---------------------------------------------------------------------*/ -{ - int i, last; -/* - void swapj(int *, int, int); -*/ - if (left >= right) return; - - swapj(wa, left, (left+right)/2); - swapj(cor1, left, (left+right)/2); - swapj(cor2, left, (left+right)/2); - last = left; - for (i=left+1; i<=right; i++) { - if (wa[i] < wa[left]) { - swapj(wa, ++last, i); - swapj(cor1, last, i); - swapj(cor2, last, i); - } - } - swapj(wa, left, last); - swapj(cor1, left, last); - swapj(cor2, left, last); - qsort3i(wa, cor1, cor2, left, last-1); - qsort3i(wa, cor1, cor2, last+1, right); -} - diff --git a/lib/parms/src/DDPQ/piluNEW.c b/lib/parms/src/DDPQ/piluNEW.c deleted file mode 100755 index 36f219a58..000000000 --- a/lib/parms/src/DDPQ/piluNEW.c +++ /dev/null @@ -1,637 +0,0 @@ -#include -#include -#include -#if defined(C99) -#include -#else -#include -#endif -#include "protos.h" - -#define DBL_EPSILON 2.2204460492503131e-16 // double epsilon - -int pilu(p4ptr amat, csptr B, csptr C, double *droptol, - int *lfil, csptr schur) -{ -/*---------------------------------------------------------------------- -| PARTIAL ILUT - -| Converted to C so that dynamic memory allocation may be implememted -| in order to have no dropping in block LU factors. -|---------------------------------------------------------------------- -| Partial block ILU factorization with dual truncation. -| -| | B F | | L 0 | | U L^{-1} F | -| | | = | | * | | -| | E C | | E U^{-1} I | | 0 S | -| -| where B is a sub-matrix of dimension B->n. -| -|---------------------------------------------------------------------- -| -| on entry: -|========== -| ( amat ) = Permuted matrix stored in a PerMat4 struct on entry -- -| Individual matrices stored in SparRow structs. -| On entry matrices have C (0) indexing. -| on return contains also L and U factors. -| Individual matrices stored in SparRow structs. -| On return matrices have C (0) indexing. -| -| lfil[0] = number nonzeros in L-part -| lfil[1] = number nonzeros in U-part -| lfil[2] = number nonzeros in L^{-1} F -| lfil[3] = not used -| lfil[4] = number nonzeros in Schur complement -| -| droptol[0] = threshold for dropping small terms in L during -| factorization. -| droptol[1] = threshold for dropping small terms in U. -| droptol[2] = threshold for dropping small terms in L^{-1} F during -| factorization. -| droptol[3] = threshold for dropping small terms in E U^{-1} during -| factorization. -| droptol[4] = threshold for dropping small terms in Schur complement -| after factorization is completed. -| -| On return: -|=========== -| -| (schur) = contains the Schur complement matrix (S in above diagram) -| stored in SparRow struct with C (0) indexing. -| -| -| integer value returned: -| -| 0 --> successful return. -| 1 --> Error. Input matrix may be wrong. (The -| elimination process has generated a -| row in L or U whose length is > n.) -| 2 --> Memory allocation error. -| 5 --> Illegal value for lfil or last. -| 6 --> zero row in B block encountered. -| 7 --> zero row in [E C] encountered. -| 8 --> zero row in new Schur complement -|----------------------------------------------------------------------- -| work arrays: -|============= -| jw, jwrev = integer work arrays of length B->n. -| w = real work array of length B->n. -| jw2, jwrev2 = integer work arrays of length C->n. -| w2 = real work array of length C->n. -|----------------------------------------------------------------------- -| All processing is done using C indexing. -|--------------------------------------------------------------------*/ - int i, ii, j, jj, jcol, jpos, jrow, k, *jw, *jwrev; - int **lfja, *lflen, len, len2, lenu, lenl, rmax; - int *jw2, *jwrev2, lsize, rsize; - int fil0=lfil[0],fil1=lfil[1],fil2=lfil[2],fil4=lfil[4]; - double tnorm, rnorm, tabs, tmax; - double drop0=droptol[0], drop1=droptol[1], drop2=droptol[2]; - double drop3=droptol[3], drop4=droptol[4]; - int lrowz, *lrowj, rrowz, *rrowj; - FLOAT *lrowm, *rrowm, t, s, fact, *w, *w2, **lfma; -#if defined(DBL_CMPLX) - double shf, ti, sgny; - int nnzB = 0; - -/*----------get nnz of amat ---------*/ - for(j = 0; jnB; j++) - nnzB += B->nnzrow[j]; -#endif -/*-----------------------------------------------------------------------*/ - - lsize = amat->nB; - rsize = C->n; - rmax = lsize > rsize ? lsize : rsize; - jw = (int *) Malloc(rmax*sizeof(int), "pilu:1" ); - w = (FLOAT *) Malloc(rmax*sizeof(FLOAT), "pilu:2" ); - jwrev = (int *) Malloc(rmax*sizeof(int), "pilu:3" ); - jw2 = (int *) Malloc(rmax*sizeof(int), "pilu:4" ); - w2 = (FLOAT *) Malloc(rmax*sizeof(FLOAT), "pilu:5" ); - jwrev2 = (int *) Malloc(rmax*sizeof(int), "pilu:6" ); - if (fil0 < 0 || fil1<0 || amat->L->n<=0) goto label9995; - lfma = (FLOAT **) Malloc(lsize*sizeof(FLOAT *), "pilu:7" ); - lfja = (int **) Malloc(lsize*sizeof(int *), "pilu:8" ); - lflen = (int *) Malloc(lsize*sizeof(int), "pilu:9" ); -/*--------------------------------------------------------------------- -| beginning of first main loop - L, U, L^{-1}F calculations -|--------------------------------------------------------------------*/ - for (j=0; jpj[ii]; - lrowm = B->pa[ii]; - lrowz = B->nnzrow[ii]; - rrowj = amat->F->pj[ii]; - rrowm = amat->F->pa[ii]; - rrowz = amat->F->nnzrow[ii]; -/*--------------------------------------------------------------------- -| check for zero row in B block -|--------------------------------------------------------------------*/ - for (k=0; k DBL_EPSILON) goto label41; - goto label9996; -/*--------------------------------------------------------------------- -| unpack B-block in arrays w, jw, jwrev -| WE ASSUME THERE IS A DIAGONAL ELEMENT -|--------------------------------------------------------------------*/ - label41: - lenu = 1; - lenl = 0; - w[ii] = 0.0; - jw[ii] = ii; - jwrev[ii] = ii; - rnorm = 0.0; - for (j=0; jU->pa[jrow]; - fact = w[jj] * lrowm[0]; - if (ABS_VALUE(fact) > drop0 ) { /* DROPPING IN L */ - lrowj = amat->U->pj[jrow]; - lrowz = amat->U->nnzrow[jrow]; - rrowj = lfja[jrow]; - rrowm = lfma[jrow]; - rrowz = lflen[jrow]; -/*--------------------------------------------------------------------- -| combine current row and row jrow -|--------------------------------------------------------------------*/ - for (k=1; k= ii) { -/*--------------------------------------------------------------------- -| this is a fill-in element -|--------------------------------------------------------------------*/ - if (jpos == -1) { - if (lenu > lsize) {printf("U row = %d\n",ii); - goto label9991;} - i = ii + lenu; - jw[i] = j; - jwrev[j] = i; - w[i] = - s; - lenu++; - } -/*--------------------------------------------------------------------- -| this is not a fill-in element -|--------------------------------------------------------------------*/ - else - w[jpos] -= s; - } -/*--------------------------------------------------------------------- -| dealing with L -|--------------------------------------------------------------------*/ - else { -/*--------------------------------------------------------------------- -| this is a fill-in element -|--------------------------------------------------------------------*/ - if (jpos == -1) { - if (lenl > lsize) {printf("L row = %d\n",ii); - goto label9991;} - jw[lenl] = j; - jwrev[j] = lenl; - w[lenl] = - s; - lenl++; - } -/*--------------------------------------------------------------------- -| this is not a fill-in element -|--------------------------------------------------------------------*/ - else - w[jpos] -= s; - } - } -/*--------------------------------------------------------------------- -| dealing with L^{-1} F -|--------------------------------------------------------------------*/ - for (k=0; k fil0 ? fil0 : len; - amat->L->nnzrow[ii] = lenl; - if (lenl < len) - qsplitCF(w, jw, len, lenl); - if (len > 0) { - amat->L->pj[ii] = (int *) Malloc(lenl*sizeof(int), "pilu:10" ); - amat->L->pa[ii] = (FLOAT *) Malloc(lenl*sizeof(FLOAT), "pilu:11" ); - memcpy(amat->L->pj[ii], jw, lenl*sizeof(int)); - memcpy(amat->L->pa[ii], w, lenl*sizeof(FLOAT)); - } -/*--------------------------------------------------------------------- -| store the diagonal element of U -| dropping in U if size is less than drop1 * diagonal entry -|--------------------------------------------------------------------*/ - t = w[ii]; - tnorm = ABS_VALUE(rnorm) > ABS_VALUE(t) ? ABS_VALUE(rnorm):ABS_VALUE(t); - len = 0; - for (j=1; j drop1*tnorm ) { - w[len] = w[ii+j]; - jw[len] = jw[ii+j]; - len++; - } - } - lenu = len+1 > fil1 ? fil1 : len+1; - amat->U->nnzrow[ii] = lenu; - jpos = lenu-1; - if (jpos < len) - qsplitCF(w, jw, len, jpos); - amat->U->pa[ii] = (FLOAT *) Malloc(lenu*sizeof(FLOAT), "pilu:12" ); - amat->U->pj[ii] = (int *) Malloc(lenu*sizeof(int), "pilu:13" ); - if(ABS_VALUE(t) <= DBL_EPSILON) t = rnorm; //(0.0001+drop1); - amat->U->pa[ii][0] = 1.0 / t; - amat->U->pj[ii][0] = ii; -/*--------------------------------------------------------------------- -| copy the rest of U -|--------------------------------------------------------------------*/ - memcpy(&amat->U->pj[ii][1], jw, jpos*sizeof(int)); - memcpy(&amat->U->pa[ii][1], w, jpos*sizeof(FLOAT)); -/*--------------------------------------------------------------------- -| copy L^{-1} F -|--------------------------------------------------------------------*/ - len = 0; - for (j=0; j drop2*tnorm ) { - w[len] = w2[j]; - jw[len] = jw2[j]; - len++; - } - } - lenu = len > fil2 ? fil2 : len; - if (lenu < len) - qsplitCF(w, jw, len, lenu); - lflen[ii] = lenu; - - if (lenu > 0) { - lfja[ii] = (int *) Malloc(lenu*sizeof(int), "pilu:14" ); - lfma[ii] = (FLOAT *) Malloc(lenu*sizeof(FLOAT), "pilu:15" ); - memcpy(lfma[ii], w, lenu*sizeof(FLOAT)); - memcpy(lfja[ii], jw, lenu*sizeof(int)); - } - } -/*--------------------------------------------------------------------- -| beginning of second main loop E U^{-1} and Schur complement -|--------------------------------------------------------------------*/ - for (ii=0; iiE->pj[ii]; - lrowm = amat->E->pa[ii]; - lrowz = amat->E->nnzrow[ii]; - rrowj = C->pj[ii]; - rrowm = C->pa[ii]; - rrowz = C->nnzrow[ii]; -/*--------------------------------------------------------------------- -| determine if there is a zero row in [ E C ] -|-------------------------------------------------------------------- - for (k=0; kU->pa[jrow]; - fact = w[jj] * lrowm[0]; - if ( ABS_VALUE(fact) > drop3 ) { /* DROPPING IN E U^{-1} */ - lrowj = amat->U->pj[jrow]; - lrowz = amat->U->nnzrow[jrow]; - rrowj = lfja[jrow]; - rrowm = lfma[jrow]; - rrowz = lflen[jrow]; -/*--------------------------------------------------------------------- -| combine current row and row jrow - first E U^{-1} -|--------------------------------------------------------------------*/ - for (k=1; k lsize) {printf(" E U^{-1} row = %d\n",ii); - goto label9991;} - jw[lenl] = j; - jwrev[j] = lenl; - w[lenl] = - s; - lenl++; - } -/*--------------------------------------------------------------------- -| this is not a fill-in element -|--------------------------------------------------------------------*/ - else - w[jpos] -= s; - } -/*--------------------------------------------------------------------- -| incorporate into Schur complement C - (E U^{-1}) (L^{-1} F) -|--------------------------------------------------------------------*/ - for (k=0; k tnorm) tnorm = ABS_VALUE(w2[j]); */ - if(ABS_VALUE(tnorm) <= DBL_EPSILON){ - len = 1; - w[0] = 1.0; - jw[0] = ii; - } - else { - len = 0; - /* tabs = drop4*tmax*(tmax/tnorm); */ - tabs = drop4*tmax*tmax/( tnorm * (double) lenu); - for (j=0; j tabs) { - w[len] = w2[j]; - jw[len] = jw2[j]; - len++; - } - } - } - lenu = len > fil4 ? fil4 : len; - schur->nnzrow[ii] = lenu; - jpos = lenu; - if (jpos < len) - qsplitCF(w, jw, len, jpos); - schur->pa[ii] = (FLOAT *) Malloc(lenu*sizeof(FLOAT), "pilu:16" ); - schur->pj[ii] = (int *) Malloc(lenu*sizeof(int), "pilu:17" ); -/*--------------------------------------------------------------------- -| copy --- -|--------------------------------------------------------------------*/ - memcpy(&schur->pj[ii][0], jw, jpos*sizeof(int)); - memcpy(&schur->pa[ii][0], w, jpos*sizeof(FLOAT)); - } -/*--------------------------------------------------------------------- -| end main loop - now do cleanup -|--------------------------------------------------------------------*/ - free(jw); - free(w); - free(jwrev); - free(jw2); - free(w2); - free(jwrev2); - for (i=0; i 0) { - free(lfma[i]); - free(lfja[i]); - } - } - free(lfma); - free(lfja); - free(lflen); -/*--------------------------------------------------------------------- -| done -- correct return -|--------------------------------------------------------------------*/ - return 0; -label9991: -/* Incomprehensible error. Matrix must be wrong. */ - return 1; -/* label9992: - Memory allocation error. - return 2; */ -label9995: -/* illegal value for lfil or last entered */ - return 5; -label9996: -/* zero row encountered */ -/* printf("row = %d\n",ii+1); */ - return 6; -} -/*--------------------------------------------------------------------- -| end of pilut -|--------------------------------------------------------------------*/ - diff --git a/lib/parms/src/DDPQ/protos.h b/lib/parms/src/DDPQ/protos.h deleted file mode 100755 index 507b240e7..000000000 --- a/lib/parms/src/DDPQ/protos.h +++ /dev/null @@ -1,133 +0,0 @@ -#ifndef __ITSOL_INCLUDED_PROTOS_H__ -#define __ITSOL_INCLUDED_PROTOS_H__ - -#include "globheads.h" - -#if defined(FORTRAN_CAPS) -#define qsplit QSPLIT -#define readmtc READMTC -#define csrcsc CSRCSC -#define roscal ROSCAL -#define coscal COSCAL -#elif defined(FORTRAN_DOUBLE_UNDERSCORE) -#define qsplit qsplit__ -#define readmtc readmtc__ -#define csrcsc csrcsc__ -#define roscal roscal__ -#define coscal coscal__ -#elif defined(FORTRAN_UNDERSCORE) -#define qsplit qsplit_ -#define readmtc readmtc_ -#define csrcsc csrcsc_ -#define roscal roscal_ -#define coscal coscal_ -#endif - -#ifndef min -#define min(a,b) (((a)>(b))?(b):(a)) -#endif -#ifndef max -#define max(a,b) (((a)>(b))?(a):(b)) -#endif - -/* sets */ -extern void errexit(char *f_str, ...); -extern void *Malloc(int nbytes, char *msg); -extern int setupP4 (p4ptr amat, int Bn, int Cn, csptr F, csptr E); -extern int cleanP4(p4ptr amat); -extern int setupILUT(ilutptr amat, int len); -extern int cleanILUT(ilutptr amat, int indic); -extern int setupCS(csptr amat, int len, int job); -extern int cleanCS(csptr amat); -extern int nnzCS(csptr amat); -extern int nnz_arms (arms PreSt); -extern int cs_nnz (csptr A) ; -extern int cscpy(csptr amat, csptr bmat); -extern int cleanILU( iluptr lu ); -extern int mallocRow( iluptr lu, int nrow ); -extern int CSRcs(int n, FLOAT *a, int *ja, int *ia, csptr mat, int rsa); -extern int lev4_nnz(p4ptr levmat, int *lev); -extern int setupILU( iluptr lu, int n ); -extern void setup_arms (arms Levmat); -extern int cleanARMS(arms ArmsPre); -extern int csSplit4(csptr amat, int bsize, int csize, csptr B, csptr F, - csptr E, csptr C); -/* MatOps */ -extern void matvec(csptr mata, FLOAT *x, FLOAT *y); -extern void matvecz(csptr mata, FLOAT *x, FLOAT *y, FLOAT *z); -extern void luinv(int n, FLOAT *a, FLOAT *x, FLOAT *y); -extern int lusolC( FLOAT *y, FLOAT *x, iluptr lu ); -extern int rpermC(csptr mat, int *perm); -extern int cpermC(csptr mat, int *perm) ; -extern int dpermC(csptr mat, int *perm) ; -extern int CSparTran(csptr amat, csptr bmat, CompressType *compress); -extern void invsp(int start, ilutptr ilusch, FLOAT *y, FLOAT *x); -extern int armsol2(FLOAT *x, arms Prec); -extern int ascend (p4ptr levmat, FLOAT *x, FLOAT *wk); -extern int descend(p4ptr levmat, FLOAT *x, FLOAT *wk); -extern p4ptr Lvsol2(FLOAT *x, int nlev, p4ptr levmat, ilutptr ilusch, - int flag); -extern int Uvsol2(FLOAT *x, int nlev, int n, p4ptr levmat, ilutptr - ilusch); -extern void SchLsol(ilutptr ilusch, FLOAT *y) ; -extern void SchUsol(ilutptr ilusch, FLOAT *y) ; -extern void Lsolp(int start, csptr mata, FLOAT *b, FLOAT *y); -extern void Usolp(int start, csptr mata, FLOAT *y, FLOAT *x); -extern int invGauss(int nn, FLOAT *A); -extern int invSVD(int nn, FLOAT *A) ; -extern void arms_Usol(csptr mata, FLOAT *b, FLOAT *x); -extern void arms_Lsol(csptr mata, FLOAT *b, FLOAT *x); -extern int condestArms(arms armspre, FLOAT *y, FILE *fp ); - -/* misc.c */ -extern int SparTran(csptr amat, csptr bmat, int job, int flag); -extern int coscalC(csptr mata, double *diag, int nrm); -extern void dscale(int n, double *dd, FLOAT *x, FLOAT * y); -extern void hilosort(csptr mat, int abval, int hilo); -extern void printmat(FILE *ft, csptr A, int i0, int i1); -extern void qqsort(int *ja, FLOAT *ma, int left, int right); -extern void qsort2C(int *ja, FLOAT *ma, int left, int right, int - abval); -extern void qsort3i(int *wa, int *cor1, int *cor2, int left, int - right); -extern void qsortC(int *ja, FLOAT *ma, int left, int right, int - abval); -extern void qsortR2I(double *wa, int *cor1, int *cor2, int left, int - right); -extern int qsplitCF(FLOAT *a, int *ind, int n, int ncut); -extern int roscalC(csptr mata, double *diag, int nrm); -extern void swapj(int v[], int i, int j); -extern void swapm(FLOAT v[], int i, int j); -extern void dswapm(double v[], int i, int j); - -/* piluNEW.c */ -extern int pilu(p4ptr amat, csptr B, csptr C, double *droptol, int - *lfil, csptr schur); - -/* ilutpC.c */ -extern int ilutD(csptr amat, double *droptol, int *lfil, ilutptr - ilusch); -extern int ilutpC(csptr amat, double *droptol, int *lfil, double - permtol, int mband, ilutptr ilusch); - -/* PQ.c */ -extern int PQperm(csptr mat, int *Pord, int *Qord, int - *nnod, double tol, int nbnd); -extern int preSel(csptr mat, int *icor, int *jcor, int job, double - tol, int *count, int nbnd); -extern int weightsC(csptr mat, double *w); -extern int add2com(int *nback, int nod, int *iord, int *riord); -extern int add2is(int *last, int nod, int *iord, int *riord); -extern int indsetC(csptr mat, int bsize, int *iord, int *nnod, double - tol,int nbnd); - -/* setblks.c */ -extern int KeyComp( const void *vfst, const void *vsnd ); -extern int init_blocks( csptr csmat, int *pnBlock, int **pnB, int - **pperm, double eps, double *t_hash, double - *t_angle ); - -/* systimer.c */ -extern double sys_timer(); - -#endif diff --git a/lib/parms/src/DDPQ/setblks.c b/lib/parms/src/DDPQ/setblks.c deleted file mode 100755 index f9bae1db6..000000000 --- a/lib/parms/src/DDPQ/setblks.c +++ /dev/null @@ -1,245 +0,0 @@ -#include -#include -#include "protos.h" - -typedef struct __KeyType -{ - int var; /* row number */ - int key; /* hash value */ -} KeyType; - -int KeyComp( const void *vfst, const void *vsnd ) -{ - KeyType *fst = (KeyType *)vfst, *snd = (KeyType *)vsnd; - if( fst->key == snd->key ) { - if( fst->var < snd->var ) - return -1; - return 1; - } - if( fst->key < snd->key ) - return -1; - return 1; -} - -int init_blocks( csptr csmat, int *pnBlock, int **pnB, int **pperm, - double eps, double *t_hash, double *t_angle ) -{ -/*---------------------------------------------------------------------------- - * Setup Blocks ( rows and columns might be permuted to get better results ) - *---------------------------------------------------------------------------- - * Na Li, Aug 2001 - *---------------------------------------------------------------------------- - * on entry: - * ========= - * csmat = a matrix stored in SparRow format - * eps = parameter for deciding when to do a union of two rows - * into the same group. Two rows u and v are merged into a - * block when cos() == (u,v)/(|u|*|v|), is > eps. - * eps should be <= 1. - *---------------------------------------------------------------------------- - * on return: - * ========== - * csmat = matrix stored in SparRow format after permutation - * pnBlock = dimension of the block matrix - * pnB = dimension of each block - * - *---------------------------------------------------------------------------- - * Combination of hash method and angle method: - *---------------------------------------------------------------------------- - * Designed for the matrices with symmetric patterns - * (1) Hash method - * a. Calculate hash values - * b. qsort rows according to their hash values - * c. Get compressed graph as the following format: - * (2) Angle method - * a. Calculate A^T - * b. for i-th row, calculate dot product (row_i, row_j) using A*A^T - * algorithm where j = i+1, ..., n-1 and group[j] == -1 - * if cos( ) = (row_i,row_j)/|row_i||row_j| is > eps, - * we merge row_i and row_j by resetting - * group[j] = i and size[i] = size[i]+size[j] - *--------------------------------------------------------------------------*/ - int n = csmat->n, nBlock = 0, i, j, k; - csptr at = NULL; - KeyType *group = NULL; - CompressType *compress = NULL; - int *perm = NULL, *nB = NULL; - int nnzrow0, nnzrow, key0, key, *ja0, *ja, row0, row, newblock; - int *iw = NULL, *jbuf = NULL; - int cnt, pos, nnz_i, row_j, col, bkcnt; - int nextBlockID, nextBlockPos, belongTo, grp; - double eps_2 = eps * eps, t1, t2; - - t1 = sys_timer(); /* begin Hash method timer */ - group = (KeyType *)Malloc( n*sizeof(KeyType), "init_blocks" ); - compress = (CompressType *)Malloc( n*sizeof(CompressType), "init_blocks" ); - perm = (int *)Malloc( n * sizeof(int), "init_blocks" ); - iw = perm; /* iw and perm array can share memory here because they will - * never be used at the same time */ - for( i = 0; i < n; i++ ) { - iw[i] = 0; - compress[i].grp = -1; - } -/*-------------------- compress matrix based on hash algorithm */ -/*-------------------- get hash value of each row */ - for( i = 0; i < n; i++ ) { - nnzrow = csmat->nnzrow[i]; - key = 0; - ja = csmat->pj[i]; - for( j = 0; j < nnzrow; j++ ) - key += ja[j]+1; - group[i].key = key; - group[i].var = i; - } -/*-------------------- sort rows -- uses function KeyComp */ - qsort( group, n, sizeof(KeyType), KeyComp ); - -/*-------------------- compress matrix */ - for( i = 0; i < n; i++ ) { - row0 = group[i].var; - if( compress[row0].grp != -1 ) continue; /* already assigned */ - key0 = group[i].key; - nnzrow0 = csmat->nnzrow[row0]; - ja0 = csmat->pj[row0]; -/*-------------------- beginning of new block. set .grp and .count */ - compress[row0].grp = -1; - compress[row0].count = 1; -/*-------------------- loop over all rows having same check-sum keys */ - for( j = i + 1; j < n; j++ ) { - key = group[j].key; - if( key != key0 ) break; - row = group[j].var; - if( compress[row].grp != -1 ) continue; /* already assigned */ - nnzrow = csmat->nnzrow[row]; - if( nnzrow != nnzrow0 ) continue; - ja = csmat->pj[row]; - newblock = 0; -/*-------------------- compare patterns of the rows */ - for( k = 0; k < nnzrow; k++ ) iw[ ja0[k] ] = 1; - for( k = 0; k < nnzrow; k++ ) { - if( iw[ ja[k] ] == 0 ) { - newblock = 1; - break; - } - } - for( k = 0; k < nnzrow; k++ ) iw[ ja0[k] ] = 0; /* reset iw */ -/*-------------------- row belongs to group row0 */ - if( !newblock ) { - compress[row].grp = row0; - compress[row0].count++; - } - } - } - t2 = sys_timer(); /* end Hash method timer */ - *t_hash = t2 - t1; - - t1 = sys_timer(); /* begin angle method timer */ - nB = (int *)Malloc( n * sizeof(int), "init_blocks" ); - jbuf = (int *)Malloc( n * sizeof(int), "init_blocks" ); - -/*-------------------- compress matrix based on angle algorithm */ -/*-------------------- calculate compressed A^T */ - at = (csptr)Malloc( sizeof(SparMat), "init_blocks" ); - setupCS( at, n, 0 ); - if( CSparTran( csmat, at, compress ) != 0 ) - return -1; - -/*---------------------------------------------------------------------------- - * only the row representing beginning of block satisfies: - * compress[row].grp = -1, so far. - * how many such rows is up to the compression rate of hash compression - * algorithm we did above - *--------------------------------------------------------------------------*/ - -/*--------------------------------------------------------------- - * use group to backup original compressed matrix by Hash method. - * It is very important because the array 'compress' will be changed - * during Angle method. Or, we'll get incorrect inner product. - *--------------------------------------------------------------*/ - for( i = 0; i < n; i++ ) { - group[i].var = compress[i].grp; - group[i].key = compress[i].count; - } - - for( i = 0; i < n; i++ ) { - if( compress[i].grp != -1 ) continue; - nB[nBlock] = compress[i].count; /* !!! not 1 here */ - cnt = 0; -/*-------------------- calculate (u,v_j ), j = i+1,...,n, using product - *-------------------- algorithm of A * A_T */ - nnz_i = csmat->nnzrow[i]; - for( j = 0; j < nnz_i; j++ ) { - row_j = csmat->pj[i][j]; - if( group[row_j].var != -1 ) /* i.e. original compress[row_j].grp */ - continue; - bkcnt = group[row_j].key; /* i.e. original compress[row_j].count */ - for( k = at->nnzrow[row_j] - 1; k >= 0; k-- ) { - col = at->pj[row_j][k]; - if( col <= i ) break; - if( compress[col].grp != -1 ) continue; /* needed because compress - array is dynamically updated */ - if( iw[col] == 0 ) { /* new nonzero of (u,v_j) */ - jbuf[cnt] = col; - cnt++; - } - iw[col] += bkcnt; /* correct for matrix with symmetric pattern */ - } - } -/*-------------------- set group for row i and reset iw */ - for( j = 0; j < cnt; j++ ) { - pos = jbuf[j]; - if( iw[pos] * iw[pos] >= eps_2 * nnz_i * csmat->nnzrow[pos] ) { - compress[pos].grp = i; - nB[nBlock] += compress[pos].count; /* !!! not 1 here */ - } - iw[pos] = 0; /* reset iw */ - } - nBlock++; /* begin new block, add block count by 1 */ - } /* end loop i */ - -/*-------------------- free group */ - if( group ) { - /* no need group array any more */ - free( group ); - group = NULL; - } - - *pnBlock = nBlock; - *pnB = (int *)Malloc( nBlock * sizeof(int), "init_blocks" ); - for( i = 0; i < nBlock; i++ ) { - if( nB[i] > MAX_BLOCK_SIZE ) { - fprintf( stderr, "Block of size = %d exceeds MAX_BLOCK_SIZE\n", nB[i] ); - return -1; - } - (*pnB)[i] = nB[i]; - } - -/*-------------------- calculate permutation array - Array nB will - * be used to store next available position in each block */ - nextBlockID = 0; - nextBlockPos = 0; - for( i = 0; i < n; i++ ) { - if( compress[i].grp == -1 ) { - perm[i] = nextBlockPos; - nextBlockPos += (*pnB)[nextBlockID++]; - nB[i] = 1; - } else { - belongTo = compress[i].grp; - grp = compress[belongTo].grp; - if( grp != -1 ) /* should find the final beginning of block */ - belongTo = grp; - perm[i] = perm[belongTo] + nB[belongTo]; - nB[belongTo]++; - } - } - t2 = sys_timer(); /* end angle method timer */ - *t_angle = t2 - t1; - *pperm = perm; - - cleanCS( at ); - free( nB ); - free( jbuf ); - free( compress ); - - return 0; -} diff --git a/lib/parms/src/DDPQ/sets.c b/lib/parms/src/DDPQ/sets.c deleted file mode 100755 index 1ccf3adb3..000000000 --- a/lib/parms/src/DDPQ/sets.c +++ /dev/null @@ -1,811 +0,0 @@ -#include -#include -#include -#if defined(C99) -#include -#else -#include -#endif -#include "protos.h" - -void parms_errexit( char *f_str, ... ) -{ - va_list argp; - char out1[256], out2[512]; - - va_start(argp, f_str); - vsprintf(out1, f_str, argp); - va_end(argp); - - sprintf(out2, "Error! %s\n", out1); - - fprintf(stdout, "%s", out2); - fflush(stdout); - - exit( -1 ); -} - -void *Malloc( int nbytes, char *msg ) -{ - void *ptr; - - if (nbytes == 0) - return NULL; - - ptr = (void *)malloc(nbytes); - if (ptr == NULL) - parms_errexit( "Not enough mem for %s. Requested size: %d bytes", msg, nbytes ); - - return ptr; -} - -int setupCS(csptr amat, int len, int job) -{ -/*---------------------------------------------------------------------- -| Initialize SparRow structs. -|---------------------------------------------------------------------- -| on entry: -|========== -| ( amat ) = Pointer to a SparRow struct. -| len = size of matrix -| job = 0: pattern only -| 1: data and pattern -| -| On return: -|=========== -| -| amat->n -| ->*nnzrow -| ->**ja -| ->**ma -| -| integer value returned: -| 0 --> successful return. -| 1 --> memory allocation error. -|--------------------------------------------------------------------*/ - amat->n = len; - amat->nnzrow = (int *)Malloc( len*sizeof(int), "setupCS" ); - amat->pj = (int **) Malloc( len*sizeof(int *), "setupCS" ); - if( job == 1 ) - amat->pa = (FLOAT **) Malloc( len*sizeof(FLOAT *), "setupCS" ); - else - amat->pa = NULL; - return 0; -} -/*--------------------------------------------------------------------- -| end of setupCS -|--------------------------------------------------------------------*/ - -int cleanCS(csptr amat) -{ -/*---------------------------------------------------------------------- -| Free up memory allocated for SparRow structs. -|---------------------------------------------------------------------- -| on entry: -|========== -| ( amat ) = Pointer to a SparRow struct. -|--------------------------------------------------------------------*/ - /* */ - int i; - if (amat == NULL) return 0; - if (amat->n < 1) return 0; - - for (i=0; in; i++) { - if (amat->nnzrow[i] > 0) { - if( amat->pa ) free(amat->pa[i]); - free(amat->pj[i]); - } - } - if (amat->pa) free(amat->pa); - free(amat->pj); - free(amat->nnzrow); - free(amat); - return 0; -} -/*--------------------------------------------------------------------- -| end of cleanCS -|--------------------------------------------------------------------*/ - -int nnzCS( csptr amat ) -{ - int nnz = 0, i, n = amat->n; - for( i = 0; i < n; i++ ) { - nnz += amat->nnzrow[i]; - } - return nnz; -} - -int cscpy(csptr amat, csptr bmat){ -/*---------------------------------------------------------------------- -| Convert CSR matrix to SparRow struct -|---------------------------------------------------------------------- -| on entry: -|========== -| ( amat ) = Matrix stored in SparRow format -| -| -| On return: -|=========== -| -| ( bmat ) = Matrix stored as SparRow struct containing a copy -| of amat -| -| integer value returned: -| 0 --> successful return. -| 1 --> memory allocation error. -|--------------------------------------------------------------------*/ - int j, len, size=amat->n; - FLOAT *bma; - int *bja; -/*------------------------------------------------------------*/ - for (j=0; jnnzrow[j] = amat->nnzrow[j]; - if (len > 0) { - bja = (int *) Malloc(len*sizeof(int), "cscpy:1" ); - bma = (FLOAT *) Malloc(len*sizeof(FLOAT), "cscpy:2" ); - memcpy(bja,amat->pj[j],len*sizeof(int)); - memcpy(bma,amat->pa[j],len*sizeof(FLOAT)); - bmat->pj[j] = bja; - bmat->pa[j] = bma; - } - } - return 0; -} -/*-----------------------------------------------------------------------*/ - -int setupILU( iluptr lu, int n ) -{ -/*---------------------------------------------------------------------- -| Initialize ILUSpar structs. -|---------------------------------------------------------------------- -| on entry: -|========== -| ( lu ) = Pointer to a ILUSpar struct. -| n = size of matrix -| -| On return: -|=========== -| -| lu->n -| ->L L matrix, SparRow format -| ->D Diagonals -| ->U U matrix, SparRow format -| ->work working buffer of length n -| ->bf buffer -| -| integer value returned: -| 0 --> successful return. -| -1 --> memory allocation error. -|--------------------------------------------------------------------*/ - lu->n = n; - lu->D = (FLOAT *)Malloc( sizeof(FLOAT) * n, "setupILU" ); - lu->L = (csptr)Malloc( sizeof(SparMat), "setupILU" ); - setupCS( lu->L, n, 1 ); - lu->U = (csptr)Malloc( sizeof(SparMat), "setupILU" ); - setupCS( lu->U, n, 1 ); - lu->work = (int *)Malloc( sizeof(int) * n, "setupILU" ); - return 0; -} -/*--------------------------------------------------------------------- -| end of setupILU -|--------------------------------------------------------------------*/ - - -int cleanILU( iluptr lu ) -{ -/*---------------------------------------------------------------------- -| Free up memory allocated for ILUSpar structs. -|---------------------------------------------------------------------- -| on entry: -|========== -| ( lu ) = Pointer to a ILUSpar struct. -|--------------------------------------------------------------------*/ - if( NULL == lu ) return 0; - if( lu->D ) { - free( lu->D ); - } - cleanCS( lu->L ); - cleanCS( lu->U ); - if( lu->work ) free( lu->work ); - free( lu ); - return 0; -} -/*--------------------------------------------------------------------- -| end of cleanILU -|--------------------------------------------------------------------*/ - -int mallocRow( iluptr lu, int nrow ) -{ -/*---------------------------------------------------------------------- -| Prepare space of a row according to the result of level structure -|---------------------------------------------------------------------- -| on entry: -|========== -| ( lu ) = Pointer to a ILUSpar struct. -| nrow = the current row to deal with -| -| On return: -|=========== -| -| lu->L->pa[nrow][...] -| ->U->pa[nrow][...] -| -| integer value returned: -| 0 --> successful return. -| -1 --> memory allocation error. -|--------------------------------------------------------------------*/ - int nnzrow = lu->L->nnzrow[nrow]; - lu->L->pa[nrow] = (FLOAT *)Malloc( sizeof(FLOAT)*nnzrow, "mallocRow" ); - nnzrow = lu->U->nnzrow[nrow]; - lu->U->pa[nrow] = (FLOAT *)Malloc( sizeof(FLOAT)*nnzrow, "mallocRow" ); - return 0; -} -/*--------------------------------------------------------------------- -| end of mallocRow -|--------------------------------------------------------------------*/ - -int setupP4 (p4ptr amat, int Bn, int Cn, csptr F, csptr E) -{ -/*---------------------------------------------------------------------- -| initialize PerMat4 struct given the F, E, blocks. -|---------------------------------------------------------------------- -| on entry: -|========== -| ( amat ) = Pointer to a PerMat4 struct. -| Bn = size of B block -| Cn = size of C block -| F, E = the two blocks to be assigned to srtruct - without the -| -| On return: -|=========== -| -| amat->L for each block: amat->M->n -| ->U ->nnzrow -| ->E ->pj -| ->F ->pa -| ->perm -| ->rperm (if meth[1] > 0) -| ->D1 (if meth[2] > 0) -| ->D2 (if meth[3] > 0) -| -| Scaling arrays are initialized to 1.0. -| -| integer value returned: -| 0 --> successful return. -| 1 --> memory allocation error. -|--------------------------------------------------------------------*/ - int n; - /* size n */ - n = amat->n = Bn + Cn; - amat->nB = Bn; -/* amat->perm = (int *) Malloc(n*sizeof(int), "setupP4:1" ); */ -/* assign space for wk -- note that this is only done at 1st level - at other levels, copy pointer of wk from previous level */ - if (amat->prev == NULL) /* wk has 2 * n entries now */ - amat->wk = (FLOAT *) Malloc(2*n*sizeof(FLOAT), "setupP4:2" ); - else - amat->wk = (amat->prev)->wk; - -/*-------------------- L and U */ - amat->L = (csptr) Malloc(sizeof(SparMat), "setupP4:3" ); - if (setupCS(amat->L, Bn,1)) return 1; - /* fprintf(stdout," -- BN %d Cn %d \n", Bn,Cn); */ - amat->U = (csptr) Malloc(sizeof(SparMat), "setupP4:4" ); - if (setupCS(amat->U, Bn,1)) return 1; - - amat->F = F; - amat->E = E; - return 0; -} -/*--------------------------------------------------------------------- -| end of setupP4 -|--------------------------------------------------------------------*/ - -int cleanP4(p4ptr amat) -{ -/*---------------------------------------------------------------------- -| Free up memory allocated for Per4Mat structs. -|---------------------------------------------------------------------- -| on entry: -|========== -| ( amat ) = Pointer to a Per4Mat struct. -|--------------------------------------------------------------------*/ - -/* -------------------------- */ - if (amat == NULL) return 0; - if (amat->n < 1) return 0; - - - if (amat->perm) { - free(amat->perm); - amat->perm = NULL; - } - - if (!amat->symperm) { - if (amat->rperm) free(amat->rperm); - amat->rperm = NULL; - } - - if (amat->F) { - cleanCS(amat->F); - amat->F = NULL; - } - if (amat->E) { - cleanCS(amat->E); - amat->E = NULL; - } - if (amat->L) { - cleanCS(amat->L); - amat->L = NULL; - } - if (amat->U) { - cleanCS(amat->U); - amat->U = NULL; - } - - if (amat->prev == NULL) - if (amat->wk) free(amat->wk); - - if (amat->D1) free(amat->D1); - if (amat->D2) free(amat->D2); - return 0; -} -/*--------------------------------------------------------------------- -| end of cleanP4 -|--------------------------------------------------------------------*/ - - -int setupILUT(ilutptr amat, int len) -{ -/*---------------------------------------------------------------------- -| Allocate pointers for ILUTfac structs. -|---------------------------------------------------------------------- -| on entry: -|========== -| ( amat ) = Pointer to a ILUTfac struct. -| len = size of L U blocks -| -| On return: -|=========== -| -| amat->L for each block: amat->M->n -| ->U ->nnzrow -| ->pj -| ->pa -| ->rperm (if meth[0] > 0) -| ->perm2 (if meth[1] > 0) -| ->D1 (if meth[2] > 0) -| ->D2 (if meth[3] > 0) -| -| Permutation arrays are initialized to the identity. -| Scaling arrays are initialized to 1.0. -| -| integer value returned: -| 0 --> successful return. -| 1 --> memory allocation error. -|--------------------------------------------------------------------*/ - amat->n = len; - amat->wk = (FLOAT *) Malloc(2*len*sizeof(FLOAT), "setupILUT:5" ); - amat->L = (csptr) Malloc(sizeof(SparMat), "setupILUT:6" ); - if (setupCS(amat->L, len,1)) return 1; - amat->U = (csptr) Malloc(sizeof(SparMat), "setupILUT:7" ); - if (setupCS(amat->U, len,1)) return 1; - return 0; -} -/*--------------------------------------------------------------------- -| end of setupILUT -|--------------------------------------------------------------------*/ -int cleanILUT(ilutptr amat, int indic) -{ -/*---------------------------------------------------------------------- -| Free up memory allocated for IluSpar structs. -|---------------------------------------------------------------------- -| on entry: -|========== -| ( amat ) = Pointer to a IluSpar struct. -| indic = indicator for number of levels. indic=0 -> zero level. -|--------------------------------------------------------------------*/ - /*----------------*/ - - if (amat->wk) { - free(amat->wk); - amat->wk = NULL; - } - cleanCS(amat->L); - cleanCS(amat->U); - - if (indic) cleanCS(amat->C); -/*-------------------- nonsymmetric permutation */ - if (amat->rperm) { - free(amat->rperm); - amat->rperm = NULL; - } - if (amat->perm) { - free(amat->perm); - amat->perm = NULL; - } - -/*-------------------- ilutp permutation */ - if (amat->perm2) free(amat->perm2); -/*-------------------- diagonal scalings */ - if (amat->D1) free(amat->D1); - if (amat->D2) free(amat->D2); - return 0; -} -/*--------------------------------------------------------------------- -| end of cleanILUT -|--------------------------------------------------------------------*/ - - -void setup_arms (arms Levmat) -{ - Levmat->ilus = (ilutptr) Malloc(sizeof(IluSpar), "setup_arms:ilus" ); - Levmat->levmat = (p4ptr) Malloc(sizeof(Per4Mat), "setup_arms:levmat" ); -} -int cleanARMS(arms ArmsPre) -{ - p4ptr amat = ArmsPre->levmat; - ilutptr cmat = ArmsPre->ilus; -/*---------------------------------------------------------------------- -| Free up memory allocated for entire ARMS preconditioner. -|---------------------------------------------------------------------- -| on entry: -|========== -| ( amat ) = Pointer to a Per4Mat struct. -| ( cmat ) = Pointer to a IluSpar struct. -|--------------------------------------------------------------------*/ -/* case when nlev == 0 */ - int indic=(amat->nB != 0) ; - /* && amat->next !=NULL) ; */ - - p4ptr levc, levn; -/* - int cleanCS(csptr); - int cleanP4(p4ptr); - int cleanILUT(ilutptr, int); -*/ - levc = amat; - - if (indic) { - while (levc) { - if (cleanP4(levc)) return(1) ; - levn = levc->next; - free(levc); - levc = levn; - } - } - else - if (amat) { - free(amat) ; - amat = NULL; - } - - cleanILUT(cmat,indic); - - - if (cmat) { - free(cmat); - cmat = NULL; - } - - return 0; -} -/*--------------------------------------------------------------------- -| end of cleanARMS -|--------------------------------------------------------------------*/ - - -int csSplit4(csptr amat, int bsize, int csize, csptr B, csptr F, - csptr E, csptr C) -{ -/*--------------------------------------------------------------------- -| Convert permuted csrmat struct to PerMat4 struct -| - matrix already permuted -|---------------------------------------------------------------------- -| on entry: -|========== -| ( amat ) = Matrix stored in SparRow format. -| Internal pointers (and associated memory) destroyed before -| return. -| -| On return: -|=========== -| -| B, E, F, C = 4 blocks in -| -| | B F | -| Amat = | | -| | E C | -| -| -| integer value returned: -| 0 --> successful return. -| 1 --> memory allocation error. -|--------------------------------------------------------------------*/ - int j, j1, numr, numl, ind, newj, rowz, *rowj, *new1j, *new2j; - FLOAT *rowm, *new1m, *new2m; -/*--------------------------------------------------------------------- -| Sort the matrix and separate into | B F | -| | | -| | E C | -|--------------------------------------------------------------------*/ - if (setupCS(B,bsize,1)) goto label111; - if (setupCS(F,bsize,1)) goto label111; - if (setupCS(E,csize,1)) goto label111; - if (setupCS(C,csize,1)) goto label111; - new1j = (int *) Malloc(bsize*sizeof(int), "csSplit4:1" ); - new2j = (int *) Malloc(csize*sizeof(int), "csSplit4:2" ); - new1m = (FLOAT *) Malloc(bsize*sizeof(FLOAT), "csSplit4:3" ); - new2m = (FLOAT *) Malloc(csize*sizeof(FLOAT), "csSplit4:4" ); -/* B and F blocks */ - for (j=0; jnnzrow[j]; - rowj = amat->pj[j]; - rowm = amat->pa[j]; - for (j1=0; j1nnzrow[j] = numl; - F->nnzrow[j] = numr; - if (numl>0) { - B->pj[j] = (int *) Malloc(numl*sizeof(int), "csSplit4:5" ); - B->pa[j] = (FLOAT *) Malloc(numl*sizeof(FLOAT), "csSplit4:6" ); - } - if (numr>0) { - F->pj[j] = (int *) Malloc(numr*sizeof(int), "csSplit4:7" ); - F->pa[j] = (FLOAT *) Malloc(numr*sizeof(FLOAT), "csSplit4:8" ); - } - numl = numr = 0; - for (j1=0; j1pj[j], new1j, numl*sizeof(int)); - memcpy(B->pa[j], new1m, numl*sizeof(FLOAT)); - memcpy(F->pj[j], new2j, numr*sizeof(int)); - memcpy(F->pa[j], new2m, numr*sizeof(FLOAT)); - } -/* E and C blocks */ - for (j=0; jnnzrow[ind]; - rowj = amat->pj[ind]; - rowm = amat->pa[ind]; - for (j1=0; j1nnzrow[j] = numl; - C->nnzrow[j] = numr; - if (numl>0) { - E->pj[j] = (int *) Malloc(numl*sizeof(int), "csSplit4:9" ); - E->pa[j] = (FLOAT *) Malloc(numl*sizeof(FLOAT), "csSplit4:10" ); - } - if (numr>0) { - C->pj[j] = (int *) Malloc(numr*sizeof(int), "csSplit4:11" ); - C->pa[j] = (FLOAT *) Malloc(numr*sizeof(FLOAT), "csSplit4:12" ); - } - numl = numr = 0; - for (j1=0; j1pj[j], new1j, numl*sizeof(int)); - memcpy(E->pa[j], new1m, numl*sizeof(FLOAT)); - memcpy(C->pj[j], new2j, numr*sizeof(int)); - memcpy(C->pa[j], new2m, numr*sizeof(FLOAT)); - } - - if (new1j) free(new1j); - if (new2j) free(new2j); - if (new1m) free(new1m); - if (new2m) free(new2m); - return 0; -label111: - return 1; -} -/*--------------------------------------------------------------------- -| end of csSplit4 -|--------------------------------------------------------------------*/ - -int CSRcs( int n, FLOAT *a, int *ja, int *ia, csptr mat, int rsa ) -{ -/*---------------------------------------------------------------------- -| Convert CSR matrix to SparRow struct -|---------------------------------------------------------------------- -| on entry: -|========== -| a, ja, ia = Matrix stored in CSR format (with FORTRAN indexing). -| rsa = source file is symmetric HB matrix -| -| On return: -|=========== -| -| ( mat ) = Matrix stored as SparRow struct. -| -| integer value returned: -| 0 --> successful return. -| 1 --> memory allocation error. -|--------------------------------------------------------------------*/ - int i, j, j1, len, col, nnz; - FLOAT *bra; - int *bja; - /* setup data structure for mat (csptr) struct */ - setupCS( mat, n, 1 ); - - if( rsa ) { /* RSA HB matrix */ - for( j = 0; j < n; j++ ) { - len = ia[j+1] - ia[j]; - mat->nnzrow[j] = len; - } - for( j = 0; j < n; j++ ) { - for( j1 = ia[j]-1; j1 < ia[j+1]-1; j1++ ) { - col = ja[j1] - 1; - if( col != j ) mat->nnzrow[col]++; - } - } - for( j = 0; j < n; j++ ) { - nnz = mat->nnzrow[j]; - mat->pj[j] = (int *)Malloc( nnz * sizeof(int), "CSRcs" ); - mat->pa[j] = (FLOAT *)Malloc( nnz * sizeof(FLOAT), "CSRcs" ); - mat->nnzrow[j] = 0; - } - for( j = 0; j < n; j++ ) { - for( j1 = ia[j]-1; j1 < ia[j+1]-1; j1++ ) { - col = ja[j1] - 1; - mat->pj[j][mat->nnzrow[j]] = col; - mat->pa[j][mat->nnzrow[j]] = a[j1]; - mat->nnzrow[j]++; - if( col != j ) { - mat->pj[col][mat->nnzrow[col]] = j; - mat->pa[col][mat->nnzrow[col]] = a[j1]; - mat->nnzrow[col]++; - } - } - } - return 0; - } - for (j=0; jnnzrow[j] = len; - if (len > 0) { - bja = (int *) Malloc( len*sizeof(int), "CSRcs" ); - bra = (FLOAT *) Malloc( len*sizeof(FLOAT), "CSRcs" ); - i = 0; - for (j1=ia[j]-1; j1pj[j] = bja; - mat->pa[j] = bra; - } - } - return 0; -} -/*--------------------------------------------------------------------- -| end of CSRcs -|--------------------------------------------------------------------*/ - -/* -int nnz_ilu( iluptr lu ) -{ - int nnz = 0, i; - for( i = 0; i < lu->n; i++ ) { - nnz += lu->L->nnzrow[i]; - nnz += lu->U->nnzrow[i]; - nnz++; - } - return nnz; -} -*/ - -int lev4_nnz(p4ptr levmat, int *lev) -{ - /* counts all nonzero elements in levmat struct -- - recursive */ - int nnzT, nnzL, nnzU, nnzF, nnzE, nnzDown=0; - p4ptr nextmat; - nnzL = cs_nnz(levmat->L); - nnzU = cs_nnz(levmat->U); - nnzF = cs_nnz(levmat->F); - nnzE = cs_nnz(levmat->E); - nnzT = nnzL+nnzU+nnzF+nnzE; - /* print */ -#if 0 - if (*lev == 0) - fprintf(ft, - "\nnnz/lev used: L U F E subtot\n"); - fprintf(ft," Level %2d %8d %8d %8d %8d %8d\n", - *lev, nnzL, nnzU, nnzF, nnzE, nnzT); -#endif - (*lev)++; - nextmat = levmat->next; - if (nextmat != NULL) - nnzDown = lev4_nnz(nextmat, lev); - return (nnzT+nnzDown); -} - -int cs_nnz (csptr A) -{ - /* counts the number of nonzero elements in CSR matrix A */ - int i, n, nnz=0; - n = A->n; - for (i=0; innzrow[i]; - return nnz; - } - -int nnz_arms (arms PreSt) -{ -/*------------------------------------------------------- -| computes and prints out total number of nonzero elements -| used in ARMS factorization -+--------------------------------------------------------*/ - p4ptr levmat = PreSt->levmat; - ilutptr ilschu = PreSt->ilus; - int nlev = PreSt->nlev; - int ilev=0,nnz_lev,nnz_sch,nnz_tot; - nnz_lev = 0; - if (nlev) nnz_lev+= lev4_nnz(levmat, &ilev); - nnz_sch = cs_nnz(ilschu->L)+cs_nnz(ilschu->U); - if (nlev) nnz_sch += cs_nnz(ilschu->C); - nnz_tot = nnz_lev+nnz_sch; -#if 0 - fprintf(ft,"\n"); - fprintf(ft,"Total nonzeros for interm. blocks.... = %10d\n",nnz_lev); - fprintf(ft,"Total nonzeros for last level ....... = %10d\n",nnz_sch); - fprintf(ft,"Grand total.......................... = %10d\n",nnz_tot); -#endif - return nnz_tot; -} - -/*---------------------------------------------------------------------- -| Output the pattern of L\U, which can be loaded by matlab -----------------------------------------------------------------------*/ -/* -int outputLU( iluptr lu, char *filename ) -{ - FILE *fmatlab = fopen( filename, "w" ); - int n = lu->n, i, j, nnzrow; - csptr L = lu->L, U = lu->U; - - if( !fmatlab ) return -1; - fprintf( fmatlab, "%d %d 0\n", n, n ); - for( i = 0; i < n; i++ ) { - nnzrow = L->nnzrow[i]; - for( j = 0; j < nnzrow; j++ ) - fprintf( fmatlab, "%d %d 1\n", i+1, L->pj[i][j]+1 ); - } - for( i = 0; i < n; i++ ) { - nnzrow = U->nnzrow[i]; - for( j = 0; j < nnzrow; j++ ) - fprintf( fmatlab, "%d %d 1\n", i+1, U->pj[i][j]+1 ); - } - for( i = 0; i < n; i++ ) - fprintf( fmatlab, "%d %d 1\n", i+1, i+1 ); - fclose( fmatlab ); - return 0; -} -*/ - diff --git a/lib/parms/src/DDPQ/svdInvC.c b/lib/parms/src/DDPQ/svdInvC.c deleted file mode 100755 index 3b6d9fb85..000000000 --- a/lib/parms/src/DDPQ/svdInvC.c +++ /dev/null @@ -1,125 +0,0 @@ -#include -#include -#include -#include "protos.h" - -#define DBL_EPSILON 2.2204460492503131e-16 // double epsilon - -#define TOL 1.e-17 -#define max_abs(a,b) ((ABS_VALUE(a)>ABS_VALUE(b))?(a):(b)) - -int invGauss(int nn, FLOAT *A) -{ - /* *-------------------- inversion by svd - This calls lapack routines for inverting a dense matrix. - dgetrf and dgetri - - ON ENTRY - ** A = square matrix of size n x n -- dimensioned with - ** leading dimension = n - - ON RETURN A contains the inverse of the input matrix. - */ - int lWk, info; - - FLOAT *Wk; - int *ipiv; - - lWk = 10*nn; - - /*-------------------- trivial case nn = 1 */ - if (nn == 1) { - if(ABS_VALUE(A[0]-DBL_EPSILON) <= DBL_EPSILON*ABS_VALUE(A[0])) - return 1; - else { - A[0] = 1.0 / A[0]; - return 0; - } - } - /*-------------------- general case */ - - Wk = (FLOAT *) malloc(lWk*sizeof(FLOAT)); - ipiv = (int *) malloc(nn*sizeof(int)); - if (Wk == NULL || ipiv == NULL) - return -1; - /*-------------------- get LU factorization with pivoting */ - GGETRF (nn, nn,A, nn, ipiv, info); - - if (info !=0 ) return info; - /*-------------------- compute inverse ---- */ - GGETRI (nn, A, nn, ipiv, Wk, lWk, info); - - free(Wk); - free(ipiv); - return info; -} - -int invSVD(int nn, FLOAT *A) -{ - /* *-------------------- inversion by svd - This calls lapack routine dgesvd -- - ON ENTRY - ** A = square matrix of size n x n -- dimensioned with - ** leading dimension = n - ON RETURN A contains the truncated SVD inverse of input matrix. - ** tolerance set for truncation is TOL and can be changed in - ** above define statement - **-------------------- - */ - int lWk, i, info; - - double *S, *Rwk; - FLOAT *U, *VT, *Wk; - REAL nrm; - FLOAT tmp, one=1.0, zero=0.0; - - double tol=TOL; - - lWk = 5*nn; - - U = (FLOAT *) malloc(nn*nn*sizeof(FLOAT)); - VT = (FLOAT *) malloc(nn*nn*sizeof(FLOAT)); - S = (double *) malloc(nn*sizeof(double)); - Rwk = (double *) malloc(5*nn*sizeof(double)); - Wk = (FLOAT *) malloc(lWk*sizeof(FLOAT)); - - if (U == NULL || VT == NULL || S == NULL || Wk == NULL) - return -1; - /*-------------------- trivial case nn = 1 */ - if (nn == 1) { - if(ABS_VALUE(A[0]-DBL_EPSILON) <= DBL_EPSILON*ABS_VALUE(A[0])) - return 1; - else { - A[0] = one / A[0]; - return 0; - } - } - /*-------------------- general case */ -#if defined(DBL_CMPLX) - zgesvd_ ("A","A", &nn, &nn, A, &nn, S, U, &nn, VT, &nn, Wk, &lWk, - Rwk, &info) ; -#else - dgesvd_ ("A","A", &nn, &nn, A, &nn, S, U, &nn, VT, &nn, Wk, &lWk, - &info) ; -#endif - - if(ABS_VALUE(S[0]-DBL_EPSILON) <= DBL_EPSILON*ABS_VALUE(S[0])) - return 1; - - nrm = S[0]*tol; - /*-------------------- compute S\inv * VT */ - for (i=0; i -#include - -/* Missing sys_timer for shared libraries */ -double sys_timer() { - clock_t t; - t = clock(); - return ((double)t) / CLOCKS_PER_SEC; -} diff --git a/lib/parms/src/FORTRAN/README b/lib/parms/src/FORTRAN/README deleted file mode 100755 index b13258a5a..000000000 --- a/lib/parms/src/FORTRAN/README +++ /dev/null @@ -1,25 +0,0 @@ -This directory contains fortran interfaces to the pARMS functions. -Any characters used in the FORTRAN source code will be changed to all -lowercase in object files for all architectures but Cray. So, the -interface functions to FORTRAN must be all lowercase (or all uppercase -for Cray). - -NOTE: - -The following functions: -parms_mapcreatefromlocal_ -parms_mapcreatefromglobal_ -parms_mapcreatefromptr_ -parms_mapcreatefromdist_ -parms_mapcreatefrompetsc_ - -all have an MPI_Comm argument as part of their argument list. In fortran, this -argument is not used. It is merely included to make the call consistent with the -calling function in C. The default communicator used in this case is the -MPI_COMM_WORLD communicator. This is to bypass a glitch in passing the MPI -communicator from a fortran environment, to a C environment. Hence the fortran -user will have no choice but to have the map object defined over the entire domain. - -In C however, a different communicator may be used without any compatibility issues. - - diff --git a/lib/parms/src/FORTRAN/parms_mapf.c b/lib/parms/src/FORTRAN/parms_mapf.c deleted file mode 100755 index 7c09d2229..000000000 --- a/lib/parms/src/FORTRAN/parms_mapf.c +++ /dev/null @@ -1,114 +0,0 @@ -#include "parms_map.h" - -#if defined(FORTRAN_CAPS) -#define parms_mapcreatefromlocal_ PARMS_MAPCREATEFROMLOCAL -#define parms_mapcreatefromglobal_ PARMS_MAPCREATEFROMGLOBAL -#define parms_mapcreatefromdist_ PARMS_MAPCREATEFROMDIST -#define parms_mapcreatefrompetsc_ PARMS_MAPCREATEFROMPETSC -#define parms_mapcreatefromptr_ PARMS_MAPCREATEFROMPTR -#define parms_mapfree_ PARMS_MAPFREE -#define parms_mapgetglobalsize_ PARMS_MAPGETGLOBALSIZE -#define parms_mapgetlocalsize_ PARMS_MAPGETLOCALSIZE -#define parms_mapgetnumprocs_ PARMS_MAPGETNUMPROCS -#define parms_mapgetpid_ PARMS_MAPGETPID -#define parms_mapview_ PARMS_MAPVIEW -#define parms_mapgetglobalindices_ PARMS_MAPGETGLOBALINDICES_ -#elif defined(FORTRAN_DOUBLE_UNDERSCORE) -#define parms_mapcreatefromlocal_ parms_mapcreatefromlocal__ -#define parms_mapcreatefromglobal_ parms_mapcreatefromglobal__ -#define parms_mapcreatefromdist_ parms_mapcreatefromdist__ -#define parms_mapcreatefrompetsc_ parms_mapcreatefrompetsc__ -#define parms_mapcreatefromptr_ parms_mapcreatefromptr__ -#define parms_mapfree_ parms_mapfree __ -#define parms_mapgetglobalsize_ parms_mapgetglobalsize__ -#define parms_mapgetlocalsize_ parms_mapgetlocalsize__ -#define parms_mapgetnumprocs_ parms_mapgetnumprocs__ -#define parms_mapgetpid_ parms_mapgetpid__ -#define parms_mapview_ parms_mapview__ -#define parms_mapgetglobalindices_ parms_mapgetglobalindices__ -#elif !defined(FORTRAN_UNDERSCORE) -#define parms_mapcreatefromlocal_ parms_mapcreatefromlocal -#define parms_mapcreatefromglobal_ parms_mapcreatefromglobal -#define parms_mapcreatefromdist_ parms_mapcreatefromdist -#define parms_mapcreatefrompetsc_ parms_mapcreatefrompetsc -#define parms_mapcreatefromptr_ parms_mapcreatefromptr -#define parms_mapfree_ parms_mapfree -#define parms_mapgetglobalsize_ parms_mapgetglobalsize -#define parms_mapgetlocalsize_ parms_mapgetlocalsize -#define parms_mapgetnumprocs_ parms_mapgetnumprocs -#define parms_mapgetpid_ parms_mapgetpid -#define parms_mapview_ parms_mapview -#define parms_mapgetglobalindices_ parms_mapgetglobalindices -#endif - -void parms_mapcreatefromlocal_(parms_Map *self, int *gsize, int - *offset, int *ierr) -{ - *ierr = parms_MapCreateFromLocal(self, *gsize, *offset); -} - -void parms_mapcreatefromglobal_(parms_Map *self, int *gsize, int - *npar, MPI_Comm *comm, int *offset, - int *dof, VARSTYPE *vtype, int *ierr) -{ - *ierr = parms_MapCreateFromGlobal(self, *gsize, npar, MPI_COMM_WORLD, - *offset, *dof, *vtype); -} - -void parms_mapcreatefromdist_(parms_Map *self, int *vtxdist, int - *part, MPI_Comm *comm, int *offset, int - *dof, VARSTYPE *vtype, int *ierr) -{ - *ierr = parms_MapCreateFromDist(self, vtxdist, part, MPI_COMM_WORLD, *offset, - *dof, *vtype); -} - -void parms_mapcreatefrompetsc_(parms_Map *self, int *m, int *M, - MPI_Comm *comm, int *ierr) -{ - *ierr = parms_MapCreateFromPetsc(self, *m, *M, MPI_COMM_WORLD); -} - -void parms_mapcreatefromptr_(parms_Map *self, int *gsize, int *nodes, - int *p2nodes, MPI_Comm *comm, int *dof, - VARSTYPE *vtype, int *ierr) -{ - *ierr = parms_MapCreateFromPtr(self, *gsize, nodes, p2nodes, MPI_COMM_WORLD, - *dof, *vtype); -} - -void parms_mapfree_(parms_Map *self, int *ierr) -{ - *ierr = parms_MapFree(self); -} - -void parms_mapgetglobalsize_(parms_Map *self, int *gsize) -{ - *gsize = parms_MapGetGlobalSize(*self); -} - -void parms_mapgetlocalsize_(parms_Map *self, int *lsize) -{ - *lsize = parms_MapGetLocalSize(*self); -} - -void parms_mapgetnumprocs_(parms_Map *self, int *numpro) -{ - *numpro = parms_MapGetNumProcs(*self); -} - -void parms_mapgetpid_(parms_Map *self, int *pid) -{ - *pid = parms_MapGetPid(*self); -} - -void parms_mapview_(parms_Map *self, parms_Viewer *v, int *ierr) -{ - *ierr = parms_MapView(*self, *v); -} - -void parms_mapgetglobalindices_(parms_Map *self, int *im, int *ierr) -{ - *ierr = parms_MapGetGlobalIndices(*self, im); -} - diff --git a/lib/parms/src/FORTRAN/parms_matf.c b/lib/parms/src/FORTRAN/parms_matf.c deleted file mode 100755 index a1ea88e8d..000000000 --- a/lib/parms/src/FORTRAN/parms_matf.c +++ /dev/null @@ -1,119 +0,0 @@ -#include "parms_mat.h" - -#if defined(FORTRAN_CAPS) -#define parms_matvec_ PARMS_MATVEC -#define parms_matcreate_ PARMS_MATCREATE -#define parms_matfree_ PARMS_MATFREE -#define parms_matmvpy_ PARMS_MATMVPY -#define parms_matsetcommtype_ PARMS_MATSETCOMMTYPE -#define parms_matsetvalues_ PARMS_MATSETVALUES -#define parms_matsetelementmatrix_ PARMS_MATSETELEMENTMATRIX -#define parms_matassembleelementmatrix_ PARMS_MATASSEMBLEELEMENTMATRIX -#define parms_matresetrowvalues_ PARMS_MATRESETROWVALUES -#define parms_matreset_ PARMS_MATRESET -#define parms_matsetup_ PARMS_MATSETUP -#define parms_matview_ PARMS_MATVIEW -#define parms_matviewcoo_ PARMS_MATVIEWCOO -#elif defined(FORTRAN_DOUBLE_UNDERSCORE) -#define parms_matvec_ parms_matvec__ -#define parms_matcreate_ parms_matcreate__ -#define parms_matfree_ parms_matfree__ -#define parms_matmvpy_ parms_matmvpy__ -#define parms_matsetcommtype_ parms_matsetcommtype__ -#define parms_matsetvalues_ parms_matsetvalues__ -#define parms_matsetelementmatrix_ parms_matsetelementmatrix__ -#define parms_matassembleelementmatrix_ parms_matassembleelementmatrix__ -#define parms_matresetrowvalues_ parms_matresetrowvalues__ -#define parms_matreset_ parms_matreset__ -#define parms_matsetup_ parms_matsetup__ -#define parms_matview_ parms_matview__ -#define parms_matviewcoo_ parms_matviewcoo__ -#elif !defined(FORTRAN_UNDERSCORE) -#define parms_matvec_ parms_matvec -#define parms_matcreate_ parms_matcreate -#define parms_matfree_ parms_matfree -#define parms_matmvpy_ parms_matmvpy -#define parms_matsetcommtype_ parms_matsetcommtype -#define parms_matsetvalues_ parms_matsetvalues -#define parms_matsetelementmatrix_ parms_matsetelementmatrix -#define parms_matassembleelementmatrix_ parms_matassembleelementmatrix -#define parms_matresetrowvalues_ parms_matresetrowvalues -#define parms_matreset_ parms_matreset -#define parms_matsetup_ parms_matsetup -#define parms_matview_ parms_matview -#define parms_matviewcoo_ parms_matviewcoo -#endif - -void parms_matvec_(parms_Mat *self, FLOAT *x, FLOAT *y, int - *ierr) -{ - *ierr = parms_MatVec(*self, x, y); -} - -void parms_matcreate_(parms_Mat *self, parms_Map *map, int *ierr) -{ - *ierr = parms_MatCreate(self, *map); -} - -void parms_matfree_(parms_Mat *self, int *ierr) -{ - *ierr = parms_MatFree(self); -} - -void parms_matmvpy_(parms_Mat *self, FLOAT *alpha, FLOAT *x, FLOAT - *beta, FLOAT *y, FLOAT *z, int *ierr) -{ - *ierr = parms_MatMVPY(*self, *alpha, x, *beta, y, z); -} - -void parms_matsetcommtype_(parms_Mat *self, COMMTYPE *ctype, int - *ierr) -{ - *ierr = parms_MatSetCommType(*self, *ctype); -} - -void parms_matsetvalues_(parms_Mat *self, int *m, int *im, int *ia, - int *ja, FLOAT *values, INSERTMODE *mode, int - *ierr) -{ - *ierr = parms_MatSetValues(*self, *m, im, ia, ja, values, *mode); -} - -void parms_matsetelementmatrix_(parms_Mat *self, int *m, int *im, int *ia, - int *ja, FLOAT *values, INSERTMODE *mode, int - *ierr) -{ - *ierr = parms_MatSetElementMatrix(*self, *m, im, ia, ja, values, *mode); -} - -void parms_matassembleelementmatrix_(parms_Mat *self, int *ierr) -{ - *ierr = parms_MatAssembleElementMatrix(*self); -} - -void parms_matresetrowvalues_(parms_Mat *self, int *m, int *im, int *ia, - int *ja, FLOAT *values, int *ierr) -{ - *ierr = parms_MatResetRowValues(*self, *m, im, ia, ja, values); -} - -void parms_matreset_(parms_Mat *self, NNZSTRUCT *nonzerostructure, int *ierr) -{ - *ierr = parms_MatReset(*self, *nonzerostructure); -} - -void parms_matsetup_(parms_Mat *self, int *ierr) -{ - *ierr = parms_MatSetup(*self); -} - -void parms_matview_(parms_Mat *self, parms_Viewer *v, int *ierr) -{ - *ierr = parms_MatView(*self, *v); -} - -void parms_matviewcoo_(parms_Mat *self, parms_Viewer *v, int *ierr) -{ - *ierr = parms_MatViewCOO(*self, *v); -} - diff --git a/lib/parms/src/FORTRAN/parms_pcf.c b/lib/parms/src/FORTRAN/parms_pcf.c deleted file mode 100755 index 9140fa802..000000000 --- a/lib/parms/src/FORTRAN/parms_pcf.c +++ /dev/null @@ -1,217 +0,0 @@ -#include "parms_mem.h" -#include "parms_pc.h" - -#if defined(FORTRAN_CAPS) -#define parms_pccreate_ PARMS_PCCREATE -#define parms_pcfree_ PARMS_PCFREE -#define parms_pcgetratio_ PARMS_PCGETRATIO -#define parms_pcsetbsize_ PARMS_PCSETBSIZE -#define parms_pcsetfill_ PARMS_PCSETFILL -#define parms_pcsetilutype_ PARMS_PCSETILUTYPE -#define parms_pcsetinnereps_ PARMS_PCSETINNEREPS -#define parms_pcsetinnerksize_ PARMS_PCSETINNERKSIZE -#define parms_pcsetinnermaxits_ PARMS_PCSETINNERMAXITS -#define parms_pcsetnlevels_ PARMS_PCSETNLEVELS -#define parms_pcsetparams_ PARMS_PCSETPARAMS -#define parms_pcsettol_ PARMS_PCSETTOL -#define parms_pcsettolind_ PARMS_PCSETTOLIND -#define parms_pcsettype_ PARMS_PCSETTYPE -#define parms_pcsetup_ PARMS_PCSETUP -#define parms_pcsolve_ PARMS_PCSOLVE -#define parms_pcview_ PARMS_PCVIEW -#define parms_pcgetname_ PARMS_PCGETNAME -#define parms_pcilugetname_ PARMS_PCILUGETNAME -#define parms_pccreateabstract_ PARMS_PCCREATEABSTRACT -#define parms_pcsetop_ PARMS_PCSETOP -#define parms_pcsetpermscaloptions_ PARMS_PCSETPERMSCALOPTIONS -#define parms_pcsetpermtype_ PARMS_PCSETPERMTYPE -#elif defined(FORTRAN_DOUBLE_UNDERSCORE) -#define parms_pccreate_ parms_pccreate__ -#define parms_pcfree_ parms_pcfree__ -#define parms_pcgetratio_ parms_pcgetratio__ -#define parms_pcsetbsize_ parms_pcsetbsize__ -#define parms_pcsetfill_ parms_pcsetfill__ -#define parms_pcsetilutype_ parms_pcsetilutype__ -#define parms_pcsetinnereps_ parms_pcsetinnereps__ -#define parms_pcsetinnerksize_ parms_pcsetinnerksize__ -#define parms_pcsetinnermaxits_ parms_pcsetinnermaxits__ -#define parms_pcsetnlevels_ parms_pcsetnlevels__ -#define parms_pcsetparams_ parms_pcsetparams__ -#define parms_pcsettol_ parms_pcsettol__ -#define parms_pcsettolind_ parms_pcsettolind__ -#define parms_pcsettype_ parms_pcsettype__ -#define parms_pcsetup_ parms_pcsetup__ -#define parms_pcsolve_ parms_pcsolve__ -#define parms_pcview_ parms_pcview__ -#define parms_pcgetname_ parms_pcgetname__ -#define parms_pcilugetname_ parms_pcilugetname__ -#define parms_pccreateabstract_ parms_pccreateabstract__ -#define parms_pcsetop_ parms_pcsetop__ -#define parms_pcsetpermscaloptions_ parms_pcsetpermscaloptions__ -#define parms_pcsetpermtype_ parms_pcsetpermtype__ -#elif !defined(FORTRAN_UNDERSCORE) -#define parms_pccreate_ parms_pccreate -#define parms_pcfree_ parms_pcfree -#define parms_pcgetratio_ parms_pcgetratio -#define parms_pcsetbsize_ parms_pcsetbsize -#define parms_pcsetfill_ parms_pcsetfill -#define parms_pcsetilutype_ parms_pcsetilutype -#define parms_pcsetinnereps_ parms_pcsetinnereps -#define parms_pcsetinnerksize_ parms_pcsetinnerksize -#define parms_pcsetinnermaxits_ parms_pcsetinnermaxits -#define parms_pcsetnlevels_ parms_pcsetnlevels -#define parms_pcsetparams_ parms_pcsetparams -#define parms_pcsettol_ parms_pcsettol -#define parms_pcsettolind_ parms_pcsettolind -#define parms_pcsettype_ parms_pcsettype -#define parms_pcsetup_ parms_pcsetup -#define parms_pcsolve_ parms_pcsolve -#define parms_pcview_ parms_pcview -#define parms_pcgetname_ parms_pcgetname -#define parms_pcilugetname_ parms_pcilugetname -#define parms_pccreateabstract_ parms_pccreateabstract -#define parms_pcsetop_ parms_pcsetop -#define parms_pcsetpermscaloptions_ parms_pcsetpermscaloptions -#define parms_pcsetpermtype_ parms_pcsetpermtype -#endif - -void parms_pccreate_(parms_PC *self, parms_Mat *A, int *ierr) -{ - *ierr = parms_PCCreate(self, *A); -} - -void parms_pcfree_(parms_PC *self, int *ierr) -{ - *ierr = parms_PCFree(self); -} - -void parms_pcgetratio_(parms_PC *self, double *ratio, int *ierr) -{ - *ierr = parms_PCGetRatio(*self, ratio); -} - -void parms_pcsetbsize_(parms_PC *self, int *bsize, int *ierr) -{ - *ierr = parms_PCSetBsize(*self, *bsize); -} - -void parms_pcsetfill_(parms_PC *self, int *fill, int *ierr) -{ - *ierr = parms_PCSetFill(*self, fill); -} - -void parms_pcsetilutype_(parms_PC *self, PCILUTYPE *pcstype, int - *ierr) -{ - *ierr = parms_PCSetILUType(*self, *pcstype); -} - -void parms_pcsetinnereps_(parms_PC *self, REAL *eps, int *ierr) -{ - *ierr = parms_PCSetInnerEps(*self, *eps); -} - -void parms_pcsetinnerksize_(parms_PC *self, int *im, int *ierr) -{ - *ierr = parms_PCSetInnerKSize(*self, *im); -} - -void parms_pcsetinnermaxits_(parms_PC *self, int *imax, int *ierr) -{ - *ierr = parms_PCSetInnerMaxits(*self, *imax); -} - -void parms_pcsetnlevels_(parms_PC *self, int *nlevel, int *ierr) -{ - *ierr = parms_PCSetNlevels(*self, *nlevel); -} - -void parms_pcsetparams_(parms_PC *self, int *nflags, char **params, - int *ierr) -{ - *ierr = parms_PCSetParams(*self, *nflags, params); -} - -void parms_pcsettol_(parms_PC *self, double *tol, int *ierr) -{ - *ierr = parms_PCSetTol(*self, tol); -} - -void parms_pcsettolind_(parms_PC *self, REAL *tolind, int *ierr) -{ - *ierr = parms_PCSetTolInd(*self, *tolind); -} - -void parms_pcsettype_(parms_PC *self, PCTYPE *pctype, int *ierr) -{ - *ierr = parms_PCSetType(*self, *pctype); -} - -void parms_pcsetup_(parms_PC *self, int *ierr) -{ - *ierr = parms_PCSetup(*self); -} - -void parms_pcsolve_(parms_PC *self, FLOAT *y, FLOAT *z, int *ierr) -{ - *ierr = parms_PCApply(*self, y, z); -} - -void parms_pcview_(parms_PC *self, parms_Viewer *v, int *ierr) -{ - parms_PCView(*self, *v); - *ierr = 0; -} - -void parms_pcgetname_(parms_PC *self, char *name, int *size, int - *ierr, int len) -{ - char *buf; - int i; - - parms_PCGetName(*self, &buf); - *size = strlen(buf); - PARMS_MEMCPY(name, buf, *size); - for (i = *size; i < len; i++) { - name[i] = ' '; - } - - *ierr = 0; -} - -void parms_pcilugetname_(parms_PC *self, char *name, int *size, int - *ierr, int len) -{ - char *buf; - int i; - - parms_PCILUGetName(*self, &buf); - *size = strlen(buf); - PARMS_MEMCPY(name, buf, *size); - for (i = *size; i < len; i++) { - name[i] = ' '; - } - *ierr = 0; -} - -void parms_pccreateabstract_(parms_PC *self, int *ierr) -{ - *ierr = parms_PCCreateAbstract(self); -} - -void parms_pcsetpermscaloptions_(parms_PC *self, int *meth, int - *flag, int *ierr) -{ - *ierr = parms_PCSetPermScalOptions(*self, meth, *flag); -} - -void parms_pcsetpermtype_(parms_PC *self, int *type, int *ierr) -{ - *ierr = parms_PCSetPermType(*self, *type); -} - -void parms_pcsetop_(parms_PC *self, parms_Mat *A, int *ierr) -{ - *ierr = parms_PCSetOP(*self, *A); -} - diff --git a/lib/parms/src/FORTRAN/parms_solverf.c b/lib/parms/src/FORTRAN/parms_solverf.c deleted file mode 100755 index 0031698d9..000000000 --- a/lib/parms/src/FORTRAN/parms_solverf.c +++ /dev/null @@ -1,105 +0,0 @@ -#include "parms_solver.h" - -#if defined(FORTRAN_CAPS) -#define parms_solverapply_ PARMS_SOLVERAPPLY -#define parms_solvercreate_ PARMS_SOLVERCREATE -#define parms_solverfree_ PARMS_SOLVERFREE -#define parms_solvergetits_ PARMS_SOLVERGETITS -#define parms_solvergetmat_ PARMS_SOLVERGETMAT -#define parms_solversetparam_ PARMS_SOLVERSETPARAM -#define parms_solversettype_ PARMS_SOLVERSETTYPE -#define parms_solvergetpc_ PARMS_SOLVERGETPC -#define parms_solvergetresidual_ PARMS_SOLVERGETRESIDUAL -#define parms_solvergetresidualnorm2_ PARMS_SOLVERGETRESIDUALNORM2 -#define parms_solverview_ PARMS_SOLVERVIEW -#elif defined(FORTRAN_DOUBLE_UNDERSCORE) -#define parms_solverapply_ parms_solverapply__ -#define parms_solvercreate_ parms_solvercreate__ -#define parms_solverfree_ parms_solverfree__ -#define parms_solvergetits_ parms_solvergetits__ -#define parms_solvergetmat_ parms_solvergetmat__ -#define parms_solversetparam_ parms_solversetparam__ -#define parms_solversettype_ parms_solversettype__ -#define parms_solvergetpc_ parms_solvergetpc__ -#define parms_solvergetresidual_ parms_solvergetresidual__ -#define parms_solvergetresidualnorm2_ parms_solvergetresidualnorm2__ -#define parms_solverview_ parms_solverview__ -#elif !defined(FORTRAN_UNDERSCORE) -#define parms_solverapply_ parms_solverapply -#define parms_solvercreate_ parms_solvercreate -#define parms_solverfree_ parms_solverfree -#define parms_solvergetits_ parms_solvergetits -#define parms_solvergetmat_ parms_solvergetmat -#define parms_solversetparam_ parms_solversetparam -#define parms_solversettype_ parms_solversettype -#define parms_solvergetpc_ parms_solvergetpc -#define parms_solvergetresidual_ parms_solvergetresidual -#define parms_solvergetresidualnorm2_ parms_solvergetresidualnorm2 -#define parms_solverview_ parms_solverview -#endif - -void parms_solverapply_(parms_Solver *self, FLOAT *y, FLOAT - *x, int *ierr) -{ - *ierr = parms_SolverApply(*self, y, x); -} - -void parms_solvercreate_(parms_Solver *self, parms_Mat *A, parms_PC - *pc, int *ierr) -{ - *ierr = parms_SolverCreate(self, *A, *pc); -} - -void parms_solverfree_(parms_Solver *self, int *ierr) -{ - *ierr = parms_SolverFree(self); -} - -void parms_solvergetits_(parms_Solver *self, int *its, int *ierr) -{ - *its = parms_SolverGetIts(*self); - *ierr = 0; -} - -void parms_solvergetmat_(parms_Solver *self, parms_Mat *A, int *ierr) -{ - *ierr = parms_SolverGetMat(*self, A); -} - -void parms_solversetparam_(parms_Solver *self, PARAMTYPE *ptype, char - *param, int *ierr) -{ - parms_SolverSetParam(*self, *ptype, param); - *ierr = 0; -} - -void parms_solversettype_(parms_Solver *self, SOLVERTYPE *stype, int - *ierr) -{ - *ierr = parms_SolverSetType(*self, *stype); -} - -void parms_solvergetpc_(parms_Solver *self, parms_PC *PC, int - *ierr) -{ - *ierr = parms_SolverGetPC(*self, PC); -} - -void parms_solvergetresidual_(parms_Solver *self, FLOAT *y, FLOAT *x, - FLOAT *r, int *ierr) -{ - *ierr = parms_SolverGetResidual(*self, y, x, r); -} - -void parms_solvergetresidualnorm2_(parms_Solver *self, FLOAT *y, FLOAT *x, - REAL *rnorm, int *ierr) -{ - *ierr = parms_SolverGetResidualNorm2(*self, y, x, rnorm); -} - -void parms_solverview_(parms_Solver *self, parms_Viewer *v, int - *ierr) -{ - *ierr = parms_SolverView(*self, *v); -} - diff --git a/lib/parms/src/FORTRAN/parms_timerf.c b/lib/parms/src/FORTRAN/parms_timerf.c deleted file mode 100755 index ec450243a..000000000 --- a/lib/parms/src/FORTRAN/parms_timerf.c +++ /dev/null @@ -1,74 +0,0 @@ -#include "parms_timer.h" - -#if defined(FORTRAN_CAPS) -#define parms_timercreate_ PARMS_TIMERCREATE -#define parms_timerfree_ PARMS_TIMERFREE -#define parms_timerget_ PARMS_TIMERGET -#define parms_timerpause_ PARMS_TIMERPAUSE -#define parms_timerreset_ PARMS_TIMERRESET -#define parms_timerresetdelay_ PARMS_TIMERRESETDELAY -#define parms_timerrestart_ PARMS_TIMERRESTART -#define parms_timerview_ PARMS_TIMERVIEW -#elif defined(FORTRAN_DOUBLE_UNDERSCORE) -#define parms_timercreate_ parms_timercreate__ -#define parms_timerfree_ parms_timerfree__ -#define parms_timerget_ parms_timerget__ -#define parms_timerpause_ parms_timerpause__ -#define parms_timerreset_ parms_timerreset__ -#define parms_timerresetdelay_ parms_timerresetdelay__ -#define parms_timerrestart_ parms_timerrestart__ -#define parms_timerview_ parms_timerview__ -#elif !defined(FORTRAN_UNDERSCORE) -#define parms_timercreate_ parms_timercreate -#define parms_timerfree_ parms_timerfree -#define parms_timerget_ parms_timerget -#define parms_timerpause_ parms_timerpause -#define parms_timerreset_ parms_timerreset -#define parms_timerresetdelay_ parms_timerresetdelay -#define parms_timerrestart_ parms_timerrestart -#define parms_timerview_ parms_timerview -#endif - -void parms_timercreate_(parms_Timer *self, int *ierr) -{ - parms_TimerCreate(self); - *ierr = 0; -} - -void parms_timerfree_(parms_Timer *self, int *ierr) -{ - *ierr = parms_TimerFree(self); -} - -void parms_timerget_(parms_Timer *self, double *t, int *ierr) -{ - *t = parms_TimerGet(*self); - *ierr = 0; -} - -void parms_timerpause_(parms_Timer *self, int *ierr) -{ - *ierr = parms_TimerPause(*self); -} - -void parms_timerreset_(parms_Timer *self, int *ierr) -{ - *ierr = parms_TimerReset(*self); -} - -void parms_timerresetdelay_(parms_Timer *self, double *delay, int - *ierr) -{ - parms_TimerResetDelay(*self, *delay); - *ierr = 0; -} - -void parms_timerrestart_(parms_Timer *self, int *ierr) -{ - *ierr = parms_TimerRestart(*self); -} - -void parms_timerview_(parms_Timer *self, parms_Viewer *v, int *ierr) -{ - *ierr = parms_TimerView(*self, *v); -} diff --git a/lib/parms/src/FORTRAN/parms_vecf.c b/lib/parms/src/FORTRAN/parms_vecf.c deleted file mode 100755 index 0938c9218..000000000 --- a/lib/parms/src/FORTRAN/parms_vecf.c +++ /dev/null @@ -1,134 +0,0 @@ -#include "parms_vec.h" - -#if defined(FORTRAN_CAPS) -#define parms_vecaxpy_ PARMS_VECAXPY -#define parms_vecaypx_ PARMS_VECAYPX -#define parms_vecdot_ PARMS_VECDOT -#define parms_vecdotc_ PARMS_VECDOTC -#define parms_vecdotarray_ PARMS_VECDOTARRAY -#define parms_vecgetnorm2_ PARMS_VECGETNORM2 -#define parms_vecscale_ PARMS_VECSCALE -#define parms_vecsetvalues_ PARMS_VECSETVALUES -#define parms_vecsetelementvector_ PARMS_VECSETELEMENTVECTOR -#define parms_vecassembleelementvector_ PARMS_VECASSEMBLEELEMENTVECTOR -#define parms_vecperm_ PARMS_VECPERM -#define parms_vecinvperm_ PARMS_VECINVPERM -#define parms_vecinvpermaux_ PARMS_VECINVPERMAUX -#define parms_vecpermaux_ PARMS_VECPERMAUX -#define parms_vecgather_ PARMS_VECGATHER -#elif defined(FORTRAN_DOUBLE_UNDERSCORE) -#define parms_vecaxpy_ parms_vecaxpy__ -#define parms_vecaypx_ parms_vecaypx__ -#define parms_vecdot_ parms_vecdot__ -#define parms_vecdotc_ parms_vecdotc__ -#define parms_vecdotarray_ parms_vecdotarray__ -#define parms_vecgetnorm2_ parms_vecgetnorm2__ -#define parms_vecscale_ parms_vecscale__ -#define parms_vecsetvalues_ parms_vecsetvalues__ -#define parms_vecsetelementvector_ parms_vecsetelementvector__ -#define parms_vecassembleelementvector_ parms_vecassembleelementvector__ -#define parms_vecperm_ parms_vecperm__ -#define parms_vecinvperm_ parms_vecinvperm__ -#define parms_vecinvpermaux_ parms_vecinvpermaux__ -#define parms_vecpermaux_ parms_vecpermaux__ -#define parms_vecgather_ parms_vecgather__ -#elif !defined(FORTRAN_UNDERSCORE) -#define parms_vecaxpy_ parms_vecaxpy -#define parms_vecaypx_ parms_vecaypx -#define parms_vecdot_ parms_vecdot -#define parms_vecdotc_ parms_vecdotc -#define parms_vecdotarray_ parms_vecdotarray -#define parms_vecgetnorm2_ parms_vecgetnorm2 -#define parms_vecscale_ parms_vecscale -#define parms_vecsetvalues_ parms_vecsetvalues -#define parms_vecsetelementvector_ parms_vecsetelementvector -#define parms_vecsassembleelementvector_ parms_vecassembleelementvector -#define parms_vecperm_ parms_vecperm -#define parms_vecinvperm_ parms_vecinvperm -#define parms_vecinvpermaux_ parms_vecinvpermaux -#define parms_vecpermaux_ parms_vecpermaux -#define parms_vecgather_ parms_vecgather -#endif - -void parms_vecaxpy_(FLOAT *self, FLOAT *x, FLOAT *scalar, parms_Map *map, int - *ierr) -{ - *ierr = parms_VecAXPY(self, x, *scalar, *map); -} - -void parms_vecaypx_(FLOAT *self, FLOAT *x, FLOAT *scalar, parms_Map *map, int - *ierr) -{ - *ierr = parms_VecAYPX(self, x, *scalar, *map); -} - - -void parms_vecdot_(FLOAT *self, FLOAT *x, FLOAT *value, parms_Map *map, int - *ierr) -{ - *ierr = parms_VecDOT(self, x, value, *map); -} - -void parms_vecdotc_(FLOAT *self, FLOAT *x, REAL *value, parms_Map *map, int - *ierr) -{ - *ierr = parms_VecDOTC(self, x, value, *map); -} - -void parms_vecdotarray_(FLOAT *self, int *n, FLOAT **vecarray, - FLOAT *result, parms_Map *map, int *ierr) -{ - *ierr = parms_VecDotArray(self, *n, vecarray, result, *map); -} - -void parms_vecgetnorm2_(FLOAT *self, REAL *value, parms_Map *map, int *ierr) -{ - *ierr = parms_VecGetNorm2(self, value, *map); -} - -void parms_vecscale_(FLOAT *self, FLOAT *scalar, parms_Map *map, int *ierr) -{ - *ierr = parms_VecScale(self, *scalar, *map); -} - -void parms_vecsetvalues_(FLOAT *self, int *m, int *im, FLOAT *values, INSERTMODE *mode, parms_Map *map, int *ierr) -{ - *ierr = parms_VecSetValues(self, *m, im, values, *mode, *map) ; -} - -void parms_vecsetelementvector_(FLOAT *self, int *m, int *im, FLOAT *values, INSERTMODE *mode, parms_Map *map, int *ierr) -{ - *ierr = parms_VecSetElementVector(self, *m, im, values, *mode, *map) ; -} - -void parms_vecassembleelementvector_(FLOAT *self, parms_Map *map, int *ierr) -{ - *ierr = parms_VecAssembleElementVector(self, *map) ; -} - -void parms_vecperm_(FLOAT *self, parms_Map *map, int *ierr) -{ - *ierr = parms_VecPerm(self, *map); -} - -void parms_vecinvperm_(FLOAT *self, parms_Map *map, int *ierr) -{ - *ierr = parms_VecInvPerm(self, *map); -} - -void parms_vecpermaux_(FLOAT *self, FLOAT *aux, parms_Map *map, int *ierr) -{ - *ierr = parms_VecPermAux(self, aux, *map); -} - -void parms_vecinvpermaux_(FLOAT *self, FLOAT *aux, parms_Map *map, int *ierr) -{ - *ierr = parms_VecInvPermAux(self, aux, *map); -} - -void parms_vecgather_(FLOAT *self, FLOAT *ga, parms_Map *map, int *ierr) -{ - *ierr = parms_VecGather(self, ga, *map); -} - - diff --git a/lib/parms/src/FORTRAN/parms_viewerf.c b/lib/parms/src/FORTRAN/parms_viewerf.c deleted file mode 100755 index def02ac09..000000000 --- a/lib/parms/src/FORTRAN/parms_viewerf.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "parms_mem.h" -#include "parms_viewer.h" - -#if defined(FORTRAN_CAPS) -#define parms_viewercreate_ PARMS_VIEWERCREATE -#define parms_viewerfree_ PARMS_VIEWERFREE -#define parms_viewergetfp_ PARMS_VIEWERGETFP -#define parms_viewergetfname_ PARMS_VIEWERGETFNAME -#define parms_viewerstorefp_ PARMS_VIEWERSTOREFP -#elif defined(FORTRAN_DOUBLE_UNDERSCORE) -#define parms_viewercreate_ parms_viewercreate__ -#define parms_viewerfree_ parms_viewerfree__ -#define parms_viewergetfp_ parms_viewergetfp__ -#define parms_viewergetfname_ parms_viewergetfname__ -#define parms_viewerstorefp_ parms_viewerstorefp__ -#elif !defined(FORTRAN_UNDERSCORE) -#define parms_viewercreate_ parms_viewercreate -#define parms_viewerfree_ parms_viewerfree -#define parms_viewergetfp_ parms_viewergetfp -#define parms_viewergetfname_ parms_viewergetfname -#define parms_viewerstorefp_ parms_viewerstorefp -#endif - -void parms_viewercreate_(parms_Viewer *self, char *fname, int *ierr, - int len) -{ - char *buf; - - PARMS_NEWARRAY(buf, len+1); - PARMS_MEMCPY(buf, fname, len); - buf[len] = '\0'; - *ierr = parms_ViewerCreate(self, buf); - PARMS_FREE(buf); -} - -void parms_viewerfree_(parms_Viewer *self, int *ierr) -{ - *ierr = parms_ViewerFree(self); -} - -void parms_viewergetfp_(parms_Viewer *self, FILE **fp, int *ierr) -{ - *ierr = parms_ViewerGetFP(*self, fp); -} - -void parms_viewergetfname_(parms_Viewer *self, char **fname, int - *ierr) -{ - *ierr = parms_ViewerGetFname(*self, fname); -} - -void parms_viewerstorefp_(parms_Viewer *self, FILE *fp, int *ierr) -{ - *ierr = parms_ViewerStoreFP(*self, fp); -} diff --git a/lib/parms/src/bicgstab.c b/lib/parms/src/bicgstab.c deleted file mode 100755 index ea97a793f..000000000 --- a/lib/parms/src/bicgstab.c +++ /dev/null @@ -1,313 +0,0 @@ -/*-------------------------------------------------------------------- - bicgstab_create : create the BICGS solver. - bicgstab_free : free the memory for the BICGS solver. - bicgstab_view : dump the BICGS solver. - parms_bicgstab : the BICGS solve function. - - $Id: fgmres.c,v 1.4 2006-12-15 07:02:07 zzli Exp $ - ------------------------------------------------------------------*/ - -#if defined(__ICC) -#include -#else -#if defined(C99) -#include -#else -#include -#endif -#endif -#include "parms_vec.h" -#include "parms_mat.h" -#include "parms_pc.h" -#include "./include/parms_solver_impl.h" - -#define DBL_EPSILON 2.2204460492503131e-16 // double epsilon - -typedef struct bicgs_data { - int neigs; -} *bicgstab_data; - -#define TINY 1.0e-20 -int parms_bicgstab(parms_Solver self, FLOAT *y, FLOAT *x) -{ - - int i, its; - int maxits, nloc, size, one = 1; - FLOAT alpha, t1, tmp[2]; - FLOAT *r0, *r, *p, *st, *v, *t, *pt; - REAL tol, rho, rho_alt, omega, sigma, beta; - MPI_Request req; - - parms_Mat A; - parms_PC pc; - parms_Map is; - parms_Viewer viewer; - - int rank; - MPI_Comm comm; - - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - - A = self->A; - pc = self->pc; - is = A->is; - - maxits = self->maxits; - tol = self->tol*self->tol; - nloc = parms_MapGetLocalSize(is); - comm = is->comm; - -/*----- allocate memory for working local arrays -------*/ - size = nloc; - /* PARMS_NEWARRAY(r0, size); */ - PARMS_NEWARRAY(r, size); - PARMS_NEWARRAY(r0, size); - PARMS_NEWARRAY(st, size); - PARMS_NEWARRAY(t, size); - PARMS_NEWARRAY(v, size); - PARMS_NEWARRAY(p, size); - PARMS_NEWARRAY(pt, size); - -/* --- first permute x and y for pARMS matrix structure ------*/ - if (is->isperm) { - for (i = 0; i < nloc; i++) { - t[is->perm[i]] = x[i]; - r0[is->perm[i]] = y[i]; - } - memcpy(x, t, nloc*sizeof(FLOAT)); - is->isvecperm = true; - } - - - - /* compute residual vector r = y - Ax */ - /* r0 = y (permuted) */ - parms_MatVec(A, x, r); - parms_VecAYPX(r, r0, -1.0, is); - - /* choose r0 (arbitrary), e.g., PARMS_MEMCPY(r0,r,nloc); or: */ - /* PARMS_MEMCPY(r0, y, nloc); */ - /* As r0 and y are never changed, we can simply set */ - /* r0=y; */ - /* Do this already above, use r0 as permuted y! */ - - tmp[0] = 0.; - tmp[1] = 0.; - for (i = 0; i < nloc; i++) - { - tmp[0] += r[i] * r[i]; // omega - tmp[1] += r0[i] * r[i]; // rho - } - if(is->isserial == false) - MPI_Iallreduce(MPI_IN_PLACE, tmp, 2, MPI_DOUBLE, MPI_SUM, comm, &req); - - - PARMS_MEMCPY(p, r, nloc); - alpha = 0.; - sigma = rho_alt = 1.0; - - if(is->isserial == false) MPI_Wait(&req, MPI_STATUS_IGNORE); - - omega = tmp[0]; - rho = tmp[1]; - - - if (omega > tol){ - for (its = 0; its < maxits; its++){ - - parms_PCApply(pc, p, pt); - parms_MatVec(A, pt, v); - parms_VecDOT(r0,v,&alpha,is); - - alpha = rho/alpha; - - for (i = 0; i < nloc; i++) r[i] -= alpha * v[i]; - - parms_PCApply(pc, r, st); - parms_MatVec(A, st, t); - - tmp[0] = 0.; - tmp[1] = 0.; - for (i = 0; i < nloc; i++) - { - tmp[0] += t[i] * r[i]; - tmp[1] += t[i] * t[i]; - } - - - if(is->isserial == false) - MPI_Iallreduce(MPI_IN_PLACE, tmp, 2, MPI_DOUBLE, MPI_SUM, comm, &req); - - for (i = 0; i < nloc; i++) x[i] += alpha * pt[i]; - - if(is->isserial == false) MPI_Wait(&req, MPI_STATUS_IGNORE); - - sigma = tmp[0]/tmp[1]; - - if(ABS_VALUE(sigma) < TINY){ - if(rank == 0) - printf("ERROR: sigma = 0\n"); - break; - } - - for (i = 0; i < nloc; i++) r[i] -= sigma * t[i]; - - tmp[0] = 0.; - tmp[1] = 0.; - for (i = 0; i < nloc; i++) - { - tmp[0] += r[i] * r[i]; // omega - tmp[1] += r0[i] * r[i]; // rho - } - - if(is->isserial == false) - MPI_Iallreduce(MPI_IN_PLACE, tmp, 2, MPI_DOUBLE, MPI_SUM, comm, &req); - - for (i = 0; i < nloc; i++) x[i] += sigma * st[i]; - - - if(is->isserial == false) MPI_Wait(&req, MPI_STATUS_IGNORE); - - omega = tmp[0]; - - if (omega < tol) break; - - rho = tmp[1]; - - beta = (rho * alpha) / (rho_alt * sigma); - rho_alt = rho; - - for (i = 0; i < nloc; i++) - { - p[i] = r[i] + beta*(p[i] - sigma* v[i]); - } - } - } - - - its++; - - if(its == maxits && omega > tol) - printf("ERROR: no convergence\n"); - - /* reset isvecperm and do inverse permutation*/ - if (is->isperm) { - for (i = 0; i < nloc; i++) { - r[is->iperm[i]] = x[i]; - } - memcpy(x, r, nloc*sizeof(FLOAT)); - is->isvecperm = false; - } - free(r0); - free(r); - free(p); - free(st); - free(t); - free(v); - free(pt); - - - self->its = its; - return 0; -} - -static int bicgstab_getresidual(parms_Solver self, FLOAT *y, FLOAT *x, FLOAT *res) -{ - - parms_Mat A; - - A = self->A; - - parms_MatMVPY(A, -1.0, x, 1.0, y, res); - - return 0; -} - -static int bicgstab_getresidualnorm2(parms_Solver self, FLOAT *y, FLOAT *x, REAL *rnorm) -{ - - int nloc; - FLOAT *res; - parms_Mat A; - parms_Map is; - - A = self->A; - is = A->is; - nloc = parms_MapGetLocalSize(is); - PARMS_NEWARRAY(res, nloc); - - parms_MatMVPY(A, -1.0, x, 1.0, y, res); - - parms_VecGetNorm2(res, rnorm, is); - - PARMS_FREE(res); - - return 0; -} - - -static int bicgstab_free(parms_Solver *self) -{ - /*dummy*/ - return 0; -} - -static int bicgstab_view(parms_Solver self, parms_Viewer v) -{ - FILE *fp; - char *name, *iluname; - int dim; - parms_ViewerGetFP(v, &fp); - - fprintf(fp,"\n=============================\n"); - fprintf(fp," Solver Parameters \n"); - fprintf(fp,"=============================\n"); - - fprintf(fp, "Solver type = BiConjugate Gradient Stabilized Method (BICGS) \n"); - - fprintf(fp, "maxits = %d \n", self->maxits); - fprintf(fp, "Relative tolerance = %-8.2e \n", self->tol); - - parms_PCGetName(self->pc, &name); - parms_PCILUGetName(self->pc, &iluname); - fprintf(fp, "Global Preconditioner: %s\n", name); - fprintf(fp, "Local Preconditioner: %s\n", iluname); - - parms_ViewerGetFP(v, &fp); - - return 0; -} - -static int bicgstab_setksize(parms_Solver self, int restart) -{ - /*dummy*/ - return 0; -} - -static int bicgstab_setneig(parms_Solver self, int neigs) -{ - /*dummy*/ - return 0; -} - -static struct parms_Solver_ops parms_bicgstab_sol = { - parms_bicgstab, - bicgstab_getresidual, - bicgstab_getresidualnorm2, - bicgstab_setksize, - bicgstab_setneig, - bicgstab_free, - bicgstab_view -}; - - -/** Create the BICGS solver. - * - * \param self A parms_Solver object. - * \return 0 on success. - */ -int bicgstab_create(parms_Solver self) -{ - PARMS_MEMCPY(self->ops, &parms_bicgstab_sol, 1); - return 0; -} diff --git a/lib/parms/src/bicgstab_ras.c b/lib/parms/src/bicgstab_ras.c deleted file mode 100644 index 505494f4f..000000000 --- a/lib/parms/src/bicgstab_ras.c +++ /dev/null @@ -1,360 +0,0 @@ -/*-------------------------------------------------------------------- - bicgstabras_create : create the BICGS_RAS solver. - bicgstabras_free : free the memory for the BICGS_RAS solver. - bicgstabras_view : dump the BICGS_RAS solver. - parms_bicgstabras : the BICGS_RAS solve function. - - $Id: fgmres.c,v 1.4 2006-12-15 07:02:07 zzli Exp $ - ------------------------------------------------------------------*/ - -#if defined(__ICC) -#include -#else -#if defined(C99) -#include -#else -#include -#endif -#endif -#include "parms_vec.h" -#include "parms_mat.h" -#include "parms_pc.h" -#include "./include/parms_solver_impl.h" - -#include "./include/parms_comm_impl.h" -#include "./include/parms_pc_impl.h" -#include "./include/parms_opt_impl.h" - - -typedef struct ras_data { - parms_Operator op; - parms_Comm handler; - parms_Map is; - BOOL issetup; - FLOAT *rbuf; - int nloc; - int n; - int nodv; - int nsend; -} *ras_data; - - -#define DBL_EPSILON 2.2204460492503131e-16 // double epsilon - -typedef struct bicgs_data { - int neigs; -} *bicgstabras_data; - -#define TINY 1.0e-20 -int parms_bicgstabras(parms_Solver self, FLOAT *y, FLOAT *x) -{ - - int i, its; - int maxits, nloc, n_ext, size, one = 1; - FLOAT alpha, t1, tmp[2]; - FLOAT *r0, *v, *t; - FLOAT *r_ext, *st_ext, *p_ext, *pt_ext; - REAL tol, rho, rho_alt, omega, sigma, beta; - MPI_Request req; - int nodv, nsend; - - parms_Mat A; - parms_PC pc; - parms_Map is; - parms_Viewer viewer; - parms_Comm pc_handler; - ras_data pc_data; - - int rank; - MPI_Comm comm; - - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - - A = self->A; - pc = self->pc; - is = A->is; - - maxits = self->maxits; - tol = self->tol*self->tol; - nloc = parms_MapGetLocalSize(is); - comm = is->comm; - - // For inlined RAS preconditioner - pc_data = (ras_data)pc->data; - n_ext = pc_data->n; // local size with halo for Schwarz - pc_handler = pc_data->handler; - nsend = pc_data->nsend; - nodv = pc_data->nodv; - -/*----- allocate memory for working local arrays -------*/ - size = nloc; - /* PARMS_NEWARRAY(r0, size); */ - PARMS_NEWARRAY(r0, size); - PARMS_NEWARRAY(t, size); - PARMS_NEWARRAY(v, size); - - PARMS_NEWARRAY(r_ext, n_ext); - PARMS_NEWARRAY(p_ext, n_ext); - PARMS_NEWARRAY(pt_ext, n_ext); - PARMS_NEWARRAY(st_ext, n_ext); - - -/* --- first permute x and y for pARMS matrix structure ------*/ - if (is->isperm) { - for (i = 0; i < nloc; i++) { - t[is->perm[i]] = x[i]; - r0[is->perm[i]] = y[i]; - } - memcpy(x, t, nloc*sizeof(FLOAT)); - is->isvecperm = true; - } - - - - /* compute residual vector r = y - Ax */ - /* r0 = y (permuted) */ - parms_MatVec(A, x, r_ext); - parms_VecAYPX(r_ext, r0, -1.0, is); - - /* choose r0 (arbitrary), e.g., PARMS_MEMCPY(r0,r,nloc); or: */ - /* PARMS_MEMCPY(r0, y, nloc); */ - /* As r0 and y are never changed, we can simply set */ - /* r0=y; */ - /* Do this already above, use r0 as permuted y! */ - - tmp[0] = 0.; - tmp[1] = 0.; - for (i = 0; i < nloc; i++) - { - tmp[0] += r_ext[i] * r_ext[i]; // omega - tmp[1] += r0[i] * r_ext[i]; // rho - } - if(is->isserial == false) - MPI_Iallreduce(MPI_IN_PLACE, tmp, 2, MPI_DOUBLE, MPI_SUM, comm, &req); - - - PARMS_MEMCPY(p_ext, r_ext, nloc); - alpha = 0.; - sigma = rho_alt = 1.0; - - if(is->isserial == false) MPI_Wait(&req, MPI_STATUS_IGNORE); - - omega = tmp[0]; - rho = tmp[1]; - - - if (omega > tol){ - for (its = 0; its < maxits; its++){ - - // parms_PCApply(pc, p, pt); - if (nsend) parms_CommDataBegin(pc_handler, p_ext, 0); - if (nodv) parms_CommDataEnd(pc_handler); - if (pc_data->n - pc_data->nloc) - PARMS_MEMCPY(&p_ext[nloc], pc_data->rbuf, pc_data->n - pc_data->nloc); - - /* solve the extended linear system */ - parms_OperatorApply(pc_data->op, p_ext, pt_ext); - - /* No halo exchange of the solution, that's the "Restricted" in RAS */ - /* === End Preconditioner === */ - - parms_MatVec(A, pt_ext, v); - parms_VecDOT(r0,v,&alpha,is); - - alpha = rho/alpha; - - for (i = 0; i < nloc; i++) r_ext[i] -= alpha * v[i]; - - // parms_PCApply(pc, r, st); - if (nsend) parms_CommDataBegin(pc_handler, r_ext, 0); - if (nodv) parms_CommDataEnd(pc_handler); - if (pc_data->n - pc_data->nloc) - PARMS_MEMCPY(&r_ext[nloc], pc_data->rbuf, pc_data->n - pc_data->nloc); - - /* solve the extended linear system */ - parms_OperatorApply(pc_data->op, r_ext, st_ext); - - /* No halo exchange of the solution, that's the "Restricted" in RAS */ - /* === End Preconditioner === */ - parms_MatVec(A, st_ext, t); - - tmp[0] = 0.; - tmp[1] = 0.; - for (i = 0; i < nloc; i++) - { - tmp[0] += t[i] * r_ext[i]; - tmp[1] += t[i] * t[i]; - } - - - if(is->isserial == false) - MPI_Iallreduce(MPI_IN_PLACE, tmp, 2, MPI_DOUBLE, MPI_SUM, comm, &req); - - //for (i = 0; i < nloc; i++) x[i] += alpha * pt_ext[i]; - - if(is->isserial == false) MPI_Wait(&req, MPI_STATUS_IGNORE); - - sigma = tmp[0]/tmp[1]; - - - for (i = 0; i < nloc; i++) r_ext[i] -= sigma * t[i]; - - tmp[0] = 0.; - tmp[1] = 0.; - for (i = 0; i < nloc; i++) - { - tmp[0] += r_ext[i] * r_ext[i]; // omega - tmp[1] += r0[i] * r_ext[i]; // rho - } - - if(is->isserial == false) - MPI_Iallreduce(MPI_IN_PLACE, tmp, 2, MPI_DOUBLE, MPI_SUM, comm, &req); - - for (i = 0; i < nloc; i++) x[i] += alpha * pt_ext[i] + sigma * st_ext[i]; - - - if(is->isserial == false) MPI_Wait(&req, MPI_STATUS_IGNORE); - - omega = tmp[0]; - - if (omega < tol) break; - - rho = tmp[1]; - - beta = (rho * alpha) / (rho_alt * sigma); - rho_alt = rho; - - for (i = 0; i < nloc; i++) - { - p_ext[i] = r_ext[i] + beta*(p_ext[i] - sigma* v[i]); - } - } - } - - - its++; - - if(its == maxits && omega > tol) - printf("ERROR: no convergence\n"); - - /* reset isvecperm and do inverse permutation*/ - if (is->isperm) { - for (i = 0; i < nloc; i++) { - t[is->iperm[i]] = x[i]; - } - memcpy(x, t, nloc*sizeof(FLOAT)); - is->isvecperm = false; - } - free(r0); - free(r_ext); - free(p_ext); - free(st_ext); - free(t); - free(v); - free(pt_ext); - - - self->its = its; - return 0; -} - -static int bicgstabras_getresidual(parms_Solver self, FLOAT *y, FLOAT *x, FLOAT *res) -{ - - parms_Mat A; - - A = self->A; - - parms_MatMVPY(A, -1.0, x, 1.0, y, res); - - return 0; -} - -static int bicgstabras_getresidualnorm2(parms_Solver self, FLOAT *y, FLOAT *x, REAL *rnorm) -{ - - int nloc; - FLOAT *res; - parms_Mat A; - parms_Map is; - - A = self->A; - is = A->is; - nloc = parms_MapGetLocalSize(is); - PARMS_NEWARRAY(res, nloc); - - parms_MatMVPY(A, -1.0, x, 1.0, y, res); - - parms_VecGetNorm2(res, rnorm, is); - - PARMS_FREE(res); - - return 0; -} - - -static int bicgstabras_free(parms_Solver *self) -{ - /*dummy*/ - return 0; -} - -static int bicgstabras_view(parms_Solver self, parms_Viewer v) -{ - FILE *fp; - char *name, *iluname; - int dim; - parms_ViewerGetFP(v, &fp); - - fprintf(fp,"\n=============================\n"); - fprintf(fp," Solver Parameters \n"); - fprintf(fp,"=============================\n"); - - fprintf(fp, "Solver type = BiConjugate Gradient Stabilized Method (BICGS_RAS) \n"); - - fprintf(fp, "maxits = %d \n", self->maxits); - fprintf(fp, "Relative tolerance = %-8.2e \n", self->tol); - - parms_PCGetName(self->pc, &name); - parms_PCILUGetName(self->pc, &iluname); - fprintf(fp, "Global Preconditioner: %s\n", name); - fprintf(fp, "Local Preconditioner: %s\n", iluname); - - parms_ViewerGetFP(v, &fp); - - return 0; -} - -static int bicgstabras_setksize(parms_Solver self, int restart) -{ - /*dummy*/ - return 0; -} - -static int bicgstabras_setneig(parms_Solver self, int neigs) -{ - /*dummy*/ - return 0; -} - -static struct parms_Solver_ops parms_bicgstabras_sol = { - parms_bicgstabras, - bicgstabras_getresidual, - bicgstabras_getresidualnorm2, - bicgstabras_setksize, - bicgstabras_setneig, - bicgstabras_free, - bicgstabras_view -}; - - -/** Create the BICGS_RAS solver. - * - * \param self A parms_Solver object. - * \return 0 on success. - */ -int bicgstabras_create(parms_Solver self) -{ - PARMS_MEMCPY(self->ops, &parms_bicgstabras_sol, 1); - return 0; -} diff --git a/lib/parms/src/cg.c b/lib/parms/src/cg.c deleted file mode 100644 index 658267f54..000000000 --- a/lib/parms/src/cg.c +++ /dev/null @@ -1,256 +0,0 @@ -/*-------------------------------------------------------------------- - cg_create : create the CG solver. - cg_free : free the memory for the CG solver. - cg_view : dump the CG solver. - parms_cg : the CG solve function. - - $Id: fgmres.c,v 1.4 2006-12-15 07:02:07 zzli Exp $ - ------------------------------------------------------------------*/ - -#if defined(__ICC) -#include -#else -#if defined(C99) -#include -#else -#include -#endif -#endif -#include "parms_vec.h" -#include "parms_mat.h" -#include "parms_pc.h" -#include "./include/parms_solver_impl.h" - -#define DBL_EPSILON 2.2204460492503131e-16 // double epsilon - -typedef struct cg_data { - int neigs; -} *cg_data; - -#define TINY 1.0e-20 -int parms_cg(parms_Solver self, FLOAT *y, FLOAT *x) -{ - int i, its, j; - int maxits, nloc; - FLOAT alpha, tmp[2]; - FLOAT *r, *z, *p, *Ap; - REAL tol, omega, beta, r2; - - - parms_Mat A; - parms_PC pc; - parms_Map is; - parms_vcsr Acsr; - - MPI_Comm comm; - - - A = self->A; - pc = self->pc; - is = A->is; - - maxits = self->maxits; - tol = self->tol * self->tol; - nloc = parms_MapGetLocalSize(is); - comm = is->comm; - - -/*----- allocate memory for local working arrays -------*/ - - r = malloc(nloc*sizeof(FLOAT)); - z = malloc(nloc*sizeof(FLOAT)); - p = malloc(nloc*sizeof(FLOAT)); - Ap = malloc(nloc*sizeof(FLOAT)); - -/* --- first permute x and y for pARMS matrix structure ------*/ - if (is->isperm) { - for (i = 0; i < nloc; i++) { - z[is->perm[i]] = x[i]; - r[is->perm[i]] = y[i]; - } - memcpy(x, z, nloc*sizeof(FLOAT)); - memcpy(y, r, nloc*sizeof(FLOAT)); - is->isvecperm = true; - } - - omega = 2.*tol; - - /* compute residual vector r = y - Ax */ - parms_MatVec(A,x,r); - for (i = 0; i < nloc; i++) r[i] = y[i] - r[i]; - - parms_PCApply(pc,r,z); - - r2 = 0; - for (i = 0; i < nloc; i++){ - p[i] = z[i]; - r2 += r[i] * z[i]; - } - - if(is->isserial == false) - MPI_Allreduce(MPI_IN_PLACE, &r2, 1, MPI_DOUBLE, MPI_SUM, comm); - - - for (its = 1; its <= maxits; its++){ - - parms_MatVec(A,p,Ap); - - alpha = 0.; - for (i = 0; i < nloc; i++) alpha += p[i] * Ap[i]; - if(is->isserial == false) - MPI_Allreduce(MPI_IN_PLACE, &alpha, 1, MPI_DOUBLE, MPI_SUM, comm); - - alpha = r2 / alpha; - - for (i = 0; i < nloc; i++){ - x[i] += alpha * p[i]; - r[i] -= alpha * Ap[i]; - } - - parms_PCApply(pc, r, z); - - tmp[0] = 0.; - tmp[1] = 0.; - for (i = 0; i < nloc; i++){ - tmp[0] += r[i] * r[i]; /* omega */ - tmp[1] += r[i] * z[i]; - } - if(is->isserial == false) - MPI_Allreduce(MPI_IN_PLACE, tmp, 2, MPI_DOUBLE, MPI_SUM, comm); - - omega = tmp[0]; - /* Convergence criterion reached? */ - if (omega <= tol) break; - - beta = tmp[1] / r2; - r2 = tmp[1]; - - for (i = 0; i < nloc; i++){ - p[i] = z[i] + beta * p[i]; - } - } - - if(its == maxits && omega > tol) - printf("ERROR: no convergence\n"); - - /* reset isvecperm and do inverse permutation*/ - if (is->isperm) { - for (i = 0; i < nloc; i++) { - r[is->iperm[i]] = x[i]; - z[is->iperm[i]] = y[i]; - } - memcpy(x, r, nloc*sizeof(FLOAT)); - memcpy(y, z, nloc*sizeof(FLOAT)); - is->isvecperm = false; - } - - free(z); - free(r); - free(p); - free(Ap); - - - self->its = its; - return 0; -} - -static int cg_getresidual(parms_Solver self, FLOAT *y, FLOAT *x, FLOAT *res) -{ - - parms_Mat A; - - A = self->A; - - parms_MatMVPY(A, -1.0, x, 1.0, y, res); - - return 0; -} - -static int cg_getresidualnorm2(parms_Solver self, FLOAT *y, FLOAT *x, REAL *rnorm) -{ - - int nloc; - FLOAT *res; - parms_Mat A; - parms_Map is; - - A = self->A; - is = A->is; - nloc = parms_MapGetLocalSize(is); - PARMS_NEWARRAY(res, nloc); - - parms_MatMVPY(A, -1.0, x, 1.0, y, res); - - parms_VecGetNorm2(res, rnorm, is); - - PARMS_FREE(res); - - return 0; -} - - -static int cg_free(parms_Solver *self) -{ - /*dummy*/ - return 0; -} - -static int cg_view(parms_Solver self, parms_Viewer v) -{ - FILE *fp; - char *name, *iluname; - int dim; - parms_ViewerGetFP(v, &fp); - - fprintf(fp,"\n=============================\n"); - fprintf(fp," Solver Parameters \n"); - fprintf(fp,"=============================\n"); - - fprintf(fp, "Solver type = Conjugate Gradient Method (CG) \n"); - - fprintf(fp, "maxits = %d \n", self->maxits); - fprintf(fp, "Relative tolerance = %-8.2e \n", self->tol); - - parms_PCGetName(self->pc, &name); - parms_PCILUGetName(self->pc, &iluname); - fprintf(fp, "Global Preconditioner: %s\n", name); - fprintf(fp, "Local Preconditioner: %s\n", iluname); - - parms_ViewerGetFP(v, &fp); - - return 0; -} - -static int cg_setksize(parms_Solver self, int restart) -{ - /*dummy*/ - return 0; -} - -static int cg_setneig(parms_Solver self, int neigs) -{ - /*dummy*/ - return 0; -} - -static struct parms_Solver_ops parms_cg_sol = { - parms_cg, - cg_getresidual, - cg_getresidualnorm2, - cg_setksize, - cg_setneig, - cg_free, - cg_view -}; - - -/** Create the CG solver. - * - * \param self A parms_Solver object. - * \return 0 on success. - */ -int cg_create(parms_Solver self) -{ - PARMS_MEMCPY(self->ops, &parms_cg_sol, 1); - return 0; -} diff --git a/lib/parms/src/fgmres.c b/lib/parms/src/fgmres.c deleted file mode 100755 index c000a4d5f..000000000 --- a/lib/parms/src/fgmres.c +++ /dev/null @@ -1,423 +0,0 @@ -/*-------------------------------------------------------------------- - fgmres_create : create the FGMRES solver. - fgmres_free : free the memory for the FGMRES solver. - fgmres_setksize : set the restart size of the FGMRES solver. - fgmres_view : dump the FGMRES solver. - parms_fgmres : the FGMRES solve function. - - $Id: fgmres.c,v 1.4 2006-12-15 07:02:07 zzli Exp $ - ------------------------------------------------------------------*/ - -#if defined(__ICC) -#include -#else -#if defined(C99) -#include -#else -#include -#endif -#endif -#include "parms_vec.h" -#include "parms_mat.h" -#include "parms_pc.h" -#include "./include/parms_solver_impl.h" - -#define DBL_EPSILON 2.2204460492503131e-16 // double epsilon - -typedef struct fgmres_data { - int restart; - int neigs; -} *fgmres_data; - -#define TINY 1.0e-20 -int parms_fgmres(parms_Solver self, FLOAT *y, FLOAT *x) -{ - BOOL outflag, intflag; - int i, i1, pti, pti1, ptih, j, restart, its; - int ii, jj, k, k1, maxits, nloc, size, one = 1; - FLOAT *vv, *z, *hh, *s, *rs, alpha, t1; - REAL eps1, tol, ro, t, *c; -#if defined(DBL_CMPLX) - FLOAT rot; -#else - REAL gam; -#endif - - parms_Mat A; - parms_PC pc; - parms_Map is; - fgmres_data fdata; - - int rank; - MPI_Comm comm; - - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - - A = self->A; - pc = self->pc; - is = A->is; - fdata = (fgmres_data)self->data; - restart = fdata->restart; - maxits = self->maxits; - tol = self->tol; - eps1 = tol; - nloc = parms_MapGetLocalSize(is); - comm = is->comm; - -/* --- first permute x and y for pARMS matrix structure ------*/ - parms_VecPerm(x, is); - parms_VecPerm(y, is); -/* ---- set isvecperm to true -----*/ - is->isvecperm = true; - -/*----- allocate memory for working local arrays -------*/ - -/* ---------allocate size for nx(m+1) matrix vv */ - size = nloc*(restart+1); - PARMS_NEWARRAY(vv, size); -/*-------- allocate size for nxm matrix z -----*/ - size = nloc*restart; - PARMS_NEWARRAY(z, size); - -/*----- allocate memory for Hessenberg matrix (nonzeros only) - *----- and rotation vectors s and rs -------------------*/ - size = (((restart+1)*(restart+2)/2) - 1) + 2*(restart+1); - PARMS_NEWARRAY(hh, size); - - s = hh + ((restart+1)*(restart+2)/2) - 1; - rs = s + restart + 1; -/*--------- allocate memory for rotation matrix c ------- - * This is done separately since for complex valued systems - * c is still a real-valued vector, and hence cannot be - * allocated as sizeof(complex double) as is the case for - * s and rs and hh above --------------------------------*/ - PARMS_NEWARRAY(c, restart+1); - - /* outer loop starts here */ - its = 0; - - outflag = true; - while(outflag) { - /* compute vv[0] = A * x */ - parms_MatVec(A, x, vv); - - /* compute residual vector */ - parms_VecAYPX(vv, y, -1.0, is); - /* compute the norm of the residual */ - parms_VecGetNorm2(vv, &ro, is); - - if(ABS_VALUE(ro) <= DBL_EPSILON) { - outflag = false; - break; - } - t1 = 1.0 / ro; - parms_VecScale(vv, t1, is); - if(its == 0) - eps1 = tol*ro; - -/* ----------initialize 1-st term of rhs of hessenberg system ------------*/ - - rs[0] = ro; - - i = -1; - pti = 0; - pti1 = 0; - ptih = 0; - intflag = true; - while (intflag) { - i++; - its++; - i1 = i + 1; - pti = i*nloc; - pti1 = i1*nloc; - -/*------------- preconditioning operation z = K^{-1}vv ---------------*/ - parms_PCApply(pc, &vv[pti], &z[pti]); - -/*------------- compute A*z -----------------*/ - parms_MatVec(A, &z[pti], &vv[pti1]); - -/*------------- classical Gram - Schmidt -------------------*/ -#if defined(DBL_CMPLX) -/* ----- Check for serial case ------ */ - if(is->isserial) - { - for(j=0; jisserial) - { - for(j=0; j TINY) { - t1 = 1.0 / t; - parms_VecScale(&vv[pti1], t1, is); - } - - /* done with classical Gram-Schmidt and Arnoldi step. now update - * factorization of hh */ -#if defined(DBL_CMPLX) - if (i != 0) { - for(k = 1; k <= i; k++) { - k1 = k-1; - t1 = hh[ptih+k1]; - - hh[ptih+k1] = c[k1]*t1 + s[k1]*hh[ptih+k]; - hh[ptih+k] = -conj(s[k1])*t1 + c[k1]*hh[ptih+k]; - } - } -/*-----------get next plane rotation------------ */ - zclartg(hh[ptih+i], hh[ptih+i1], &c[i], &s[i], &rot); - rs[i1] = -conj(s[i])*rs[i]; - rs[i] = c[i]*rs[i]; - hh[ptih+i] = rot; - ro = cabs(rs[i1]); -#else - if (i != 0) { - for(k=1; k<=i; k++){ - k1 = k-1; - t1 = hh[ptih+k1]; - - hh[ptih+k1] = c[k1]*t1 + s[k1]*hh[ptih+k]; - hh[ptih+k] = -s[k1]*t1 + c[k1]*hh[ptih+k]; - } - } -/*-----------get next plane rotation------------ */ - gam = sqrt(hh[ptih+i]*hh[ptih+i] + hh[ptih+i1]*hh[ptih+i1]); - /* - if gamma is zero then any small value will do ... - will affect only residual estimate - */ - if (fabs(gam) <= TINY) gam = TINY; - - /* determine-next-plane-rotation */ - c[i] = hh[ptih+i]/gam; - s[i] = hh[ptih+i1]/gam; - - rs[i1] = -s[i]*rs[i]; - rs[i] = c[i]*rs[i]; - /* determine res. norm and test for convergence */ - hh[ptih+i] = c[i]*hh[ptih+i] + s[i]*hh[ptih+i1]; - ro = fabs(rs[i1]); -#endif - -/*------------ Check for convergence ---------*/ - if ((i+1 >= restart) || (ro <= eps1) || its >= maxits) - intflag = false; - else -/*------------ update hh pointer ptih ---------*/ - ptih += i+2; - } - - /* now compute solution first solve upper triangular system */ - rs[i] = rs[i]/hh[ptih+i]; - for (ii = 1; ii <= i; ii++) { - k = i-ii; - k1 = k+1; - t1 = rs[k]; - for (j = k1; j <= i; j++) { - jj = ((j+1)*(j+2)/2) - 1; - t1 = t1 - hh[jj+k]*rs[j]; - } - jj = ((k+1)*(k+2)/2)-1; - rs[k] = t1/hh[jj+k]; - } - /* done with back substitution. now form linear combination to - * get solution */ - for (j = 0; j <= i; j++) { - t1 = rs[j]; - parms_VecAXPY(x, &z[j*nloc], t1, is); - } - /* test for return */ - /* if ((ro <= eps1) || (its >= maxits)) (AF){ */ - if((ro <= tol) || (its >= maxits)) { - outflag = false; - } - } - - free(vv); - free(z); - free(hh); - free(c); - - -/* reset isvecperm and do inverse permutation*/ - is->isvecperm = false; // this comes before inverse permutation - /* permutes x and y */ - parms_VecInvPerm(x, is); - parms_VecInvPerm(y, is); - - self->its = its; - return 0; -} - -static int fgmres_getresidual(parms_Solver self, FLOAT *y, FLOAT *x, FLOAT *res) -{ - - parms_Mat A; - - A = self->A; - - parms_MatMVPY(A, -1.0, x, 1.0, y, res); - - return 0; -} - -static int fgmres_getresidualnorm2(parms_Solver self, FLOAT *y, FLOAT *x, REAL *rnorm) -{ - - int nloc; - FLOAT *res; - parms_Mat A; - parms_Map is; - - A = self->A; - is = A->is; - nloc = parms_MapGetLocalSize(is); - PARMS_NEWARRAY(res, nloc); - - parms_MatMVPY(A, -1.0, x, 1.0, y, res); - - parms_VecGetNorm2(res, rnorm, is); - - PARMS_FREE(res); - - return 0; -} - - -static int fgmres_free(parms_Solver *self) -{ - fgmres_data fdata; - -/* free data associated with solver */ - fdata = (fgmres_data)(*self)->data; - PARMS_FREE(fdata); - return 0; -} - -static int fgmres_view(parms_Solver self, parms_Viewer v) -{ - FILE *fp; - char *name, *iluname; - int restart; - fgmres_data fdata; - - fdata = (fgmres_data)self->data; - restart = fdata->restart; - - parms_ViewerGetFP(v, &fp); - - fprintf(fp,"\n=============================\n"); - fprintf(fp," Solver Parameters \n"); - fprintf(fp,"=============================\n"); - - fprintf(fp, "Solver type = flexible gmres (fgmres) \n"); - - fprintf(fp, "maxits = %d \n", self->maxits); - fprintf(fp, "Relative tolerance = %-8.2e \n", self->tol); - - fprintf(fp, "Krylov dimension = %d \n", restart); - - parms_PCGetName(self->pc, &name); - parms_PCILUGetName(self->pc, &iluname); - fprintf(fp, "Global Preconditioner: %s\n", name); - fprintf(fp, "Local Preconditioner: %s\n", iluname); - - parms_ViewerGetFP(v, &fp); - - return 0; -} - -static int fgmres_setksize(parms_Solver self, int restart) -{ - fgmres_data fdata; - - fdata = (fgmres_data)self->data; - fdata->restart = restart; - - return 0; -} - -static int fgmres_setneig(parms_Solver self, int neigs) -{ - fgmres_data fdata; - - fdata = (fgmres_data)self->data; - fdata->neigs = neigs; - - return 0; -} - -static struct parms_Solver_ops parms_fgmres_sol = { - parms_fgmres, - fgmres_getresidual, - fgmres_getresidualnorm2, - fgmres_setksize, - fgmres_setneig, - fgmres_free, - fgmres_view -}; - - -/** Create the FGMRES solver. - * - * \param self A parms_Solver object. - * \return 0 on success. - */ -int fgmres_create(parms_Solver self) -{ - fgmres_data fdata; - - PARMS_NEW(fdata); - fdata->restart = 30; - fdata->neigs = 0; - self->data =fdata; - PARMS_MEMCPY(self->ops, &parms_fgmres_sol, 1); - - return 0; -} - diff --git a/lib/parms/src/gmres.c b/lib/parms/src/gmres.c deleted file mode 100755 index b71abecb7..000000000 --- a/lib/parms/src/gmres.c +++ /dev/null @@ -1,425 +0,0 @@ -/*---------Left Preconditioned GMRES----------------------------------*/ -/*-------------------------------------------------------------------- - gmres_create : create the gmres solver. - gmres_free : free the memory for the gmres solver. - gmres_setksize : set the restart size of the gmres solver. - gmres_view : dump the gmres solver. - parms_gmres : the gmres solve function. - - $Id: gmres.c,v 1.4 2006-12-15 07:02:07 zzli Exp $ - ------------------------------------------------------------------*/ - -#if defined(__ICC) -#include -#else -#if defined(C99) -#include -#else -#include -#endif -#endif -#include "parms_vec.h" -#include "parms_mat.h" -#include "parms_pc.h" -#include "./include/parms_solver_impl.h" - -#define DBL_EPSILON 2.2204460492503131e-16 // double epsilon - -typedef struct gmres_data { - int restart; - int neigs; -} *gmres_data; - -#define TINY 1.0e-20 -int parms_gmres(parms_Solver self, FLOAT *y, FLOAT *x) -{ - BOOL outflag, intflag; - int i, i1, pti, pti1, ptih, j, restart, its; - int ii, jj, k, k1, maxits, nloc, size, one = 1; - FLOAT *vv, *z, *hh, *s, *rs, *rvec, alpha, t1; - REAL eps1, tol, ro, t, *c; -#if defined(DBL_CMPLX) - FLOAT rot; -#else - REAL gam; -#endif - - parms_Mat A; - parms_PC pc; - parms_Map is; - gmres_data fdata; - - int rank; - MPI_Comm comm; - - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - - A = self->A; - pc = self->pc; - is = A->is; - fdata = (gmres_data)self->data; - restart = fdata->restart; - maxits = self->maxits; - tol = self->tol; - eps1 = tol; - nloc = parms_MapGetLocalSize(is); - comm = is->comm; - -/* --- first permute x and y for pARMS matrix structure ------*/ - parms_VecPerm(x, is); - parms_VecPerm(y, is); -/* ---- set isvecperm to true -----*/ - is->isvecperm = true; - -/*----- allocate memory for working local arrays -------*/ - -/* ---------allocate size for nx(m+1) matrix vv */ - size = nloc*(restart+1); - PARMS_NEWARRAY(vv, size); -/*-------- allocate size for nxm matrix z -----*/ - size = 2*nloc; - PARMS_NEWARRAY(z, size); - rvec = z + nloc; - -/*----- allocate memory for Hessenberg matrix (nonzeros only) - *----- and rotation vectors s and rs -------------------*/ - size = (((restart+1)*(restart+2)/2) - 1) + 2*(restart+1); - PARMS_NEWARRAY(hh, size); - - s = hh + ((restart+1)*(restart+2)/2) - 1; - rs = s + restart + 1; -/*--------- allocate memory for rotation matrix c ------- - * This is done separately since for complex valued systems - * c is still a real-valued vector, and hence cannot be - * allocated as sizeof(complex double) as is the case for - * s and rs and hh above --------------------------------*/ - PARMS_NEWARRAY(c, restart+1); - - /* outer loop starts here */ - its = 0; - - outflag = true; - while(outflag) { -/*------- Left preconditioning: compute Mr = b-Ax ------ */ -/*------ compute Ax = rvec -------*/ - parms_MatVec(A, x, rvec); - -/*-------- compute residual vector: rvec = b-Ax --------*/ - parms_VecAYPX(rvec, y, -1.0, is); - -/*--------- Precondition residual vector ---------------*/ - parms_PCApply(pc, rvec, vv); -/*--------- compute the norm of the residual -----------*/ - parms_VecGetNorm2(vv, &ro, is); -// if(fabs(ro-DBL_EPSILON) <= DBL_EPSILON*fabs(ro)) { - if(ABS_VALUE(ro) <= DBL_EPSILON) { - outflag = false; - break; - } - t1 = 1.0 / ro; - parms_VecScale(vv, t1, is); - if(its == 0) - eps1 = tol*ro; - -/* ----------initialize 1-st term of rhs of hessenberg system ------------*/ - - rs[0] = ro; - i = -1; - pti = 0; - pti1 = 0; - ptih = 0; - intflag = true; - while (intflag) { - i++; - its++; - i1 = i + 1; - pti = i*nloc; - pti1 = i1*nloc; - -/*---------- compute A*vv[i] = z --------------*/ - parms_MatVec(A, &vv[pti], z); - -/*---------- apply preconditioner -------------*/ - parms_PCApply(pc, z, &vv[pti1]); - -/*------------- classical Gram - Schmidt -------------------*/ -#if defined(DBL_CMPLX) -/* ----- Check for serial case ------ */ - if(is->isserial) - { - for(j=0; jisserial) - { - for(j=0; j TINY) { - t1 = 1.0 / t; - parms_VecScale(&vv[pti1], t1, is); - } - - /* done with classical Gram-Schmidt and Arnoldi step. now update - * factorization of hh */ -#if defined(DBL_CMPLX) - if (i != 0) { - for(k = 1; k <= i; k++) { - k1 = k-1; - t1 = hh[ptih+k1]; - - hh[ptih+k1] = c[k1]*t1 + s[k1]*hh[ptih+k]; - hh[ptih+k] = -conj(s[k1])*t1 + c[k1]*hh[ptih+k]; - } - } -/*-----------get next plane rotation------------ */ - zclartg(hh[ptih+i], hh[ptih+i1], &c[i], &s[i], &rot); - rs[i1] = -conj(s[i])*rs[i]; - rs[i] = c[i]*rs[i]; - hh[ptih+i] = rot; - ro = cabs(rs[i1]); -#else - if (i != 0) { - for(k=1; k<=i; k++){ - k1 = k-1; - t1 = hh[ptih+k1]; - - hh[ptih+k1] = c[k1]*t1 + s[k1]*hh[ptih+k]; - hh[ptih+k] = -s[k1]*t1 + c[k1]*hh[ptih+k]; - } - } -/*-----------get next plane rotation------------ */ - gam = sqrt(hh[ptih+i]*hh[ptih+i] + hh[ptih+i1]*hh[ptih+i1]); - /* - if gamma is zero then any small value will do ... - will affect only residual estimate - */ - if (fabs(gam) <= TINY) gam = TINY; - - /* determine-next-plane-rotation */ - c[i] = hh[ptih+i]/gam; - s[i] = hh[ptih+i1]/gam; - - rs[i1] = -s[i]*rs[i]; - rs[i] = c[i]*rs[i]; - /* determine res. norm and test for convergence */ - hh[ptih+i] = c[i]*hh[ptih+i] + s[i]*hh[ptih+i1]; - ro = fabs(rs[i1]); -#endif - -/*------------ Check for convergence ---------*/ - if ((i+1 >= restart) || (ro <= eps1) || its >= maxits) - intflag = false; - else -/*------------ update hh pointer ptih ---------*/ - ptih += i+2; - } - - /* now compute solution first solve upper triangular system */ - rs[i] = rs[i]/hh[ptih+i]; - for (ii = 1; ii <= i; ii++) { - k = i-ii; - k1 = k+1; - t1 = rs[k]; - for (j = k1; j <= i; j++) { - jj = ((j+1)*(j+2)/2) - 1; - t1 = t1 - hh[jj+k]*rs[j]; - } - jj = ((k+1)*(k+2)/2)-1; - rs[k] = t1/hh[jj+k]; - } -/* done with back substitution. now form linear combination to - * get solution */ - - for (j = 0; j <= i; j++) { - t1 = rs[j]; - parms_VecAXPY(x, &vv[j*nloc], t1, is); - } - /* test for return */ - /* if ((ro <= eps1) || (its >= maxits)) { (AF)*/ - if((ro <= tol) || (its >= maxits)) { - outflag = false; - } - } - - free(vv); - free(z); - free(hh); - free(c); - -/* reset isvecperm and do inverse permutation*/ - is->isvecperm = false; // this comes before inverse permutation - /* permutes x and y */ - parms_VecInvPerm(x, is); - parms_VecInvPerm(y, is); - - self->its = its; - return 0; -} - -static int gmres_getresidual(parms_Solver self, FLOAT *y, FLOAT *x, FLOAT *res) -{ - - parms_Mat A; - - A = self->A; - - parms_MatMVPY(A, -1.0, x, 1.0, y, res); - - return 0; -} - -static int gmres_getresidualnorm2(parms_Solver self, FLOAT *y, FLOAT *x, REAL *rnorm) -{ - - int nloc; - FLOAT *res; - parms_Mat A; - parms_Map is; - - A = self->A; - is = A->is; - nloc = parms_MapGetLocalSize(is); - PARMS_NEWARRAY(res, nloc); - - parms_MatMVPY(A, -1.0, x, 1.0, y, res); - - parms_VecGetNorm2(res, rnorm, is); - - PARMS_FREE(res); - - return 0; -} - - -static int gmres_free(parms_Solver *self) -{ - gmres_data fdata; - -/* free data associated with solver */ - fdata = (gmres_data)(*self)->data; - PARMS_FREE(fdata); - return 0; -} - -static int gmres_view(parms_Solver self, parms_Viewer v) -{ - FILE *fp; - char *name, *iluname; - int restart; - gmres_data fdata; - - fdata = (gmres_data)self->data; - restart = fdata->restart; - - parms_ViewerGetFP(v, &fp); - - fprintf(fp,"\n=============================\n"); - fprintf(fp," Solver Parameters \n"); - fprintf(fp,"=============================\n"); - - fprintf(fp, "Solver type = flexible gmres \n"); - - fprintf(fp, "maxits = %d \n", self->maxits); - fprintf(fp, "Relative tolerance = %-8.2e \n", self->tol); - - fprintf(fp, "Krylov dimension = %d \n", restart); - - parms_PCGetName(self->pc, &name); - parms_PCILUGetName(self->pc, &iluname); - fprintf(fp, "Global Preconditioner: %s\n", name); - fprintf(fp, "Local Preconditioner: %s\n", iluname); - - parms_ViewerGetFP(v, &fp); - - return 0; -} - -static int gmres_setksize(parms_Solver self, int restart) -{ - gmres_data fdata; - - fdata = (gmres_data)self->data; - fdata->restart = restart; - - return 0; -} - -static int gmres_setneig(parms_Solver self, int neigs) -{ - gmres_data fdata; - - fdata = (gmres_data)self->data; - fdata->neigs = neigs; - - return 0; -} - -static struct parms_Solver_ops parms_gmres_sol = { - parms_gmres, - gmres_getresidual, - gmres_getresidualnorm2, - gmres_setksize, - gmres_setneig, - gmres_free, - gmres_view -}; - - -/** Create the gmres solver. - * - * \param self A parms_Solver object. - * \return 0 on success. - */ -int gmres_create(parms_Solver self) -{ - gmres_data fdata; - - PARMS_NEW(fdata); - fdata->restart = 30; - fdata->neigs = 0; - self->data =fdata; - PARMS_MEMCPY(self->ops, &parms_gmres_sol, 1); - - return 0; -} - diff --git a/lib/parms/src/include/parms_comm_impl.h b/lib/parms/src/include/parms_comm_impl.h deleted file mode 100755 index b83374299..000000000 --- a/lib/parms/src/include/parms_comm_impl.h +++ /dev/null @@ -1,77 +0,0 @@ -/*! - \file parms_comm_impl.h - \brief parms_Comm structure file - - \author zzli - \date 2006-05-07 -*/ - -#ifndef _PARMS_COMM_IMPL_H_ -#define _PARMS_COMM_IMPL_H_ - -#include "parms_comm.h" -#include "parms_mem.h" - -/*! - \struct parms_Comm_ - */ -struct parms_Comm_ { - - int ref; - MPI_Comm comm; //!< The communicator. - COMMTYPE ctype; //!< The communication style. - /*! - \param npsend The number of processors receiving data from the local - processor. - */ - int npsend; - int *procs_send; //!< list of processors to be sent. - int *vlist_send; //!< list of variables to be sent. - /*! - \param ptrvsend An array of pointers to the beginning of the data - sent to the i-th processor in vlist_send. - */ - int *ptrvsend; - FLOAT *buf_send; //!< The send buffer. - MPI_Status *status_send; //!< The send status. - MPI_Request *req_send; //!< The send request. - /*! - \param isdt_alloc Indicate if the derived data type for sending - data is created or not. - */ - BOOL isdt_alloc; - /*! - \param mdata_send The maximum number of the data to be sent on each row. - */ - int mdata_send; - MPI_Datatype *dtype_send; //!< The derived send datatype - /*! - \param nprecv The number processors from which data are received - */ - int nprecv; - int nodv; //!< The number of external variables. - /*! - \param odvlist The list of external variables stored in processor - succession. - */ - int *odvlist; - /*! - \param novp The number of subdomains to which the variable is sent - */ - int *novp; - /*! - \param ptrvrecv An array of the beginning of data received from the - i-th processor. - */ - int *ptrvrecv; - /*! - \param procs_recv The list of processors from which the data are - received. - */ - int *procs_recv; - FLOAT *buf_recv; //!< The receive buffer. - MPI_Status *status_recv; //!< The receive status. - MPI_Request *req_recv; //!< The receive request. -}; - -#endif diff --git a/lib/parms/src/include/parms_map_impl.h b/lib/parms/src/include/parms_map_impl.h deleted file mode 100755 index 5ed5f3a1d..000000000 --- a/lib/parms/src/include/parms_map_impl.h +++ /dev/null @@ -1,87 +0,0 @@ -/*! - \file parms_map_impl.h - \brief The definition of the parms_Map struct. - - \author zzli - \date 2006-05-05 -*/ - -#ifndef _PARMS_MAP_IMPL_H_ -#define _PARMS_MAP_IMPL_H_ - -#include "parms_mem.h" -#include "parms_table.h" -#include "parms_map.h" - - -/*! \struct parms_Map_ - */ -struct parms_Map_ { - int ref; - parms_Table table; /**< contains pair (gindex,lindex) */ - MPI_Comm comm; /**< MPI communicator */ - int pid; /**< processor ID */ - int npro; /**< number of processors */ - int lsize; /**< number of local variables */ - int gsize; /**< number of global variables */ - /*! numbering style used. - * - * \f{tabular}{lcl} - * FORTRAN &-& 1 \\ - * C &-& 0 - * \f} - */ - int start; - /*! - The number of variables associated with each vertex. - */ - int dof; - /*! style of labelling variables u_i,v_i associated with the i-th vertex. - * - * - NONINTERLACED u_1,v_1,u_2,v_2. - * - * - INTERLACED u_1,u_2,\cdots,v_1,v_2. - */ - VARSTYPE vtype; - /*! array of size lsize, stores local variables in global labels */ - int *lvars; - BOOL isserial; //!< data layout on one processor? - /*! variables are permuted or not. - * -true: interior variables labelled first followed by interface - * variables */ - BOOL isperm; - BOOL isvecperm; /* used to check if vector has been permuted in gmres */ - BOOL ispermalloc; /* permutation array allocated? */ - int *perm; //!< permutation array of size lsize. - int *iperm; //!< inverse permutation array. - int nint; //!< number of interior variables. - int ninf; //!< number of interface variables. - /*! - \param schur_start start of the local Schur complement which - may be lesser than nbnd for the matrix with unsymmetric pattern - */ - int schur_start; - int ninf_send; //!< number of variables to be sent. - /*! - \param vsend array of size ninf_send which stores variables in local - indices - */ - int *vsend; - /*! - \parameters for external data contributions - */ - int n_ext; - int *ext_im; - /*! - \parameter data used to handle external vector contributions - */ - void *data; - BOOL isdatalloc; - /*! - \param vstable stores variables to be sent to adjacent processors in pairs - (local_index, index_in_vsend) - */ - parms_Table vstable; -}; - -#endif diff --git a/lib/parms/src/include/parms_mat_impl.h b/lib/parms/src/include/parms_mat_impl.h deleted file mode 100755 index 5cedf037c..000000000 --- a/lib/parms/src/include/parms_mat_impl.h +++ /dev/null @@ -1,154 +0,0 @@ -/*! - \file parms_mat_impl.h - \brief The matrix structure and functions - - \author zzli - \date 2006-05-08 -*/ - -#ifndef _PARMS_MAT_IMPL_H_ -#define _PARMS_MAT_IMPL_H_ - -#include "parms_mat.h" -#include "parms_operator.h" -#include "parms_map_impl.h" -#include "parms_table_impl.h" - -#define PARMS_MAT_FORMAT_KIND 0x00f00000 -#define PARMS_MAT_SHIFT 20 -#define PARMS_MAT_GET_KIND(a) (((a)&PARMS_MAT_FORMAT_KIND) >> PARMS_MAT_SHIFT) -#define PARMS_MAT_SET_KIND(a, kind) ((a)|(kind) << PARMS_MAT_SHIFT) - -/* -typedef int (*PARMS_ILU)(parms_Mat self, parms_FactParam param, void - *data, parms_Operator *op); -*/ - -/*! \struct parms_Mat_ops. - \brief struct parms_mat_ops. - */ -typedef struct parms_Mat_ops { - /*! function parameter: apply - \brief performs \f$y = self \times x\f$. - */ - int (*apply)(parms_Mat self, FLOAT *x, FLOAT *y); - /*! - Divide the local equation into diagonal part and off-diagonal - part. Set up communcation handler for matrix-vector product. - */ - int (*setup)(parms_Mat self); - /*! Set the communication type - \f{tabular}{ll} - P2P & Copying data into a temporary buffer \\ - DERIVED & Creating a derived datatype as in the old version of - pARMS. - \f} - */ - int (*setcommtype)(parms_Mat self, COMMTYPE ctype); - - /*! function parameter: mvpy - \brief Performs z = beta*y + alpha*self*x. - */ - int (*mvpy)(parms_Mat self, FLOAT alpha, FLOAT *x, FLOAT beta, - FLOAT *y, FLOAT *z); - - /*! function parameter: getdiag - \brief Get the diagonal part of the local matrix. - */ - int (*getdiag)(parms_Mat self, void **mat); - - - /*! function parameter: getoffdiag (used by pc_schurras) (AF) - \brief Get the offdiagonal part of the local matrix. - */ - int (*getoffdiag)(parms_Mat self, void **mat); - /*! function parameter: getlmat - \brief Get the local matrix including diagonal and off-diagonal - matrix. - */ - - int (*getlmat)(parms_Mat self, void **mat); - /*! function parameter: extend - \brief Extend the submatrix mat by including equations that - correspond to immediate neighbouring variables. - */ - int (*extend)(parms_Mat self, parms_Comm handler, int start, void - *mat, int *n, void **ext_mat); - /*! function Parameter: mvoffd - \brief The matrix-vector product for off-diagonal part. - */ - int (*mvoffd)(parms_Mat self, FLOAT *x, FLOAT *y, int pos); - - /*! function Parameter: matfree - \brief Free the matrix mat. - */ - int (*matfree)(parms_Mat self, void *mat); - - /*! function Parameter: gethandler - \brief Get the communication handler for mv product. - */ - int (*gethandler)(parms_Mat self, parms_Comm *handler); -} *parms_Mat_ops; - - -/*! \struct parms_vcsr. - \ Description: struct parms_vcsr. - */ -typedef struct parms_vcsr { - int n; //!< the dimension of the matrix - int *nnzrow; //!< the length of each row - int *space; //!< length of space ( a work array) - int off_proc_n; //!< size of off-processor contributions - /*! - Parameter: pj = An indirect pointer to store column indices. - */ - int **pj; - /*! parameter: pa = An indirect pointer to store corresponding nonzero entries. - */ - FLOAT **pa; - - int *pj_data; - FLOAT *pa_data; -} *parms_vcsr; - -/*! \struct parms_Mat_ - \Description: struct parms_Mat_ - */ -struct parms_Mat_ { - - int ref; - parms_Mat_ops ops; - void *data; - BOOL isserial; - BOOL issetup; - BOOL isperm; - BOOL isalloc; - BOOL isreset; - NNZSTRUCT resetpattern; - MATTYPE type; - PCILUTYPE ilutype; - int m,n; - int M,N; - parms_Map is; - parms_vcsr aux_data; -/* data for external contributions */ - parms_vcsr ext_data; - BOOL isassembled; -/* Table to track count of offdiagonal variables. - * This allows efficient update of the hash table - * for the variables, when a row is reset. -*/ - parms_Table odtable; -}; - - -/* external function protos */ -extern int parms_MatCreate_vcsr(parms_Mat self); -extern int parms_MatCreate_dvcsr(parms_Mat self); -extern int parms_MatFree_dvcsr(parms_Mat *self); -extern int parms_MatView_vcsr(parms_Mat self, parms_Viewer v); -extern int parms_MatViewCOO_vcsr(parms_Mat self, parms_Viewer v); -extern int parms_MatView_dvcsr(parms_Mat self, parms_Viewer v); -extern int parms_MatViewCOO_dvcsr(parms_Mat self, parms_Viewer v); - -#endif diff --git a/lib/parms/src/include/parms_opt_impl.h b/lib/parms/src/include/parms_opt_impl.h deleted file mode 100755 index 9631ed6a9..000000000 --- a/lib/parms/src/include/parms_opt_impl.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef _PARMS_OPERATOR_IMPL_H_ -#define _PARMS_OPERATOR_IMPL_H_ - -#include "parms_vec.h" -#include "parms_operator.h" -#include "parms_mat_impl.h" - -typedef struct parms_Operator_ops{ - int (*apply)(parms_Operator self, FLOAT *y, FLOAT *x); - int (*lsol)(parms_Operator self, FLOAT *y, FLOAT *x); - int (*invs)(parms_Operator self, FLOAT *y, FLOAT *x); - int (*getu)(parms_Operator self, void **mat); - int (*ascend)(parms_Operator self, FLOAT *y, FLOAT *x); - int (*getssize)(parms_Operator self); - void (*getnnz)(parms_Operator self, int *nnz_mat, int - *nnz_pc); - int (*operator_free)(parms_Operator *self); - int (*operator_view)(parms_Operator self, parms_Viewer v); -} *parms_Operator_ops; - -struct parms_Operator_ { - int ref; - parms_Operator_ops ops; - void *data; -}; - -/*-----external function protos--------*/ -extern int parms_ilu0_vcsr(parms_Mat self, parms_FactParam param, void *data, parms_Operator *op); -extern int parms_iluk_vcsr(parms_Mat self, parms_FactParam param, void *data, parms_Operator *op); -extern int parms_ilut_vcsr(parms_Mat self, parms_FactParam param, void *data, parms_Operator *op); -extern int parms_arms_vcsr(parms_Mat self, parms_FactParam param, void *data, parms_Operator *op); - -/* reuse LU-Factorization (AF) */ -extern int parms_ilu_update(parms_Mat self, parms_FactParam param, void *data, parms_Operator *op); - -/*----------End Protos------------------*/ - -#endif diff --git a/lib/parms/src/include/parms_pc_impl.h b/lib/parms/src/include/parms_pc_impl.h deleted file mode 100755 index 19dd0d4d4..000000000 --- a/lib/parms/src/include/parms_pc_impl.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef _PARMS_PC_IMPL_H_ -#define _PARMS_PC_IMPL_H_ - -#include "parms_pc.h" -#include "parms_vec.h" -#include "parms_mat_impl.h" -#include "parms_viewer.h" - -typedef struct parms_PC_ops { - int (*apply)(parms_PC self, FLOAT *y, FLOAT *x); - int (*setup)(parms_PC self); - int (*getratio)(parms_PC self, double *ratio); - int (*pc_free)(parms_PC *self); - int (*pc_view)(parms_PC self, parms_Viewer v); -} *parms_PC_ops; - -extern char *pcname[]; -extern char *pciluname[]; - -struct parms_PC_ { - - int ref; - parms_PC_ops ops; - void *data; - parms_Mat A; - BOOL istypeset; - BOOL isiluset; - BOOL issetup; - BOOL isopset; - BOOL isperm; - int *perm; - int *iperm; - parms_FactParam param; - PCTYPE pctype; - PCILUTYPE pcilutype; -}; - -/*-----external function protos--------*/ -extern int parms_PCCreate_BJ(parms_PC self); -extern int parms_PCCreate_Schur(parms_PC self); -extern int parms_PCCreate_RAS(parms_PC self); -/* additional PC based on Li Z, Saad Y: SchurRAS: A restricted version of the - overlapping Schur complement preconditioner, Report umsi-2004-76, Minnesota - Supercomputer Institute, University of Minnesota, Minneapolis, MN, 2004.(AF)*/ -extern int parms_PCCreate_Schurras(parms_PC self); -/*----------End Protos------------------*/ - -#endif diff --git a/lib/parms/src/include/parms_solver_impl.h b/lib/parms/src/include/parms_solver_impl.h deleted file mode 100755 index 60d98f74f..000000000 --- a/lib/parms/src/include/parms_solver_impl.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef _PARMS_SOLVER_IMPL_H_ -#define _PARMS_SOLVER_IMPL_H_ - -#include "parms_operator.h" -#include "parms_solver.h" -#include "parms_mat_impl.h" -#include "parms_pc_impl.h" - -typedef struct parms_Solver_ops { - int (*apply)(parms_Solver self, FLOAT *y, FLOAT *x); - int (*getresidual)(parms_Solver self, FLOAT *y, FLOAT *x, FLOAT *res); - int (*getresidualnorm2)(parms_Solver self, FLOAT *y, FLOAT *x, REAL *rnorm); - int (*setksize)(parms_Solver self, int restart); - int (*setneig)(parms_Solver self, int neig); - int (*solver_free)(parms_Solver *self); - int (*solver_view)(parms_Solver self, parms_Viewer v); -} *parms_Solver_ops; - -struct parms_Solver_ { - - int ref; - parms_Solver_ops ops; - void *data; - SOLVERTYPE stype; - BOOL istypeset; - int maxits; - double tol; - int its; - parms_Mat A; - parms_PC pc; -}; - -/* external function protos */ -extern int fgmres_create(parms_Solver self); -extern int parms_fgmres(parms_Solver self, FLOAT *y, FLOAT *x); - -extern int gmres_create(parms_Solver self); -extern int parms_gmres(parms_Solver self, FLOAT *y, FLOAT *x); -/* extern int dgmres_create(parms_Solver self); */ - -/* additional Solver Type (AF) */ -extern int bicgstab_create(parms_Solver self); -extern int parms_bicgstab(parms_Solver self, FLOAT *y, FLOAT *x); - -/* additional Solver Type (NR) */ -extern int cg_create(parms_Solver self); -extern int parms_cg(parms_Solver self, FLOAT *y, FLOAT *x); -extern int pbicgstab_create(parms_Solver self); -extern int parms_pbicgstab(parms_Solver self, FLOAT *y, FLOAT *x); -extern int pbicgstabras_create(parms_Solver self); -extern int parms_pbicgstabras(parms_Solver self, FLOAT *y, FLOAT *x); -extern int bicgstabras_create(parms_Solver self); -extern int parms_bicgstabras(parms_Solver self, FLOAT *y, FLOAT *x); - -#endif diff --git a/lib/parms/src/include/parms_table_impl.h b/lib/parms/src/include/parms_table_impl.h deleted file mode 100755 index 203255ff3..000000000 --- a/lib/parms/src/include/parms_table_impl.h +++ /dev/null @@ -1,37 +0,0 @@ -/*! - \file parms_table_impl.h - \brief parms_Table is used for parms_Map object to insert pairs - (global_index, local_index) into the table. - - \author zzli - \date 2006-05-05 -*/ - -#ifndef _PARMS_TABLE_IMPL_H_ -#define _PARMS_TABLE_IMPL_H_ - -#include "parms_mem.h" -#include "parms_table.h" - -/*! typedef Slot -*/ -/*! \struct Slot - \brief structure Slot - */ -typedef struct Slot { - struct Slot *link; //!< pointer to a next entry in this slot. - int key; //!< key in a pair (key,value) - int value; //!< value in a pair (key, value) -} Slot; - -/*! \struct parms_Table_ - \brief parms_Table_ structure. - */ -struct parms_Table_ { - Slot **Slots; //!< array of a pointer array to struct Slot - HashFcn hf; //!< function pointer to a hash function. - int space; //!< size of array Slots. - int size; //!< number of pairs in the table. -}; - -#endif diff --git a/lib/parms/src/include/parms_timer_impl.h b/lib/parms/src/include/parms_timer_impl.h deleted file mode 100755 index bddd9ac2b..000000000 --- a/lib/parms/src/include/parms_timer_impl.h +++ /dev/null @@ -1,31 +0,0 @@ -/*! - \file parms_timer_impl.h - \brief parms_Timer structure - - \author zzli - \date 2006-05-05 -*/ - -#ifndef _PARMS_TIMER_IMPL_H_ -#define _PARMS_TIMER_IMPL_H_ - -#include "parms_mem.h" -#include "parms_timer.h" - -/*! \struct parms_Timer_ - \brief parms_Timer_ structure. - */ -struct parms_Timer_ { - - int ref; - /*! \var initial_time - \brief initial time - */ - double initial_time; - /*! \var elapsed_time - \brief elapsed time so far. - */ - double elapsed_time; -}; - -#endif diff --git a/lib/parms/src/include/parms_viewer_impl.h b/lib/parms/src/include/parms_viewer_impl.h deleted file mode 100755 index 19c5bc8f4..000000000 --- a/lib/parms/src/include/parms_viewer_impl.h +++ /dev/null @@ -1,26 +0,0 @@ -/*! - \file parms_viewer_impl.h - \brief parms_Viewer object. - - \author zzli - \date 2006-05-05 -*/ - -#ifndef _PARMS_VIEWER_IMPL_H_ -#define _PARMS_VIEWER_IMPL_H_ - -#include -#include "parms_viewer.h" -#include "parms_mem.h" - -/*! \struct parms_Viewer_ - \brief parms_Viewer_ structure. - */ -struct parms_Viewer_ { - int ref; - BOOL isstd; //!< is stdout or stderr - FILE *fp; //!< file pointer - char *fname; //!< file name -}; - -#endif diff --git a/lib/parms/src/parms_comm.c b/lib/parms/src/parms_comm.c deleted file mode 100755 index 6fdd9a777..000000000 --- a/lib/parms/src/parms_comm.c +++ /dev/null @@ -1,402 +0,0 @@ -/*-------------------------------------------------------------------- - parms_CommCreate : create a communication handler - parms_CommDataBegin : start data communication - parms_CommDataEnd : end data communication - parms_CommFree : free the memory for the communication handler - parms_CommGetNumRecv : get the number of the data to be received - parms_CommGetNumSend : get the number of the data to be sent - parms_CommGetRecvBuf : get the receive buffer - parms_CommView : dump the communication handler - - A code fragment for using above functions: - - parms_Comm comm; - double *data; - - // create a communication handler - parms_CommCreate(comm, MPI_COMM_WORLD); - - // exchange data - parms_CommDataBegin(comm, data, 0); - parms_CommDataEnd(comm); - - // free the memory for the comm. handler - parms_CommFree(comm); - - $Id: parms_comm.c,v 1.1.1.1 2006-11-27 22:28:01 zzli Exp $ ---------------------------------------------------------------------*/ - -#include "./include/parms_comm_impl.h" - -/** - * Create a parms_Comm object. - * - * @param self A pointer to the parms_Comm object. - * @param comm MPI communicator - * - * @return 0 on success. - */ -int parms_CommCreate(parms_Comm *self, MPI_Comm comm) -{ - parms_Comm comm_handler; - PARMS_NEW0((comm_handler)); - comm_handler->ref = 1; - MPI_Comm_dup(comm, &comm_handler->comm); - - /* communication via packed data rather than derived datatype */ - comm_handler->ctype = P2P; - - /* the derived datatype created or not? */ - comm_handler->isdt_alloc = false; - - /* the number of PEs to which this PE sends data */ - comm_handler->npsend = 0; - - /* the ids of the PEs to which this PE sends data */ - comm_handler->procs_send = NULL; - - /* list of local vars sent to other PEs */ - comm_handler->vlist_send = NULL; - - /* the pointer array to the beginning position of the data sent to - procs_send[i] in vlist_send */ - comm_handler->ptrvsend = NULL; - - /* send buffer */ - comm_handler->buf_send = NULL; - - /* MPI send status */ - comm_handler->status_send = NULL; - - /* MPI send request */ - comm_handler->req_send = NULL; - - /* derived datatype */ - comm_handler->dtype_send = NULL; - - /* the number of PEs from which this PE receives data */ - comm_handler->nprecv = 0; - - /* the ids of the PEs from which this PE receives data */ - comm_handler->procs_recv = NULL; - - /* number of data received */ - comm_handler->nodv = 0; - - /* list of data received */ - comm_handler->odvlist = NULL; - - /* the pointer array to the beginning position of the data received from - procs_send[i] in odvlist */ - comm_handler->ptrvrecv = NULL; - - /* receive buffer */ - comm_handler->buf_recv = NULL; - - /* MPI receive status */ - comm_handler->status_recv = NULL; - - /* MPI receive request */ - comm_handler->req_recv = NULL; - - *self = comm_handler; - return 0; -} - -/** - * Free the memory for the parms_Comm object. - * - * @param self A pointer to the parms_Comm object. - * - * @return 0 on success. - */ -int parms_CommFree(parms_Comm *self) -{ - int i; - - // PARMS_VALIDCOMMPTR(*self); - /* decrease the reference number */ - (*self)->ref--; - /* if reference number is zero (the object is not referenced by - other objects), then remove the object*/ - if ((*self)->ref == 0 ) { - if ((*self)->npsend) { - PARMS_FREE((*self)->procs_send); - PARMS_FREE((*self)->vlist_send); - PARMS_FREE((*self)->buf_send); - if ((*self)->isdt_alloc) { - for (i = 0; i < (*self)->npsend; i++) { - MPI_Type_free(&(*self)->dtype_send[i]); - } - PARMS_FREE((*self)->dtype_send); - } - } - PARMS_FREE((*self)->ptrvsend); - PARMS_FREE((*self)->ptrvrecv); - if ((*self)->nprecv) { - PARMS_FREE((*self)->procs_recv); - PARMS_FREE((*self)->odvlist); - PARMS_FREE((*self)->buf_recv); - } - if ((*self)->npsend + (*self)->nprecv) { - PARMS_FREE((*self)->status_send); - PARMS_FREE((*self)->req_send); - } - MPI_Comm_free(&(*self)->comm); - PARMS_FREE(*self); - } - return 0; -} - -/** - * Dump the communication handler comm. - * - * @param self A communication handler. - * @param v A parms_Viewer object. - * - * @return 0 on success. - */ -int parms_CommView(parms_Comm self, parms_Viewer v) -{ - FILE *fp; - int i, j; - - parms_ViewerGetFP(v, &fp); - fprintf(fp, "=====Dump the content of parms_Comm=====\n"); - fprintf(fp, "npsend = %d\n", self->npsend); - for (i = 0; i < self->npsend; i++) { - fprintf(fp, "Sending data to processor%d\n", self->procs_send[i]); - } - for (i = 0; i < self->npsend; i++) { - fprintf(fp, "The data in local indices sent to processor %d\n", - self->procs_send[i]); - for (j = self->ptrvsend[i]; j < self->ptrvsend[i+1]; j++) { - fprintf(fp, "vlist_send[%d] = %d\n", j, self->vlist_send[j]); - } - } - - fprintf(fp, "nprecv = %d\n", self->nprecv); - for (i = 0; i < self->nprecv; i++) { - fprintf(fp, "Receiving data from processor%d\n", self->procs_recv[i]); - } - fprintf(fp, "========================================\n"); - parms_ViewerStoreFP(v, fp); - return 0; -} - -/** - * Start communication among neighbouring processors. - * - * If the self->ctype is set to P2P, then the data are copied to the - * send buffer, self->buf_send, otherwise, the data are sent directly. - * - * The parameter, pos, indicates the offset of the parameter, data, to - * the beginning of the local part of the distributed vector and - * matrix. - * - * @param self A communication handler. - * @param data The data to be sent. - * @param pos The distance between the beginning of the parameter - * data and the beginning of the local part of the - * distirbuted vector. - * - * @return 0 on success. - */ -int parms_CommDataBegin(parms_Comm self, void *data, int pos) -{ - FLOAT *data_send; - MPI_Comm comm; - int i, j, index, start, end, length, tag; - - data_send = (FLOAT *)data; - comm = self->comm; - tag = 100; -#if defined(DBL_CMPLX) - for (i = 0; i < self->nprecv; i++) { - start = self->ptrvrecv[i]; - end = self->ptrvrecv[i+1]; - length = end - start; - MPI_Irecv(&self->buf_recv[start], length, MPI_CMPLX, - self->procs_recv[i], tag, comm, &self->req_recv[i]); - } - if (self->ctype == P2P) { - for (i = 0; i < self->npsend; i++) { - start = self->ptrvsend[i]; - end = self->ptrvsend[i+1]; - length = end - start; - /* copy data to the send buffer */ - for (j = start; j < end; j++) { - index = self->vlist_send[j]; - self->buf_send[j] = data_send[index-pos]; - } - MPI_Isend(&self->buf_send[start], length, MPI_CMPLX, - self->procs_send[i], tag, comm, &self->req_send[i]); - } - } - else if (self->ctype == DERIVED) { - if (self->isdt_alloc == false) { - int *blen, *disp_index; - - if (self->npsend) { - PARMS_NEWARRAY(blen, self->mdata_send); - PARMS_NEWARRAY(disp_index, self->mdata_send); - PARMS_NEWARRAY(self->dtype_send, self->npsend); - - } - for (i = 0; i < self->npsend; i++) { - start = self->ptrvsend[i]; - end = self->ptrvsend[i+1]; - length = end - start; - for (j = start; j < end; j++) { - disp_index[j-start] = self->vlist_send[j]-pos; - blen[j-start] = 1; - } - MPI_Type_indexed(length, blen, disp_index, MPI_CMPLX, - &self->dtype_send[i]); - MPI_Type_commit(&self->dtype_send[i]); - } - self->isdt_alloc = true; - } - - for (i = 0; i < self->npsend; i++) { - MPI_Isend(data_send-pos, 1, self->dtype_send[i], - self->procs_send[i], tag, comm, &self->req_send[i]); - } - } -#else - for (i = 0; i < self->nprecv; i++) { - start = self->ptrvrecv[i]; - end = self->ptrvrecv[i+1]; - length = end - start; - MPI_Irecv(&self->buf_recv[start], length, MPI_DOUBLE, - self->procs_recv[i], tag, comm, &self->req_recv[i]); - } - if (self->ctype == P2P) { - for (i = 0; i < self->npsend; i++) { - start = self->ptrvsend[i]; - end = self->ptrvsend[i+1]; - length = end - start; - /* copy data to the send buffer */ - for (j = start; j < end; j++) { - index = self->vlist_send[j]; - self->buf_send[j] = data_send[index-pos]; - } - MPI_Isend(&self->buf_send[start], length, MPI_DOUBLE, - self->procs_send[i], tag, comm, &self->req_send[i]); - } - } - else if (self->ctype == DERIVED) { - if (self->isdt_alloc == false) { - int *blen, *disp_index; - - if (self->npsend) { - PARMS_NEWARRAY(blen, self->mdata_send); - PARMS_NEWARRAY(disp_index, self->mdata_send); - PARMS_NEWARRAY(self->dtype_send, self->npsend); - - } - for (i = 0; i < self->npsend; i++) { - start = self->ptrvsend[i]; - end = self->ptrvsend[i+1]; - length = end - start; - for (j = start; j < end; j++) { - disp_index[j-start] = self->vlist_send[j]-pos; - blen[j-start] = 1; - } - MPI_Type_indexed(length, blen, disp_index, MPI_DOUBLE, - &self->dtype_send[i]); - MPI_Type_commit(&self->dtype_send[i]); - } - self->isdt_alloc = true; - } - - for (i = 0; i < self->npsend; i++) { - MPI_Isend(data_send-pos, 1, self->dtype_send[i], - self->procs_send[i], tag, comm, &self->req_send[i]); - } - } -#endif - - return 0; -} - -/** - * Wait for all communication requests. - * - * Data in the receive buffer can be used safely after calling this - * functions. - * - * @param self A communication handler. - * - * @return 0 on success. - */ -int parms_CommDataEnd(parms_Comm self) -{ - int npsend, nprecv; - MPI_Status *status_send, *status_recv; - MPI_Request *req_send, *req_recv; - - npsend = self->npsend; - nprecv = self->nprecv; - // status_send = self->status_send; - // status_recv = self->status_recv; - req_send = self->req_send; - req_recv = self->req_recv; - - // MPI_Waitall(nprecv, req_recv, status_recv); - // MPI_Waitall(npsend, req_send, status_send); - - MPI_Waitall(nprecv, req_recv, MPI_STATUSES_IGNORE); - MPI_Waitall(npsend, req_send, MPI_STATUSES_IGNORE); - - return 0; -} - -/** - * Get receive buffer. - * - * @param self A communication handler. - * @param rbuf Receive buffer. - * - * @return 0 on success. - */ -int parms_CommGetRecvBuf(parms_Comm self, FLOAT **rbuf) -{ - - *rbuf = self->buf_recv; - return 0; -} - -/** - * Get the number of the data received. - * - * @param self A communication handler. - * - * @return The number of data received. - */ -int parms_CommGetNumRecv(parms_Comm self) -{ - - return self->nodv; -} - -/** - * Get the number of data to be sent. - * - * @param self A communication handler. - * - * @return The number of data to be sent. - */ -int parms_CommGetNumSend(parms_Comm self) -{ - - return self->ptrvsend[self->npsend]; -} - - -/* used by SCHURRAS (AF) */ -int parms_CommGetOdvlist(parms_Comm self, int **odvlist) -{ - *odvlist = self->odvlist; - return 0; -} diff --git a/lib/parms/src/parms_complex.c b/lib/parms/src/parms_complex.c deleted file mode 100755 index eb81c6b80..000000000 --- a/lib/parms/src/parms_complex.c +++ /dev/null @@ -1,104 +0,0 @@ -#include -#include -#include -#include -#include "parms_sys.h" - -#if defined(DBL_CMPLX) -#define epsmac 1.0e-16 - -/*---------------------------------------------------------------------------* - * A C implementation of the LAPACK auxilliary routine clartg.f. - * Computes the plane givens rotation so that: - * - * [ CS SN ] . [ F ] = [ rot ] - * [ -conjg(SN) CS ] [ G ] [ 0 ] - * - * where CS**2 + |SN|**2 = 1 and Re(rot) >= 0. - * - * The algorithm implements both parts of the LAPACK3E clartg routine - * and algorithm 3 in "On computing Givens Rotations Reliably and Efficiently" - * by Bindel et al. - *---------------------------------------------------------------------------*/ - -/* Auxilliary functions */ -/* -------------------------------------------*/ -double sgn(double x, double y){ - /* Sign transfer function */ - if (y >= 0.0) return fabs(x); - else return -fabs(x); -} -double sign(double x){ - return x/fabs(x); -} -double abs1(complex double x){ - return fabs(creal(x)) + fabs(cimag(x)); -} -double abssq(complex double x){ - return (pow(creal(x), 2) + pow(cimag(x),2)); - // return cpow(cabs(x),2); -} -/* ----------------------------------------------*/ - -/* Givens Rotation */ -void zclartg(complex double f, complex double g, double *cs, complex double *sn, complex double *rot) -{ - double one=1.0, zero=0.0; - complex double czero = 0.0 + 0.0*I; - double D, F2, G2, FG2; - if (cabs(g) <= epsmac) { - *cs = sgn(one, creal(f)); - *sn = czero; - *rot = f*(*cs); - } - else if (cabs(f) <= epsmac){ - *cs = zero; - *sn = conj(g)/cabs(g); - *rot = cabs( g ); - } - else{ - F2 = abssq(f); - G2 = abssq( g ); - FG2 = F2 + G2; - if(fabs(FG2) <= epsmac) FG2 = epsmac; - D = 1/sqrt(F2*FG2); - *cs = F2 * D; - FG2 = FG2*D; - *rot = f*FG2; - *sn = f*D; - *sn = conj(g)*(*sn); - } -} - -/*-------------- complex addition operation for MPI -----*/ -/* This function defines the addition operation for an array - * of complex numbers, for use in MPI reduction operations. - * It is defined here, but not use, since everything seems to - * work well with MPI_SUM when the complex number is defined - * as a contiguous array of two doubles (see definition for MPI_CMPLX). - */ -void complex_sum(complex_type *in, complex_type *inout, int *len, MPI_Datatype *data_ptr) -{ - int i; - complex_type sum; - for(i = 0; i<(*len); i++) - { - sum.real = in->real + inout->real; - sum.imag = in->imag + inout->imag; - *inout = sum; - in++; - inout++; - } -} - -/* ----------------- Initialize complex data type and ops for MPI ----*/ -void parms_InitComplex() -{ - MPI_Type_contiguous(2, MPI_DOUBLE, &MPI_CMPLX); - MPI_Type_commit( &MPI_CMPLX ); - - MPI_Op_create((MPI_User_function *)complex_sum, true, &MPI_CMPLX_SUM); -} - - -#endif diff --git a/lib/parms/src/parms_ilu_vcsr.c b/lib/parms/src/parms_ilu_vcsr.c deleted file mode 100755 index 17169e210..000000000 --- a/lib/parms/src/parms_ilu_vcsr.c +++ /dev/null @@ -1,1747 +0,0 @@ -#include -#include "include/parms_mat_impl.h" -#include "include/parms_opt_impl.h" -#include "DDPQ/protos.h" - -#define DBL_EPSILON 2.2204460492503131e-16 // double epsilon - -typedef struct parms_ilu_data { - parms_vcsr L; - parms_vcsr U; - int schur_start; - int n; - int *nnzschur; - int nnz_mat; - int nnz_prec; -} *parms_ilu_data; - -/* -int qsplitCF(FLOAT *a, int *ind, int n, int ncut); -void qqsort(int *ja, FLOAT *ma, int left, int right); -*/ - -static int parms_ilu_free(parms_Operator *self) -{ - parms_ilu_data data; - int n, nnz, i, *pj; - FLOAT *pa; - - data = (parms_ilu_data)(*self)->data; - n = data->L->n; - - for (i = 0; i < n; i++) { - nnz = data->L->nnzrow[i]; - pj = data->L->pj[i]; - pa = data->L->pa[i]; - if (nnz) { - PARMS_FREE(pj); - PARMS_FREE(pa); - } - } - PARMS_FREE(data->L->nnzrow); - PARMS_FREE(data->L->pj); - PARMS_FREE(data->L->pa); - PARMS_FREE(data->L); - - n = data->U->n; - for (i = 0; i < n; i++) { - nnz = data->U->nnzrow[i]; - pj = data->U->pj[i]; - pa = data->U->pa[i]; - if (nnz) { - PARMS_FREE(pj); - PARMS_FREE(pa); - } - } - - PARMS_FREE(data->U->nnzrow); - PARMS_FREE(data->U->pj); - PARMS_FREE(data->U->pa); - PARMS_FREE(data->U); - - if (data->n - data->schur_start){ - PARMS_FREE(data->nnzschur); - } - - PARMS_FREE(data); - return 0; -} - -static int parms_ilu_view(parms_Operator self, parms_Viewer v) -{ - parms_ilu_data data; - int i, j, n, nnz, *pj; - FLOAT *pa; - FILE *fp; - - parms_ViewerGetFP(v, &fp); - data = (parms_ilu_data)self->data; - n = data->L->n; - fprintf(fp, "L part of the matrix:\n"); - fprintf(fp, "n = %d\n", n); -#if defined(DBL_CMPLX) - for (i = 0; i < n; i++) { - nnz = data->L->nnzrow[i]; - pj = data->L->pj[i]; - pa = data->L->pa[i]; - fprintf(fp, "nnzrow[%d]=%d\n", i, nnz); - for (j = 0; j < nnz; j++) { - fprintf(fp, "(%d,%d,%f %f) ", i, pj[j], creal(pa[j]), cimag(pa[j])); - } - } - n = data->U->n; - fprintf(fp, "U part of the matrix:\n"); - fprintf(fp, "n = %d\n", n); - for (i = 0; i < n; i++) { - nnz = data->U->nnzrow[i]; - pj = data->U->pj[i]; - pa = data->U->pa[i]; - fprintf(fp, "nnzrow[%d]=%d\n", i, nnz); - for (j = 0; j < nnz; j++) { - fprintf(fp, "(%d,%d,%f %f) ", i, pj[j], creal(pa[j]), cimag(pa[j])); - } - } -#else - for (i = 0; i < n; i++) { - nnz = data->L->nnzrow[i]; - pj = data->L->pj[i]; - pa = data->L->pa[i]; - fprintf(fp, "nnzrow[%d]=%d\n", i, nnz); - for (j = 0; j < nnz; j++) { - fprintf(fp, "(%d,%d,%f) ", i, pj[j], pa[j]); - } - } - n = data->U->n; - fprintf(fp, "U part of the matrix:\n"); - fprintf(fp, "n = %d\n", n); - for (i = 0; i < n; i++) { - nnz = data->U->nnzrow[i]; - pj = data->U->pj[i]; - pa = data->U->pa[i]; - fprintf(fp, "nnzrow[%d]=%d\n", i, nnz); - for (j = 0; j < nnz; j++) { - fprintf(fp, "(%d,%d,%f) ", i, pj[j], pa[j]); - } - } -#endif - parms_ViewerStoreFP(v, fp); - return 0; -} - -static void parms_ilu_nnz_vcsr(parms_Operator self, int *nnz_mat, int *nnz_pc) -{ - parms_ilu_data data; - - data = (parms_ilu_data)self->data; - *nnz_mat = data->nnz_mat; - *nnz_pc = data->nnz_prec; -} - -static int parms_ilu_lsol_vcsr(parms_Operator self, FLOAT *y, FLOAT - *x) -{ - parms_ilu_data data; - parms_vcsr L; - int n, i, j, nnz, *pj, start; - FLOAT *pa, t; - - data = (parms_ilu_data)self->data; - start = data->schur_start; - L = data->L; - n = L->n; - for (i = 0; i < start; i++) { - nnz = L->nnzrow[i]; - pj = L->pj[i]; - pa = L->pa[i]; - t = y[i]; - for (j = 0; j < nnz; j++) { - t -= pa[j] * x[pj[j]]; - } - x[i] = t; - } - - for (i = start; i < n; i++) { - pj = L->pj[i]; - pa = L->pa[i]; - t = y[i]; - nnz = data->nnzschur[i-start]; - for (j = 0; j < nnz; j++) { - t -= pa[j] * x[pj[j]]; - } - x[i] = t; - } - - return 0; -} - -static int parms_ilu_invs_vcsr(parms_Operator self, FLOAT *y, FLOAT - *x) -{ - parms_ilu_data data; - parms_vcsr L, U; - int n, i, j, nnz, start, *pj, nnzS; - FLOAT *pa, t, diag; - - data = (parms_ilu_data)self->data; - start = data->schur_start; - L = data->L; - n = L->n; - for (i = start; i < n; i++) { - nnz = L->nnzrow[i]; - pj = L->pj[i]; - pa = L->pa[i]; - t = y[i-start]; - nnzS = data->nnzschur[i-start]; - for (j = nnzS; j < nnz; j++) { - t -= pa[j] * x[pj[j]-start]; - } - x[i-start] = t; - } - - U = data->U; - for (i = n-1; i >= start; i--) { - nnz = U->nnzrow[i]; - pj = U->pj[i]; - pa = U->pa[i]; - t = x[i-start]; - diag = pa[0]; - for (j = 1; j < nnz; j++) { - t -= pa[j] * x[pj[j]-start]; - } - x[i-start] = t*diag; - } - return 0; -} - -static int parms_ilu_getu_vcsr(parms_Operator self, void **mat) -{ - parms_ilu_data data; - parms_vcsr U, S; - int i, n, start, nnz; - - data = (parms_ilu_data)self->data; - start = data->schur_start; - U = data->U; - *mat = U; - return 0; -} - - -static int parms_ilu_ascend_vcsr(parms_Operator self, FLOAT *y, FLOAT - *x) -{ - parms_ilu_data data; - parms_vcsr U; - int i, j, nnz, start, *pj; - FLOAT *pa, t, diag; - - data = (parms_ilu_data)self->data; - - U = data->U; - start = data->schur_start; - - for (i = start-1; i >= 0; i--) { - nnz = U->nnzrow[i]; - pj = U->pj[i]; - pa = U->pa[i]; - t = y[i]; - diag = pa[0]; - for (j = 1; j < nnz; j++) { - t -= pa[j] * x[pj[j]]; - } - x[i] = t*diag; - } - return 0; -} - -static int parms_ilu_sol_vcsr(parms_Operator self, FLOAT *y, - FLOAT *x) -{ - FLOAT *pa; - parms_ilu_data data; - parms_vcsr L, U; - int i, j, n, *pj, nnz; - - data = (parms_ilu_data)self->data; - L = data->L; - U = data->U; - n = L->n; - - for (i = 0; i < n; i++) { - // nnz = L->nnzrow[i]; - pj = L->pj[i]; - pa = L->pa[i]; - x[i] = y[i]; - - for (j = 0; j < L->nnzrow[i]; j++) { - x[i] -= pa[j] * x[pj[j]]; - } - } - - for (i = n-1; i >= 0; i--) { - // nnz = U->nnzrow[i]; - pj = U->pj[i]; - pa = U->pa[i]; - - for (j = 1; j < U->nnzrow[i]; j++) { - x[i] -= pa[j] * x[pj[j]]; - } - x[i] *= pa[0]; // pa[0] = diag - } - - return 0; -} - -static int parms_ilu_getssize_vcsr(parms_Operator self) -{ - parms_ilu_data data; - - data = (parms_ilu_data)self->data; - return data->schur_start; -} - -static struct parms_Operator_ops parms_ilu_sol_vptr = { - parms_ilu_sol_vcsr, - parms_ilu_lsol_vcsr, - parms_ilu_invs_vcsr, - parms_ilu_getu_vcsr, - parms_ilu_ascend_vcsr, - parms_ilu_getssize_vcsr, - parms_ilu_nnz_vcsr, - parms_ilu_free, - parms_ilu_view -}; - -int parms_ilu0_vcsr(parms_Mat self, parms_FactParam param, void *mat, - parms_Operator *op) -{ - /* - * it is assumed that the the elements in the input matrix are - * stored in such a way that in each row the lower part comes first - * and then the upper part. To get the correct ILU factorization, it - * is also necessary to have the elements of L sorted by increasing - * column number. It may therefore be necessary to sort the elements - * of a, ja, ia prior to calling ilu0. This can be achieved by - * transposing the matrix twice using csrcsc. - *------------------------------------------------------------------*/ - parms_Operator newOpt; - parms_ilu_data data; - parms_vcsr mat_vcsr; - parms_Map is; - int n, start, schur_start, m, *iw, ierr, ii, jj, i, j, k, nnz, lenl, lenu; - int *pj, *rowj, *rowjj, nloc, jcol, jpos, jrow, jw; - FLOAT *pa, *rowm, *rowmm, t1, shift, rnrm; -// int flag; - BOOL found; - int rank; - - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - - is = self->is; - n = param->n; - jw = n; /* initialize jw */ - nloc = parms_MapGetLocalSize(is); - start = param->start; - schur_start = param->schur_start; - if (schur_start == -1) { - schur_start = n; - } - - if (!param->isalloc) { - parms_OperatorCreate(&newOpt); - PARMS_MEMCPY(newOpt->ops, &parms_ilu_sol_vptr, 1); - PARMS_NEW(data); - PARMS_NEW(data->L); - data->L->n = n; - PARMS_NEWARRAY(data->L->nnzrow, n); - PARMS_NEWARRAY(data->L->pj, n); - PARMS_NEWARRAY(data->L->pa, n); - PARMS_NEW(data->U); - data->U->n = n; - PARMS_NEWARRAY(data->U->nnzrow, n); - PARMS_NEWARRAY(data->U->pj, n); - PARMS_NEWARRAY(data->U->pa, n); - data->nnz_mat = 0; - data->nnz_prec = 0; - param->isalloc = true; - - newOpt->data = data; - *op = newOpt; - } - else { - data = (*op)->data; - } - - mat_vcsr = (parms_vcsr)mat; - - PARMS_NEWARRAY(iw, n); - for(i = 0; i < n; i++) { - iw[i] = -1; - } - /* main loop */ - ierr = 0; - m = mat_vcsr->n; - - /* sort the column indices of the entries received from neighbouring - * processors in ascending order */ - for (ii = nloc; ii < m+start; ii++) { - nnz = mat_vcsr->nnzrow[ii]; - pj = mat_vcsr->pj[ii]; - pa = mat_vcsr->pa[ii]; - qqsort(pj, pa, 0, nnz-1); - } - - for(ii = 0; ii < m; ii++) { - nnz = mat_vcsr->nnzrow[ii]; - pj = mat_vcsr->pj[ii]; - pa = mat_vcsr->pa[ii]; - - /* calculating the number of entries in L and U */ - lenl = 0; - lenu = 0; - if (ii+start < schur_start) { - for(j = 0; j < nnz; j++) { - jcol = pj[j]; - if(jcol < ii+start) { - ++lenl; - } - else { - ++lenu; - } - } - } - else { - for(j = 0; j < nnz; j++) { - jcol = pj[j]; - if(jcol < schur_start) { - ++lenl; - } - else { - ++lenu; - } - } - } - - data->L->nnzrow[ii+start] = lenl; - - if(lenl > 0) { - PARMS_NEWARRAY(data->L->pj[ii+start], lenl); - PARMS_NEWARRAY(data->L->pa[ii+start], lenl); - } - if(lenu == 0) - { - if(lenl == 0){ - printf("myid %d: Zero row encountered at row %d \n", rank, ii); - MPI_Abort(MPI_COMM_WORLD, 10); - } - } - - data->U->nnzrow[ii+start] = lenu; -/* Assume diagonal element */ - PARMS_NEWARRAY(data->U->pj[ii+start], lenu+1); - PARMS_NEWARRAY(data->U->pa[ii+start], lenu+1); - - lenl = 0; - lenu = 0; - shift = 0.0; - rnrm = 0.0; - /* copy row ii into L and U part */ - if (ii+start < schur_start) { - lenu = 1; // Assume diagonal element - found = false; - data->U->pa[ii+start][0] = 0; - data->U->pj[ii+start][0] = ii+start; - iw[ii+start] = ii + start; - - for(j = 0; j < nnz; j++) { - jcol = pj[j]; - rnrm += ABS_VALUE(pa[j]); - shift = ABS_VALUE(shift) > ABS_VALUE(pa[j]) ? shift : pa[j]; - if(jcol < ii+start) { - data->L->pj[ii+start][lenl] = jcol; - data->L->pa[ii+start][lenl] = pa[j]; - iw[jcol] = lenl; - ++lenl; - } - else if(jcol == ii + start){ - /* Diagonal element */ - data->U->pa[ii+start][0] = pa[j]; - found = true; - } - else { - data->U->pj[ii+start][lenu] = jcol; - data->U->pa[ii+start][lenu] = pa[j]; - iw[jcol] = ii + start+ lenu; - ++lenu; - } - } -/* create room for diagonal element, if none. - * This just ensures that the fill-factor is 1. - */ - shift /= rnrm; - if(found == false){ - data->U->nnzrow[ii+start] += 1; - } -/* perturb zero or small diagonals - this is done later*/ -// else if(ABS_VALUE(data->U->pa[ii+start][0]) <= DBL_EPSILON) -// data->U->pa[ii+start][0] = 0.0;//shift; - } - else { - for(j = 0; j < nnz; j++) { - jcol = pj[j]; - if(jcol < schur_start) { - data->L->pj[ii+start][lenl] = jcol; - data->L->pa[ii+start][lenl] = pa[j]; - iw[jcol] = lenl; - ++lenl; - } - else { - data->U->pj[ii+start][lenu] = jcol; - data->U->pa[ii+start][lenu] = pa[j]; - iw[jcol] = schur_start + lenu; - ++lenu; - } - } - } - - /* exit if diagonal element is reached. */ - rowj = data->L->pj[ii+start]; - rowm = data->L->pa[ii+start]; - for(j = 0; j < lenl; j++) { -/*---------------------------------------------------------------------------- - * in order to do the elimination in the correct order we must select the - * smallest column index among rowj[k], k = j+1, ..., lenl - *--------------------------------------------------------------------------*/ - jrow = rowj[j]; - jpos = j; -/*---------- determine smallest column index */ - for( k = j + 1; k < lenl; k++ ) { - if( rowj[k] < jrow ) { - jrow = rowj[k]; - jpos = k; - } - } - if( jpos != j ) { -/*---------- swaps */ - jcol = rowj[j]; - rowj[j] = rowj[jpos]; - rowj[jpos] = jcol; - iw[jrow] = j; - iw[jcol] = jpos; - t1 = rowm[j]; - rowm[j] = rowm[jpos]; - rowm[jpos] = t1; - } - - /* Eliminate previous rows: jrow now contains the smallest - column index in L part - */ -// jrow = rowj[j]; - rowjj = data->U->pj[jrow]; - rowmm = data->U->pa[jrow]; - nnz = data->U->nnzrow[jrow]; - rowm[j] *= rowmm[0]; - t1 = rowm[j]; - /* perform linear combination */ - if (ii+start < schur_start) { - for(jj = 1; jj < nnz; jj++) { - jw = iw[rowjj[jj]]; - if(jw != -1) { - if(jw < ii+start) { - rowm[jw] -= t1*rowmm[jj]; - } - else { - data->U->pa[ii+start][jw-ii-start] -= t1*rowmm[jj]; - } - } - } - } - else { - for (jj = 1; jj < nnz; jj++) { - if(jw < schur_start) { - rowm[jw] -= t1*rowmm[jj]; - } - else { - data->U->pa[ii+start][jw-schur_start] -= t1*rowmm[jj]; - } - } - } - } - - if (ii+start < schur_start) { - t1 = data->U->pa[ii+start][0]; -/* Check for zero diagonal */ - if(ABS_VALUE(t1) <= DBL_EPSILON) { -/* ierr = ii+1; - printf("myid %d: Zero diagonal encountered at row %d \n", rank, ii); - MPI_Abort(MPI_COMM_WORLD, 10); -*/ -/* Perturb zero diagonal to be (maximum elimination coeff) */ - t1 = shift; - } - /* invert and store diagonal element. */ - data->U->pa[ii+start][0] = 1.0/t1; - } - /* reset pointer iw to -1 */ - rowj = data->L->pj[ii+start]; - for(i = 0; i < lenl; i++) { - iw[rowj[i]] = -1; - } - rowj = data->U->pj[ii+start]; - for(i = 0; i < lenu; i++) { - iw[rowj[i]] = -1; - } - } - - data->schur_start = is->schur_start; - data->n = n; - - if (n-data->schur_start) { - FLOAT *w; - PARMS_NEWARRAY(w, n); - PARMS_NEWARRAY(data->nnzschur, n-data->schur_start); - for (i = data->schur_start; i < n; i++) { - lenl = data->L->nnzrow[i]; - rowj = data->L->pj[i]; - rowm = data->L->pa[i]; - jw = 0; - - for (j = 0; j < lenl; j++) { - if (rowj[j] < data->schur_start) { - ++jw; - } - } - - data->nnzschur[i-data->schur_start] = jw; - - ii = 0; - jj = jw; - for (j = 0; j < lenl; j++) { - if (rowj[j] < data->schur_start) { - iw[ii] = rowj[j]; - w[ii++] = rowm[j]; - } - else { - iw[jj] = rowj[j]; - w[jj++] = rowm[j]; - } - } - PARMS_MEMCPY(rowj, iw, lenl); - PARMS_MEMCPY(rowm, w, lenl); - } - PARMS_FREE(w); - } - PARMS_FREE(iw); - - /* compute the number of nonzeros in matrix */ - for (i = 0; i < m; i++) { - data->nnz_mat += mat_vcsr->nnzrow[i]; - } - - /* compute the number of nonzeros in pc */ - for (i = 0; i < m; i++) { - data->nnz_prec += data->L->nnzrow[i]; - data->nnz_prec += data->U->nnzrow[i]; - } - return ierr; -} - -#define MIN(a, b) (a) > (b) ? (b) : (a) -#define MAX_ROWS_PER_COL 20 -int parms_iluk_vcsr(parms_Mat self, parms_FactParam param, void *mat, - parms_Operator *op) -{ - /* - * ON RETURN - *=========== - * ierr = integer. Error message with the following meaning. - * ierr = 0 --> successful return. - * ierr > 0 --> zero pivot encountered at step - * number ierr. - * ierr = -1 --> Error. input matrix may be wrong. - * (The elimination process has - * generated a row in L or U whose - * length is greater than n) - * ierr = -2 --> Illegal value for lfil. - * ierr = -3 --> zero row encountered in A or U - *------------------------------------------------------------------*/ - parms_Operator newOpt; - parms_ilu_data data; - parms_vcsr mat_vcsr; - parms_Map is; - int n, start, schur_start, m, nnz, lfil, *jw, **levs, n2, *pj, ierr; - int lenl, lenu, ii, jj, i, j, k, jcol, jpos, jrow, *rowj, *lev, jlev; - FLOAT *w, *pa, *rowm, t, s, fact, shift, rnrm; -// int flag; -// BOOL found; - int rank; - int lenl_all, lenu_all, max_entr; - - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - - is = self->is; - n = param->n; - start = param->start; - schur_start = param->schur_start; - if (schur_start == -1) { - schur_start = n; - } - - if (!param->isalloc) { -// global data arrays, F77 maximum size... - max_entr = n*MAX_ROWS_PER_COL; - parms_OperatorCreate(&newOpt); - PARMS_MEMCPY(newOpt->ops, &parms_ilu_sol_vptr, 1); - PARMS_NEW(data); - PARMS_NEW(data->L); - data->L->n = n; - PARMS_NEWARRAY(data->L->nnzrow, n); - PARMS_NEWARRAY(data->L->pj, n); - PARMS_NEWARRAY(data->L->pa, n); -// allocate global data arrays - PARMS_NEWARRAY(data->L->pj_data, max_entr); - PARMS_NEWARRAY(data->L->pa_data, max_entr); - PARMS_NEW(data->U); - data->U->n = n; - PARMS_NEWARRAY(data->U->nnzrow, n); - PARMS_NEWARRAY(data->U->pj, n); - PARMS_NEWARRAY(data->U->pa, n); -// allocate global data arrays - PARMS_NEWARRAY(data->U->pj_data, max_entr); - PARMS_NEWARRAY(data->U->pa_data, max_entr); - data->nnz_mat = 0; - data->nnz_prec = 0; - param->isalloc = true; - - newOpt->data = data; - *op = newOpt; - } - else { - data = (*op)->data; - } - - mat_vcsr = (parms_vcsr)mat; - - lfil = param->ipar[0]; - - if(lfil < 0) { - ierr = -2; - printf("myid %d: lfil parameter must be > zero: ierr %d \n", rank, ierr); - MPI_Abort(MPI_COMM_WORLD, 11); - } - - /* malloc memory for working arrays levs, w and jw */ - PARMS_NEWARRAY(levs, n); - PARMS_NEWARRAY(w, n); - PARMS_NEWARRAY(jw, 3*n); - - /* main loop */ - n2 = n << 1; - for(i = n; i < n2; i++) { - jw[i] = -1; - jw[n+i] = 0; - } - m = mat_vcsr->n; - ierr = 0; - lenl_all = 0; - lenu_all = 0; - for(ii = 0; ii < m; ii++) { - lenl = 0; - lenu = 0; - shift = 0.0; - rnrm = 0.0; - nnz = mat_vcsr->nnzrow[ii]; - pj = mat_vcsr->pj[ii]; - pa = mat_vcsr->pa[ii]; - if (start+ii < schur_start) { - lenu = 1; -// found = false; - jw[ii+start] = ii + start; - w[ii+start] = 0.0; - jw[n+ii+start] = ii + start; - - for(j = 0; j < nnz; j++) { - jcol = pj[j]; - t = pa[j]; - if(ABS_VALUE(t) <= DBL_EPSILON) continue; - rnrm += ABS_VALUE(t); - shift = ABS_VALUE(shift) > ABS_VALUE(t) ? shift : t; - if(jcol < ii+start) { - jw[lenl] = jcol; - w[lenl] = t; - jw[n2+lenl] = 0; - jw[n+jcol] = lenl; - ++lenl; - } - else if(jcol == ii+start) { - w[ii+start] = t; - jw[n2+ii+start] = 0; -// found = true; - } - else { - jpos = ii + start + lenu; - jw[jpos] = jcol; - w[jpos] = t; - jw[n2+jpos] = 0; - jw[n+jcol] = jpos; - ++lenu; - } - } -/* perturb zero or small diagonals */ - shift /= rnrm; -/* if(found == false){ - w[ii+start] = shift; - jw[n2+ii+start] = 0; - }*/ - } - else { - for(j = 0; j < nnz; j++) { - jcol = pj[j]; - t = pa[j]; - if(ABS_VALUE(t) <= DBL_EPSILON) continue; - if(jcol < schur_start) { - jw[lenl] = jcol; - w[lenl] = t; - jw[n2+lenl] = 0; - jw[n+jcol] = lenl; - ++lenl; - } - else { - jpos = schur_start + lenu; - jw[jpos] = jcol; - w[jpos] = t; - jw[n2+jpos] = 0; - jw[n+jcol] = jpos; - ++lenu; - } - } - } - - /* eliminate previous rows */ - for(jj = 0; jj < lenl; jj++) { - /* in order to do the elimination in the correct order we must - select the smallest column index among jw[k], k=jj,...lenl */ - jrow = jw[jj]; - k = jj; - /* determine smallest column index */ - for(j = jj+1; j < lenl; j++) { - if(jw[j] < jrow) { - jrow = jw[j]; - k = j; - } - } - if(k != jj) { - /* exchange in jw */ - j = jw[jj]; - jw[jj] = jw[k]; - jw[k] = j; - /* exchange in jw(n+ (pointers/ nonzero indicator). */ - jw[n+jrow] = jj; - jw[n+j] = k; - /* exchange in jw(n2 + (levels) */ - j = jw[n2+jj]; - jw[n2+jj] = jw[n2+k]; - jw[n2+k] = j; - /* exchange in w */ - s = w[jj]; - w[jj] = w[k]; - w[k] = s; - } - /* zero out element in row by resetting jw[n+jrow] to zero */ - jw[n+jrow] = -1; - /* get the multiplier for row to be eliminated (jrow) + its - level */ - rowj = data->U->pj[jrow]; - rowm = data->U->pa[jrow]; - nnz = data->U->nnzrow[jrow]; - lev = levs[jrow]; - fact = w[jj]*rowm[0]; - jlev = jw[n2+jj]; - if(jlev > lfil) continue; - /* combine current row and row jrow */ - if (ii+start < schur_start) { - for(k = 1; k < nnz; k++) { - s = fact*rowm[k]; - jcol = rowj[k]; - jpos = jw[n+jcol]; - if(jcol >= ii+start) { - /* dealing with upper part */ - if(jpos == -1) { - /* this is a fill-in element */ - i = ii + start + lenu; - jw[i] = jcol; - jw[n+jcol] = i; - w[i] = -s; - jw[n2+i] = jlev+lev[k]+1; - ++lenu; - if(lenu > n) { - ierr = -1; - printf("myid %d: Error in ILUK (nnz(U) > n): ierr = %d \n", rank, ierr); - MPI_Abort(MPI_COMM_WORLD, 12); - } - } - else { - /* this is not a fill-in element */ - w[jpos] -= s; - jw[n2+jpos] = MIN(jw[n2+jpos], jlev+lev[k]+1); - } - } - else { - /* dealing with lower part */ - if(jpos == -1) { - /* this is a fill-in element */ - jw[lenl] = jcol; - jw[n+jcol] = lenl; - w[lenl] = -s; - jw[n2+lenl] = jlev+lev[k]+1; - ++lenl; - if(lenl > n) { - ierr = -1; - break; - } - } - else { - /* this is not a fill-in element */ - w[jpos] -= s; - jw[n2+jpos] = MIN(jw[n2+jpos], jlev+lev[k]+1); - } - } - } - } - else { - for(k = 1; k < nnz; k++) { - s = fact*rowm[k]; - jcol = rowj[k]; - jpos = jw[n+jcol]; - if(jcol >= schur_start) { - /* dealing with upper part */ - if(jpos == -1) { - /* this is a fill-in element */ - i = schur_start + lenu; - jw[i] = jcol; - jw[n+jcol] = i; - w[i] = -s; - jw[n2+i] = jlev+lev[k]+1; - ++lenu; - if(lenu > n) { - ierr = -1; - break; - } - } - else { - /* this is not a fill-in element */ - w[jpos] -= s; - jw[n2+jpos] = MIN(jw[n2+jpos], jlev+lev[k]+1); - } - } - else { - /* dealing with lower part */ - if(jpos == -1) { - /* this is a fill-in element */ - jw[lenl] = jcol; - jw[n+jcol] = lenl; - w[lenl] = -s; - jw[n2+lenl] = jlev+lev[k]+1; - ++lenl; - if(lenl > n) { - ierr = -1; - break; - } - } - else { - /* this is not a fill-in element */ - w[jpos] -= s; - jw[n2+jpos] = MIN(jw[n2+jpos], jlev+lev[k]+1); - } - } - } - } - w[jj] = fact; - jw[jj] = jrow; - } - /* reset double-pointer to zero (U-part) */ - if (ii+start < schur_start) { - for(k = 0; k < lenu; k++) { - jw[n+jw[ii+start+k]] = -1; - } - } - else { - for(k = 0; k < lenu; k++) { - jw[n+jw[schur_start+k]] = -1; - } - - } - - /* update l-matrix */ - j = 0; - for(k = 0; k < lenl; k++) { - if(jw[n2+k] <= lfil) { - ++j; - } - } - data->L->nnzrow[ii+start] = j; - if(j > 0) { - // PARMS_NEWARRAY(data->L->pj[ii+start], j); - // PARMS_NEWARRAY(data->L->pa[ii+start], j); - data->L->pj[ii+start] = data->L->pj_data + lenl_all; - data->L->pa[ii+start] = data->L->pa_data + lenl_all; - lenl_all += j; - if (lenl_all > max_entr) { - printf("parms_ilu_vcsr.c, parms_iluk_vcsr: set larger MAX_ROWS_PER_COL and recompile\n"); - MPI_Abort(MPI_COMM_WORLD, 13); - } - } - rowj = data->L->pj[ii+start]; - rowm = data->L->pa[ii+start]; - j = 0; - for(k = 0; k < lenl; k++) { - if(jw[n2+k] <= lfil) { - rowj[j] = jw[k]; - rowm[j] = w[k]; - ++j; - } - } - - if (ii+start < schur_start) { -/* update u-matrix */ - if(ABS_VALUE(w[ii+start]) <= DBL_EPSILON) { -/* ierr = ii+1; - printf("myid %d: Zero diagonal encountered at row %d \n", rank, ii); - MPI_Abort(MPI_COMM_WORLD, 13); -*/ -/* Perturb zero diagonal to be (maximum elimination coeff) */ - w[ii+start] = shift; - } - j = 1; - for(k = ii+1; k < ii+lenu; k++) { - if(jw[n2+start+k] <= lfil) { - ++j; - } - } - } - else { - j = 0; - for(k = schur_start; k < schur_start+lenu; k++) { - if(jw[n2+k] <= lfil) { - ++j; - } - } - } - - data->U->nnzrow[ii+start] = j; - // PARMS_NEWARRAY(data->U->pj[ii+start], j); - data->U->pj[ii+start] = data->U->pj_data + lenu_all; - rowj = data->U->pj[ii+start]; - // PARMS_NEWARRAY(data->U->pa[ii+start], j); - data->U->pa[ii+start] = data->U->pa_data + lenu_all; - rowm = data->U->pa[ii+start]; - lenu_all += j; - - if (lenu_all > max_entr) { - printf("parms_ilu_vcsr.c, parms_iluk_vcsr: set larger MAX_ROWS_PER_COL and recompile\n"); - MPI_Abort(MPI_COMM_WORLD, 13); - } - - PARMS_NEWARRAY(levs[ii+start], j); - lev = levs[ii+start]; - if (ii+start < schur_start) { - rowm[0] = 1.0 / w[ii+start]; - rowj[0] = ii+start; - j = 1; - for(k = ii+1; k < ii+lenu; k++) { - if(jw[n2+start+k] <= lfil) { - rowj[j] = jw[start+k]; - rowm[j] = w[start+k]; - lev[j] = jw[n2+start+k]; - ++j; - } - } - } - else { - j = 0; - for(k = schur_start; k < schur_start+lenu; k++) { - if(jw[n2+k] <= lfil) { - rowj[j] = jw[k]; - rowm[j] = w[k]; - lev[j] = jw[n2+k]; - ++j; - } - } - } - } - for(i = 0; i < m; i++) { - if (levs[i+start]) { - PARMS_FREE(levs[i+start]); - } - } - PARMS_FREE(levs); - - data->schur_start = is->schur_start; - data->n = n; - - if (n-data->schur_start) { - PARMS_NEWARRAY(data->nnzschur, n-data->schur_start); - for (i = data->schur_start; i < n; i++) { - lenl = data->L->nnzrow[i]; - rowj = data->L->pj[i]; - rowm = data->L->pa[i]; - k = 0; - - for (j = 0; j < lenl; j++) { - if (rowj[j] < data->schur_start) { - ++k; - } - } - data->nnzschur[i-data->schur_start] = k; - ii = 0; - jj = k; - for (j = 0; j < lenl; j++) { - if (rowj[j] < data->schur_start) { - jw[ii] = rowj[j]; - w[ii++] = rowm[j]; - } - else { - jw[jj] = rowj[j]; - w[jj++] = rowm[j]; - } - } - PARMS_MEMCPY(rowj, jw, lenl); - PARMS_MEMCPY(rowm, w, lenl); - } - } - - PARMS_FREE(w); - PARMS_FREE(jw); - - /* compute the number of nonzeros in matrix */ - for (i = 0; i < m; i++) { - data->nnz_mat += mat_vcsr->nnzrow[i]; - } - - /* computer the number of nonzeros in pc */ - for (i = 0; i < m; i++) { - data->nnz_prec += data->L->nnzrow[i]; - data->nnz_prec += data->U->nnzrow[i]; - } -// printf("nnzmat = %d, nnzprec = %d \n",data->nnz_mat, data->nnz_prec); - return ierr; -} - -int parms_ilut_vcsr(parms_Mat self, parms_FactParam param, void *mat, - parms_Operator *op) -{ - parms_Operator newOpt; - parms_ilu_data data; - parms_vcsr mat_vcsr; - parms_Map is; - int i, j, j1, j2, ii, jj, n, m, fill, start, schur_start, *jw; - int lenl, lenu, len, k, jpos, jrow, jcol, ierr, nnz, *rowj; - - REAL dt, tnorm; - FLOAT *w, *rowm, ft, s, t; - -#if defined(DBL_CMPLX) - double shf = 0.0, ti, sgny; - int nnzA = 0; -/*----------get nnz of mat ---------*/ - for(j = 0; j<((parms_vcsr)mat)->n; j++) - nnzA += ((parms_vcsr)mat)->nnzrow[j]; -#endif - - is = self->is; - n = param->n; - start = param->start; - schur_start = param->schur_start; - - if (schur_start == -1) { - schur_start = n; - } - - if (!param->isalloc) { - parms_OperatorCreate(&newOpt); - PARMS_MEMCPY(newOpt->ops, &parms_ilu_sol_vptr, 1); - PARMS_NEW(data); - PARMS_NEW(data->L); - data->L->n = n; - PARMS_NEWARRAY(data->L->nnzrow, n); - PARMS_NEWARRAY(data->L->pj, n); - PARMS_NEWARRAY(data->L->pa, n); - PARMS_NEW(data->U); - data->U->n = n; - PARMS_NEWARRAY(data->U->nnzrow, n); - PARMS_NEWARRAY(data->U->pj, n); - PARMS_NEWARRAY(data->U->pa, n); - data->nnz_mat = 0; - data->nnz_prec = 0; - param->isalloc = true; - - newOpt->data = data; - *op = newOpt; - } - else { - - data = (*op)->data; - - } - - mat_vcsr = (parms_vcsr)mat; - - /* malloc memory for working arrays w and jw */ - PARMS_NEWARRAY(w, 2*n); - PARMS_NEWARRAY(jw, 2*n); - - /* initialize nonzero indicator array */ - for(j = 0; j < n; j++) { - jw[n+j] = -1; - } - - fill = param->lfil[0]; - dt = param->droptol[0]; - - /* beginning of main loop */ - ierr = 0; - m = mat_vcsr->n; - for (ii = 0; ii < m; ii++) { - tnorm = 0.0; - j1 = 0; - j2 = mat_vcsr->nnzrow[ii]; - for (k = 0; k < j2; k++) { - tnorm += ABS_VALUE(mat_vcsr->pa[ii][k]); - } - if (ABS_VALUE(tnorm) < 1.0e-20) { - printf("ilut: zero row encountered at row %d.\n", ii ); - ierr = -3; - break; - } - /* unpack L-part and U-part of row of A in array w */ - lenu = 0; - lenl = 0; - if (ii+start < schur_start) { - jw[ii+start] = ii+start; - w[ii+start] = 0.0; - jw[n+ii+start] = ii+start; - lenu = 1; - for (j = j1; j < j2; j++) { - jcol = mat_vcsr->pj[ii][j]; - t = mat_vcsr->pa[ii][j]; - if (jcol < ii+start) { - jw[lenl] = jcol; - w[lenl] = t; - jw[n+jcol] = lenl; - ++lenl; - } - else if(jcol == ii+start){ - w[ii+start] = t; - } - else { - jpos = ii + lenu + start; - jw[jpos] = jcol; - w[jpos] = t; - jw[n+jcol] = jpos; - ++lenu; - } - } -#if defined(DBL_CMPLX) -/*---------------- Add complex shift -- for complex case----------*/ -/*------ shift based on droptol -- tau-based shift ----*/ - shf = dt*tnorm; -/* ----- shift based on diagonal dominance gap -- dd-based shift -----*/ -// shf = schur_start*(tnorm - 2*cabs(w[ii+start]))/(double)nnzA; // rownorm includes diagonal - ti = cimag(w[ii+start]) ; - if (ti<=0) - sgny = -ti - sqrt(ti*ti+shf*shf) ; - else - sgny = -ti + sqrt(ti*ti+shf*shf) ; - - w[ii+start] = w[ii+start] + sgny*I; -#endif - - tnorm /= (j2-j1); - } - else { - for (j = j1; j < j2; j++) { - jcol = mat_vcsr->pj[ii][j]; - t = mat_vcsr->pa[ii][j]; - if (jcol < schur_start) { - jw[lenl] = jcol; - w[lenl] = t; - jw[n+jcol] = lenl; - ++lenl; - } - else { - jpos = schur_start + lenu; - jw[jpos] = jcol; - w[jpos] = t; - jw[n+jcol] = jpos; - ++lenu; - } - } - } - - len = 0; - /* eliminate previous rows */ - for (jj = 0; jj < lenl; jj++) { - /* in order to do the elimination in the correct order we must - select the smallest column index among jw[k], - k=jj+1,...,lenl.*/ - jrow = jw[jj]; - k = jj; - for (j = jj+1; j < lenl; j++) { - if (jw[j] < jrow) { - jrow = jw[j]; - k = j; - } - } - if (k != jj) { - /* exchange in jw */ - j = jw[jj]; - jw[jj] = jw[k]; - jw[k] = j; - /* exchange in jr */ - jw[n+jrow] = jj; - jw[n+j] = k; - /* exchange in w */ - s = w[jj]; - w[jj] = w[k]; - w[k] = s; - } - jw[n+jrow] = -1; - /* get the multiplier for row to be eliminated (jrow) */ - rowm = data->U->pa[jrow]; - ft = w[jj]*rowm[0]; - if (ABS_VALUE(ft) <= dt) { - continue; - } - rowj = data->U->pj[jrow]; - nnz = data->U->nnzrow[jrow]; - /* combine current row and row jrow */ - /* if fill-in element is small then disregard */ - if (ii+start < schur_start) { /* ilut */ - for (k = 1; k < nnz; k++) { - s = ft*rowm[k]; - jcol = rowj[k]; - jpos = jw[n+jcol]; - - if (jcol >= ii+start) { - /* dealing with upper part */ - if (jpos == -1) { - /* this is a fill-in element */ - i = ii+start+lenu; - jw[i] = jcol; - jw[n+jcol] = i; - w[i] = -s; - ++lenu; - if(lenu > n) { - ierr = -1; - goto done; - } - } - else { - /* this is not a fill-in element */ - w[jpos] -= s; - } - } - else { - /* dealing with lower part */ - if(jpos == -1) { - /* this is a fill-in element */ - jw[lenl] = jcol; - w[lenl] = -s; - jw[n+jcol] = lenl; - ++lenl; - if(lenl > n) { - ierr = -1; - goto done; - } - } - else { - w[jpos] -= s; - } - } - } - } - else { /* partial ilut */ - for (k = 1; k < nnz; k++) { - s = ft*rowm[k]; - jcol = rowj[k]; - jpos = jw[n+jcol]; - - if (jcol >= schur_start) { - /* dealing with upper part */ - if (jpos == -1) { - /* this is a fill-in element */ - i = schur_start+lenu; - jw[i] = jcol; - jw[n+jcol] = i; - w[i] = -s; - ++lenu; - if(lenu > n) { - ierr = -1; - goto done; - } - } - else { - /* this is not a fill-in element */ - w[jpos] -= s; - } - } - else { - /* dealing with lower part */ - if(jpos == -1) { - /* this is a fill-in element */ - jw[lenl] = jcol; - w[lenl] = -s; - jw[n+jcol] = lenl; - ++lenl; - if(lenl > n) { - ierr = -1; - goto done; - } - } - else { - w[jpos] -= s; - } - } - } - } - - /* store this pivot element -- (from left to right -- no danger - of overlap with the working elements in L (pivots). */ - w[len] = ft; - jw[len] = jrow; - ++len; - } - - /* reset double-pointer to -1 (U-part) */ - if (ii+start < schur_start) { - for (k = 0; k < lenu; k++) { - jw[n+jw[ii+start+k]] = -1; - } - } - else { - for (k = 0; k < lenu; k++) { - jw[n+jw[schur_start+k]] = -1; - } - } - - /* update l-mat_vcsr */ - lenl = len > fill ? fill : len; - data->L->nnzrow[ii+start] = lenl; - - /* weigh the elements before sorting */ -#if 1 - for (k = 0; k < len; k++) { - w[k] *= (dt+w[n+jw[k]]); - } -#endif - /* quick sort */ - if (len > lenl) { - qsplitCF(w,jw,len,lenl); - } - - if (lenl > 0) { - PARMS_NEWARRAY(data->L->pj[start+ii], lenl); - rowj = data->L->pj[start+ii]; - PARMS_MEMCPY(rowj, jw, lenl); - PARMS_NEWARRAY(data->L->pa[start+ii], lenl); - rowm = data->L->pa[start+ii]; - /* PARMS_MEMCPY(rowm, w, lenl); */ - /* GCOPY(lenl, w, incx, rowm, incx); */ - } - -#if 1 - for (k = 0; k < lenl; k++) { - rowm[k] = w[k] / (dt + w[n+jw[k]]); - } -#endif - - /* update u-part */ - if (ii+start < schur_start) { - len = 0; - for (k = 1; k < lenu; k++) { - if (ABS_VALUE(w[ii+k]) > ABS_VALUE(dt*w[ii])) { - /* - if (ABS_VALUE(w[ii+start+k]) > dt*tnorm) { - */ - ++len; - w[ii+start+len] = w[ii+start+k]; - jw[ii+start+len] = jw[ii+start+k]; - } - } - lenu = len + 1 > fill ? fill: len + 1; - jpos = lenu - 1; - if (len > jpos) - qsplitCF(&w[ii+start+1], &jw[ii+start+1], len, jpos); - - data->U->nnzrow[start+ii] = lenu; - - PARMS_NEWARRAY(data->U->pa[start+ii], lenu); - rowm = data->U->pa[start+ii]; - - PARMS_NEWARRAY(data->U->pj[start+ii], lenu); - rowj = data->U->pj[start+ii]; - - /* copy the rest of U */ - if (jpos) { - PARMS_MEMCPY(&rowj[1], &jw[ii+start+1], jpos); - PARMS_MEMCPY(&rowm[1], &w[ii+start+1], jpos); - } - t = ABS_VALUE(w[ii+start]); - for (k = 1; k < lenu; k++) { - t += ABS_VALUE(w[ii+start+k]); - } - w[n+ii+start] = t / (FLOAT)(lenu+1); - /* store inverse of diagonal element of u */ - if (ABS_VALUE(w[ii+start]) < 1.0e-20) { - w[ii+start] = tnorm;//(0.0001 + dt)*tnorm; - } - rowm[0] = 1.0 / w[ii+start]; - rowj[0] = jw[ii+start]; - } - else if (ii >= schur_start) { - data->U->nnzrow[start+ii] = lenu; - PARMS_NEWARRAY(data->U->pj[start+ii], lenu); - PARMS_NEWARRAY(data->U->pa[start+ii], lenu); - PARMS_MEMCPY(data->U->pj[start+ii], &jw[schur_start], lenu); - PARMS_MEMCPY(data->U->pa[start+ii], &w[schur_start], lenu); - } - } - - done: - - data->schur_start = is->schur_start; - data->n = n; - - if (n-data->schur_start) { - PARMS_NEWARRAY(data->nnzschur, n-data->schur_start); - for (i = data->schur_start; i < n; i++) { - lenl = data->L->nnzrow[i]; - rowj = data->L->pj[i]; - rowm = data->L->pa[i]; - k = 0; - for (j = 0; j < lenl; j++) { - if (rowj[j] < data->schur_start) { - ++k; - } - } - data->nnzschur[i-data->schur_start] = k; - j1 = 0; - j2 = k; - for (j = 0; j < lenl; j++) { - if (rowj[j] < data->schur_start) { - jw[j1] = rowj[j]; - w[j1++] = rowm[j]; - } - else { - jw[j2] = rowj[j]; - w[j2++] = rowm[j]; - } - } - PARMS_MEMCPY(rowj, jw, lenl); - PARMS_MEMCPY(rowm, w, lenl); - } - } - - PARMS_FREE(w); - PARMS_FREE(jw); - - /* compute the number of nonzeros in matrix */ - for (i = 0; i < m; i++) { - data->nnz_mat += mat_vcsr->nnzrow[i]; - } - - /* computer the number of nonzeros in pc */ - for (i = 0; i < m; i++) { - data->nnz_prec += data->L->nnzrow[i]; - data->nnz_prec += data->U->nnzrow[i]; - } - - return ierr; -} - -/* reuse LU-Facorization (AF) */ -int parms_ilu_update(parms_Mat self, parms_FactParam param, void *mat, - parms_Operator *op) -{ - parms_Operator newOpt; - parms_ilu_data data; - parms_vcsr mat_vcsr; - parms_Map is; - int n, start, schur_start, m, iw,*rowjj, nnz, lfil, *jw, **levs, n2, *pj, ierr; - int lenl, lenu, ii, jj, i, j, k, jcol, jpos, jrow, *rowj, *rowi, *lev, jlev; - FLOAT *w, *pa, *rowm,*rowmm, t, s, fact, shift, rnrm,t1, t2; - int rank; - - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - - is = self->is; - data = (*op)->data; - n = data->L->n; - start = param->start; - schur_start = param->schur_start; - if (schur_start == -1) { - schur_start = n; - } - - mat_vcsr = (parms_vcsr)mat; - - PARMS_NEWARRAY(jw, n); - for(i = 0; i < n; i++) - jw[i] = -1; - - for(ii = 0; ii < n; ii++){ - - lenl = data->L->nnzrow[ii+start]; - rowj = data->L->pj[ii+start]; - rowm = data->L->pa[ii+start]; - for(j = 0; j < lenl; j++){ - jw[rowj[j]] = j; - rowm[j] = 0.; - } - - lenu = data->U->nnzrow[ii+start]; - rowjj = data->U->pj[ii+start]; - rowmm = data->U->pa[ii+start]; - if(ii + start < schur_start) - for(jj = 0; jj < lenu; jj++){ - jw[rowjj[jj]] = jj + ii + start; - rowmm[jj] = 0.; - } - else - for(jj = 0; jj < lenu; jj++){ - jw[rowjj[jj]] = jj + schur_start; - rowmm[jj] = 0.; - } - - - shift = 0.; - rnrm = 0.; - - nnz = mat_vcsr->nnzrow[ii]; - pj = mat_vcsr->pj[ii]; - pa = mat_vcsr->pa[ii]; - - /*copy row ii in L and U part */ - if(ii + start < schur_start){ - - for(j = 0; j < nnz; j++){ - rnrm += ABS_VALUE(pa[j]); - shift = ABS_VALUE(shift) > ABS_VALUE(pa[j]) ? shift : pa[j]; - jcol = jw[pj[j]]; - if(jcol < 0) - printf("jw %d\n",jcol); - else - if(jcol < ii + start) - rowm[jcol] = pa[j]; - else - rowmm[jcol-ii-start] = pa[j]; - } - shift /= rnrm; - - - } - else{ - for(j = 0; j < nnz; j++){ - jcol = jw[pj[j]]; - if(jcol < schur_start) - rowm[jcol] = pa[j]; - else - rowmm[jcol-schur_start] = pa[j]; - } - } - - - for(j = 0; j < lenl; j++){ - - jrow = rowj[j]; - jpos = j; - - /* find minimal column index k in row i */ - for(k = j+1; k < lenl; k++) - if(rowj[k] < jrow){ - jrow = rowj[k]; - jpos = k; - } - - if(jpos != j){ - jcol = rowj[j]; - rowj[j] = rowj[jpos]; - rowj[jpos] = jcol; - jw[jrow] = j; - jw[jcol] = jpos; - t1 = rowm[j]; - rowm[j] = rowm[jpos]; - rowm[jpos] = t1; - } - - /* a_{ik} := a_{ik}/a_{kk} */ - nnz = data->U->nnzrow[jrow]; - rowjj = data->U->pj[jrow]; - rowmm = data->U->pa[jrow]; - rowm[j] *= rowmm[0]; - t1 = rowm[j]; - if(ABS_VALUE(t1) < DBL_EPSILON) continue; - - if( ii+start < schur_start ){ - for(jj = 1; jj < nnz; jj++) { - iw = jw[rowjj[jj]]; - if(iw != -1) { - if(iw < ii+start) - rowm[iw] -= t1*rowmm[jj]; - else - data->U->pa[ii+start][iw-ii-start] -= t1*rowmm[jj]; - } - } - } - else { - for(jj = 1; jj < nnz; jj++){ - iw = jw[rowjj[jj]]; - if(iw < schur_start) - data->L->pa[ii+start][iw] -= t1*rowmm[jj]; - else - data->U->pa[ii+start][iw-schur_start] -= t1*rowmm[jj]; - } - } - } - if(ii + start < schur_start){ - t1 = data->U->pa[ii+start][0]; - if(ABS_VALUE(t1) < DBL_EPSILON) - t1 = shift; - data->U->pa[ii+start][0] = 1./t1; - } - - for(i = 0; i < lenl; i++) - jw[rowj[i]] = -1; - rowjj = data->U->pj[ii+start]; - for(i = 0; i < lenu; i++) - jw[rowjj[i]] = -1; - } - - - if(n - data->schur_start){ - PARMS_NEWARRAY0(w, n); - for(i = data->schur_start; i < n; i++){ - lenl = data->L->nnzrow[i]; - rowj = data->L->pj[i]; - rowm = data->L->pa[i]; - k = data->nnzschur[i-data->schur_start]; - ii = 0; - jj = k; - for(j = 0; j < lenl; j++) - if(rowj[j] < data->schur_start){ - jw[ii] = rowj[j]; - w[ii++] = rowm[j]; - } - else{ - jw[jj] = rowj[j]; - w[jj++] = rowm[j]; - } - PARMS_MEMCPY(rowj, jw,lenl); - PARMS_MEMCPY(rowm, w, lenl); - } - PARMS_FREE(w); - } - - PARMS_FREE(jw); - return 0; -} diff --git a/lib/parms/src/parms_map.c b/lib/parms/src/parms_map.c deleted file mode 100755 index 35a3ac23f..000000000 --- a/lib/parms/src/parms_map.c +++ /dev/null @@ -1,759 +0,0 @@ -/*-------------------------------------------------------------------- - parms_MapCreateFromLocal : create a local parms_Map object. - parms_MapCreateFromGlobal : create a parms_Map object based on the - output of Metis. - parms_MapCreateFromDist : create a parms_Map object based on the - output of ParMetis. - parms_MapCreateFromPetsc : create a parms_Map object based on the - default partitoning strategy in PETSc - (contiguous chunks of rows are - distributed among PEs) - parms_MapCreateFromPtr : create a parms_Map object based on a - list of all vars and a pointer array to - the beginning location of each PE in the - list. - parms_MapGetGlobalSize : get global size of data partitioned. - parms_MapGetLocalSize : get the size of data on the local PE. - parms_MapGetNumProcs : get the number of PEs. - parms_MapGetPid : get id of PE. - parms_MapGlobalToLocal : get the local index of a var with its - global index as the input. - parms_MapView : dump the parms_Map object. - parms_MapFree : free the memory for the parms_Map object. - - A code fragment for using parms_Map functions: - - parms_Map map; - parms_Mat mat; - - // partition the graph or mesh using Metis - METIS_PartGraphVKway(&n, ..., riord); - - // create a parms_Map object based on the output of Metis - parms_MapCreateFromGlobal(&map, n, riord, ...); - - // create vector and matrix object based on map - parms_MatCreate(&mat, map); - - $Id: parms_map.c,v 1.1.1.1 2006-11-27 22:28:01 zzli Exp $ ---------------------------------------------------------------------*/ - -#include "include/parms_map_impl.h" - -/** - * Free the memory for the parms_Map object pointed to by self. - * - * @param self A pointer to the parms_Map object. - * - * @return 0 on success. - */ -int parms_MapFree(parms_Map *self) -{ - (*self)->ref--; - /* if the reference number is zero, then remove the object */ - if ((*self)->ref == 0 ) { - if (!(*self)->isserial) { - if((*self)->lvars) PARMS_FREE((*self)->lvars); - if((*self)->vsend) PARMS_FREE((*self)->vsend); - if((*self)->vstable)parms_TableFree(&(*self)->vstable); - parms_TableFree(&(*self)->table); - MPI_Comm_free(&(*self)->comm); - } - - if ((*self)->ispermalloc) { - PARMS_FREE((*self)->perm); - PARMS_FREE((*self)->iperm); - } - PARMS_FREE(*self); - } - return 0; -} - -/** - * Dump the parms_Map object. - * - * @param self A parms_Map object. - * @param v A parms_Viewer object. - * - * @return 0 on success. - */ -int parms_MapView(parms_Map self, parms_Viewer v) -{ - FILE *fp; - int i, *val; - - parms_ViewerGetFP(v, &fp); - fprintf(fp, "=====Dump the content of parms_Map=====\n"); - fprintf(fp, "pid = %d, npro = %d\n", self->pid, self->npro); - fprintf(fp, "lsize = %d, gsize = %d\n", self->lsize, self->gsize); - fprintf(fp, "isserial = %d, isperm = %d, isalloc = %d\n", - self->isserial, self->isperm, self->ispermalloc); - if (!self->isserial) { - fprintf(fp, "size in the table is %d\n", - parms_TableGetSize(self->table)); - fprintf(fp, "global vertices ==> local vertices on proc%d\n", self->pid); - for (i = 0; i < self->lsize; i++) { - val = parms_TableGet(self->table, self->lvars[i]); - fprintf(fp, "%d==>%d\n", self->lvars[i], *val); - } - if (self->isperm) { - fprintf(fp, "The permutation array is:\n"); - for (i = 0; i < self->lsize; i++) { - fprintf(fp, "perm[%d]=%d\n", i, self->perm[i]); - } - } - } - - fprintf(fp, "nint = %d\n", self->nint); - fprintf(fp, "ninf = %d\n", self->ninf); - - fprintf(fp, "ninf_send = %d\n", self->ninf_send); - for (i = 0; i < self->ninf_send; i++) { - fprintf(fp, "send(%d)=%d ", i, self->vsend[i]); - val = parms_TableGet(self->vstable, self->vsend[i]); - fprintf(fp, "vsend[%d]=>%d\n", self->vsend[i], *val); - } - - - fprintf(fp, "=======================================\n"); - parms_ViewerStoreFP(v, fp); - return 0; -} - -/** - * Get the size of variables on the local PE. - * - * Return the size of variables not vertices on the local PE. - * - * @param self A parms_Map object. - * - * @return The size of variables on the local PE. - */ -int parms_MapGetLocalSize(parms_Map self) -{ - return self->lsize; -} - -/** - * Get the global size of variables. - * - * Return the global size of variables rather than vertices. - * - * @param self A parms_Map object. - * - * @return The global size of variables rather than vertices. - */ -int parms_MapGetGlobalSize(parms_Map self) -{ - return self->gsize; -} - -/** - * Get PE's ID. - * - * @param self A parms_Map object. - * - * @return The PE's ID. - */ -int parms_MapGetPid(parms_Map self) -{ - /*-------------------- obtains processor IDs */ - return self->pid; -} - -/** - * Get the number of PEs. - * - * @param self A parms_Map object. - * - * @return The number of PEs. - */ -int parms_MapGetNumProcs(parms_Map self) -{ - /*-------------------- Get number of PEs */ - - return self->npro; -} - -/** - * Create a local parms_Map object. - * - * @param self A parms_Map object created. - * @param gsize The global size of variables. - * @param offset The start index. - * - 1 FORTRAN - * - 0 C - * - * @return 0 on success. - */ -int parms_MapCreateFromLocal(parms_Map *self, int gsize, int offset) -{ - parms_Map newMap; - - PARMS_NEW0((newMap)); - newMap->ref = 1; - newMap->gsize = gsize; - newMap->lsize = gsize; - newMap->start = offset; - newMap->isserial = true; - newMap->isperm = false; - newMap->isvecperm = false; - MPI_Comm_rank(MPI_COMM_WORLD, &newMap->pid); - newMap->npro = 1; - newMap->comm = 0; - newMap->nint = gsize; - newMap->ninf = 0; - newMap->n_ext = 0; - - newMap->ispermalloc = false; - newMap->isdatalloc = false; - - *self = newMap; - -/* Define complex data type for MPI if complex code is compiled */ -#if defined(DBL_CMPLX) - parms_InitComplex(); -#endif - return 0; -} - -/** - * Create a parms_Map object based on the Metis partitioning. - * - * @param self A pointer to the parms_Map object created. - * @param gsize The total number of vertices. - * @param npar An integer array of size gsize. node \f$i\f$ - * resides on PE npar[i]. - * @param comm MPI communicator. - * @param offset The start index. - * - 1 FORTRAN - * - 0 C - * @param dof The number of variables associated with each - * vertex. - * @param VARSTYPE Assuming the variables \f$u_i, v_i\f$ are - * associated with vertex \f$i\f$, two styles of - * numbering variables are as follows: - * - INTERLACED. Variables are numbered in the - * order of \f$u_1, v_1, u_2, v_2, \cdots\f$; - * - NONINTERLACED. Variables are numbered in the - * order of \f$u_1, u_2, u_3,...,v_1, v_2,...\f$. - * - * @return 0 on success. - */ -int parms_MapCreateFromGlobal(parms_Map *self, int gsize, int *npar, - MPI_Comm comm, int offset, int dof, - VARSTYPE vtype) -{ - parms_Map newMap; - int pid, i, j, gindex; - int hint, gv_size, lsize; - MPI_Comm newComm; - - MPI_Comm_dup(comm, &newComm); - PARMS_NEW0((newMap)); - newMap->ref = 1; - MPI_Comm_rank(newComm, &newMap->pid); - MPI_Comm_size(newComm, &newMap->npro); - newMap->comm = newComm; - pid = newMap->pid; - gv_size = newMap->gsize = gsize * dof; - newMap->start = offset; - newMap->dof = dof; - newMap->vtype = vtype; - newMap->isserial = false; - if (newMap->npro == 1) { - newMap->isserial = true; - } - newMap->isperm = false; - newMap->isvecperm = false; - newMap->ispermalloc = false; - newMap->isdatalloc = false; - - if (!newMap->isserial) { - hint = gv_size / newMap->npro; - /* create a hash table */ - parms_TableCreate(&newMap->table, NULL, hint); - PARMS_NEWARRAY(newMap->lvars, hint); - lsize = 0; - if (vtype == INTERLACED) { - for (i = 0; i < gsize; i++) { - if (npar[i] - offset == pid) { - for (j = 0; j < dof; j++) { - gindex = dof*i+j; - /* put the pair (gindex, lsize) into the table */ - parms_TablePut(newMap->table, gindex, lsize); - if (lsize >= hint) { - hint += 50; - PARMS_RESIZE(newMap->lvars, hint); - } - newMap->lvars[lsize++] = gindex; - } - } - } - } - else if (vtype == NONINTERLACED) { - for (i = 0; i < gsize; i++) { - if (npar[i] - offset == pid) { - for (j = 0; j < dof; j++) { - gindex = gsize*j+i; - /* put the pair (gindex, lsize) into the table */ - parms_TablePut(newMap->table, gindex, lsize); - if (lsize >= hint) { - hint += 50; - PARMS_RESIZE(newMap->lvars, hint); - } - newMap->lvars[lsize++] = gindex; - } - } - } - } - - newMap->lsize = lsize; - PARMS_RESIZE(newMap->lvars, lsize); - newMap->ispermalloc = true; - PARMS_NEWARRAY0(newMap->perm, lsize); - PARMS_NEWARRAY0(newMap->iperm, lsize); - for (i = 0; i < lsize; i++) { - newMap->perm[i] = -1; - } - } - else { - lsize = gv_size; - newMap->lsize = gv_size; - } - newMap->nint = lsize; - newMap->ninf = 0; - newMap->n_ext = 0; - - *self = newMap; - -/* Define complex data type for MPI if complex code is compiled */ -#if defined(DBL_CMPLX) - parms_InitComplex(); -#endif - - return 0; -} - -/** - * Create a parms_Map object based on the output of ParMetis. - * - * @param self A parms_Map object created. - * @param vtxdist An integer array of size np+1, where np is the - * number of PEs. This array indicates the range of - * vertices that are local to each processor. PE i - * stores vertices in the range of [vtxdist[i], - * vtxdist[i+1]). - * @param part An array of size equal to the number of - * locally-stored vertices. part[j] indicates the ID - * of the PE to which the vertex with local index j - * and global index vtxdist[pid]+j belongs (pid is ID - * of local PE). - * @param comm MPI communicator. - * @param offset The start index. - * - 1 FORTRAN - * - 0 C - * @param dof The number of variables associated with each - * vertex. - * @param vtype Assuming the variables u_i, v_i are associated - * with vertex i, two styles of numbering variables - * are as follows: - * - INTERLACED. Variables are numbered in the - * order of \f$u_1, v_1, u_2, v_2, \cdots\f$; - * - NONINTERLACED. Variables are numbered in the - * order of \f$u_1, u_2, u_3,...,v_1, v_2,...\f$. - * - * @return 0 on success. - */ -int parms_MapCreateFromDist(parms_Map *self, int *vtxdist, int *part, - MPI_Comm comm, int offset, int dof, - VARSTYPE vtype) -{ - parms_Map newMap; - int npro, pid, i, j, l, gsize; - int gv_size, lsize, nl, ind, gindex; - int *num, *nums, *num_rcv, *disp, *snd_buf, *rcv_buf; - MPI_Comm newComm; - - MPI_Comm_dup(comm, &newComm); - PARMS_NEW0((newMap)); - newMap->ref = 1; - MPI_Comm_rank(newComm, &newMap->pid); - MPI_Comm_size(newComm, &newMap->npro); - newMap->comm = newComm; - npro = newMap->npro; - pid = newMap->pid; - /* get the number of local vertices */ - nl = vtxdist[pid+1] - vtxdist[pid]; - /* calculate the total number of vertices */ - gsize = vtxdist[npro] - vtxdist[0]; - /* total number of variables */ - gv_size = newMap->gsize = gsize * dof; - newMap->start = offset; - newMap->dof = dof; - newMap->vtype = vtype; - newMap->isserial = false; - if (newMap->npro == 1) { - newMap->isserial = true; - } - newMap->isperm = false; - newMap->isvecperm = false; - newMap->ispermalloc = false; - newMap->isdatalloc = false; - - if (!newMap->isserial) { - PARMS_NEWARRAY(snd_buf, nl); - /* create a hash table */ - parms_TableCreate(&newMap->table, NULL, nl); - PARMS_NEWARRAY0(num, npro); - PARMS_NEWARRAY(nums, npro); - /* num[i] stores the number of locally-stored variables being - distributed to PE i */ - for (i = 0; i < nl; i++) { - num[part[i]-offset]++; - } - MPI_Allreduce(num, nums, npro, MPI_INT, MPI_SUM, newComm); - /* nums[i] stores the number of variables on PE i */ - lsize = newMap->lsize = nums[pid]*dof; - PARMS_NEWARRAY(newMap->lvars, lsize); - PARMS_FREE(nums); - - PARMS_NEWARRAY(disp, npro+1); - PARMS_NEWARRAY(num_rcv, npro); - /* num_rcv stores the number of data received from other processors */ - for (i = 0; i < npro; i++) { - MPI_Gather(&num[i], 1, MPI_INT, num_rcv, 1, MPI_INT, i, - newComm); - /* snd_buf stores the data sent to PE i */ - ind = 0; - for (j = 0; j < nl; j++) { - if (part[j]-offset == i) { - snd_buf[ind++] = vtxdist[pid] + j - offset; - } - } - if (pid == i) { - /* disp is an integer array. disp[i] specifies the - displacement relative to rcv_buf at which to place the - incoming data from PE i */ - disp[0] = 0; - for (j = 0; j < npro; j++) { - disp[j+1] = disp[j] + num_rcv[j]; - } - - /* variables in rcv_buf are stored in C-style */ - PARMS_NEWARRAY(rcv_buf, disp[npro]); - MPI_Gatherv(snd_buf, num[i], MPI_INT, newMap->lvars, num_rcv, - disp, MPI_INT, i, newComm); - if (vtype == INTERLACED) { - ind = 0; - for (j = 0; j < disp[npro]; j++) { - for (l = 0; l < dof; l++) { - gindex = dof*rcv_buf[j]+l; - parms_TablePut(newMap->table, gindex, ind); - newMap->lvars[ind++] = gindex; - } - } - } - else if (vtype == NONINTERLACED) { - for (j = 0; j < disp[npro]; j++) { - parms_TablePut(newMap->table, newMap->lvars[j], j); - } - ind = disp[npro]; - for (j = 0; j < disp[npro]; j++) { - for (l = 1; l < dof; l++) { - gindex = gsize*l+newMap->lvars[j]; - parms_TablePut(newMap->table, gindex, ind); - newMap->lvars[ind++] = gindex; - } - } - } - PARMS_FREE(rcv_buf); - } - else { - MPI_Gatherv(snd_buf, num[i], MPI_INT, rcv_buf, num_rcv, - disp, MPI_INT, i, newComm); - } - } - - PARMS_FREE(snd_buf); - PARMS_FREE(num); - PARMS_FREE(num_rcv); - PARMS_FREE(disp); - newMap->ispermalloc = true; - PARMS_NEWARRAY0(newMap->perm, lsize); - PARMS_NEWARRAY0(newMap->iperm, lsize); - for (i = 0; i < lsize; i++) { - newMap->perm[i] = -1; - } - } - else { - lsize = gv_size; - newMap->lsize = gv_size; - } - newMap->nint = lsize; - newMap->ninf = 0; - newMap->n_ext = 0; - - *self = newMap; - -/* Define complex data type for MPI if complex code is compiled */ -#if defined(DBL_CMPLX) - parms_InitComplex(); -#endif - - return 0; -} - -/** - * Create a parms_Map object. - * - * @param self A parms_Map object created. - * @param gsize The total number of vertices. - * @param nodes A list of all vertices stored PE by PE. - * @param p2nodes An integer array of size np+1, np is the number of - * PEs. If k1 = p2nodes[i], k2 = p2nodes[i+1] - * , then PE i contains the vertices in the - * range of [[nodes[k1], [nodes[k2-1]]. - * @param comm MPI communication. - * @param dof The number of variables associated with each - * node. - * @param vtype Assuming the variables \f$u_i, v_i\f$ are - * associated with vertex \f$i\f$, two styles of - * numbering variables are as follows: - * - INTERLACED. Variables are numbered in the - * order of \f$u_1, v_1, u_2, v_2, \cdots\f$; - * - NONINTERLACED. Variables are numbered in the - * order of \f$u_1, u_2, u_3,...,v_1, v_2,...\f$. - * - * @return 0 on success. - */ -int parms_MapCreateFromPtr(parms_Map *self, int gsize, int *nodes, int - *p2nodes, MPI_Comm comm, int dof, VARSTYPE - vtype) -{ - int pid, npro, index, i, j, l; - int start_index, end_index, gindex; - int offset, start_offset; - parms_Map newMap; - MPI_Comm newComm; - - MPI_Comm_dup(comm, &newComm); - - PARMS_NEW0((newMap)); - newMap->ref = 1; - newMap->gsize = gsize * dof; - start_offset = newMap->start = p2nodes[0]; - newMap->dof = dof; - newMap->vtype = vtype; - newMap->isserial = false; - newMap->isperm = false; - newMap->isvecperm = false; - newMap->ispermalloc = false; - newMap->isdatalloc = false; - newMap->nint = 0; - MPI_Comm_rank(newComm, &newMap->pid); - MPI_Comm_size(newComm, &newMap->npro); - newMap->comm = newComm; - npro = newMap->npro; - pid = newMap->pid; - - if (npro == 1) { - newMap->isserial = true; - } - - newMap->lsize = p2nodes[pid+1] - p2nodes[pid]; - newMap->lsize *= dof; - - - if (!newMap->isserial) { - PARMS_NEWARRAY(newMap->lvars, newMap->lsize); - - index = 0; - /* create a table */ - parms_TableCreate(&newMap->table, NULL, newMap->lsize); - - if (vtype == INTERLACED) { - /* calculate the range [start_index, end_index) */ - start_index = p2nodes[pid] - start_offset; - end_index = p2nodes[pid+1] - start_offset; - for (j = start_index; j < end_index; j++) { - offset = j - start_index; - for (l = 0; l < dof; l++) { - /* global index of variable l on vertex nodes[j] */ - gindex = dof*(nodes[j]-start_offset)+l; - parms_TablePut(newMap->table, gindex, index); - /* local variables in global indices */ - newMap->lvars[dof*offset+l] = gindex; - - index++; - } - } - } - else if (vtype == NONINTERLACED) { - /* calculate the range [start_index, end_index) */ - start_index = p2nodes[pid] - start_offset; - end_index = p2nodes[pid+1] - start_offset; - - for (j = start_index; j < end_index; j++) { - offset = j - start_index; - for (l = 0; l < dof; l++) { - /* global index of variable l on vertex nodes[j] */ - gindex = gsize*l+nodes[j]-start_offset; - parms_TablePut(newMap->table, gindex, index); - /* local variables in global indices */ - newMap->lvars[dof*offset+l] = gindex; - - index++; - } - } - } - newMap->ispermalloc = true; - PARMS_NEWARRAY0(newMap->perm, newMap->lsize); - PARMS_NEWARRAY0(newMap->iperm, newMap->lsize); - for (i = 0; i < newMap->lsize; i++) { - newMap->perm[i] = -1; - } - } - newMap->nint = newMap->lsize; - newMap->ninf = 0; - newMap->n_ext = 0; - - *self = newMap; - -/* Define complex data type and ops for MPI if complex code is compiled */ -#if defined(DBL_CMPLX) - parms_InitComplex(); -#endif - - return 0; -} - -/** - * Create a parms_Map object with the default partioning strategy in - * PETSc. - * - * @param self A parms_Map object created. - * @param m The local size of variables. - * @param M The global size of variables. - * @param comm MPI communicatior. - * - * @return 0 on success. - */ -int parms_MapCreateFromPetsc(parms_Map *self, int m, int M, MPI_Comm - comm) -{ - int pid, npro, index, i, j; - int start_index, end_index; - int *nptr; - parms_Map newMap; - MPI_Comm newComm; - - MPI_Comm_dup(comm, &newComm); - /* create a parms_Map object */ - - PARMS_NEW0((newMap)); - newMap->ref = 1; - newMap->gsize = M; - newMap->start = 0; - newMap->dof = 1; - newMap->vtype = NONINTERLACED; - newMap->isserial = false; - newMap->isperm = false; - newMap->isvecperm = false; - newMap->ispermalloc = false; - newMap->isdatalloc = false; - newMap->nint = m; - newMap->ninf = 0; - newMap->n_ext = 0; - MPI_Comm_rank(newComm, &newMap->pid); - MPI_Comm_size(newComm, &newMap->npro); - newMap->comm = newComm; - npro = newMap->npro; - pid = newMap->pid; - - if (npro == 1) { - newMap->isserial = true; - } - - newMap->lsize = m; - - /* if the number of PEs is more than 1 */ - if (!newMap->isserial) { - PARMS_NEWARRAY(newMap->lvars, newMap->lsize); - PARMS_NEWARRAY(nptr, npro); - - /* create a table */ - parms_TableCreate(&newMap->table, NULL, newMap->lsize); - - MPI_Allgather(&m, 1, MPI_INT, nptr, 1, MPI_INT, newComm); - - /* chunks for contiguous data are distributed across PEs. find - the range [start_index, end_index) contained in the local PE. */ - start_index = 0; - for (i = 0; i < pid; i++) { - start_index += nptr[i]; - } - - end_index = start_index + m; - index = 0; - for (j = start_index; j < end_index; j++) { - parms_TablePut(newMap->table, j, index); - newMap->lvars[index++] = j; - } - newMap->ispermalloc = true; - PARMS_NEWARRAY0(newMap->perm, newMap->lsize); - PARMS_NEWARRAY0(newMap->iperm, newMap->lsize); - for (i = 0; i < newMap->lsize; i++) { - newMap->perm[i] = -1; - } - } - - *self = newMap; - -/* Define complex data type for MPI if complex code is compiled */ -#if defined(DBL_CMPLX) - parms_InitComplex(); -#endif - - return 0; -} - -/** - * Get local index for a given global index. - * - * Return a pointer to an integer. If it is NULL, then the variable - * with global index gindex doesn't reside on the local - * PE. Otherwise, it points to an address of a variable - * whose value is local index. - * - * @param self A parms_Map object. - * @param gindex A global index. - * - * @return A pointer to an integer whose value is the corresponding - * local index. - */ -int *parms_MapGlobalToLocal(parms_Map self, int gindex) -{ - int *lindex; - - if(self->isserial) - lindex = &gindex; - else - lindex = parms_TableGet(self->table, gindex); - - return lindex; -} - -/** - * Get global index array for the local PE (see *lvars in parms_map_impl.h) - * -*/ -int parms_MapGetGlobalIndices(parms_Map self, int *im) -{ - int i, offset = self->start; - - for(i=0; ilsize; i++) - im[i] = self->lvars[i]+offset; - - return 0; -} diff --git a/lib/parms/src/parms_mat.c b/lib/parms/src/parms_mat.c deleted file mode 100755 index f8b0f343c..000000000 --- a/lib/parms/src/parms_mat.c +++ /dev/null @@ -1,1413 +0,0 @@ -/*-------------------------------------------------------------------- - parms_MatVec : perform the multiplication of - matrix-vector product. - parms_MatCreate : create a matrix object. - parms_MatExtend : extend submatrix by including equations - correspond to the immediate neighbouring - variables. - parms_MatFree : free the memory for a matrix object. - parms_MatFreeSubMat : free the memory for the submatrix object. - parms_MatGetCommHandler : get the communication handler. - parms_MatGetDiag : get the diagonal part of the local matrix. - parms_MatGetSubMat : get the local matrix. - parms_MatMVPY : perform \f$z = \alpha \times self \times x - + beta \times y\f$. - parms_MatSetCommType : set the communication type. - parms_MatSetValues : insert/add values to the matrix object. - parms_MatSetup : set up a matrix object. - parms_MatVecOffDiag : perform the multiplication of the - off-diagonal matrix and the external vars. - parms_MatView : dump a matrix object. - - A code fragment for using matrix functions: - - parms_Mat mat; - parms_Map map; - parms_PC pc; - - ... - - // create a matrix object. - parms_MatCreate(&mat, map); - - // insert/add values into the matrix - parms_MatSetValues(mat, ...); - - // setup the matrix. - // this function divides local variables into two categories: - // interior variables and interface variables. interior variables - // listed first. when the matrix-vector product is performed, the - // vector is permuted automatically if the vector is created - parms_MatSetup(mat); - - // matrix-vector product - parms_MatVec(mat, vec, y); - - // create a preconditioner object. - parms_PCCreate(&pc, mat); - - $Id: parms_mat.c,v 1.4 2006-12-18 22:23:40 zzli Exp $ - ------------------------------------------------------------------*/ -#include "include/parms_mat_impl.h" - -/* -int parms_MatCreate_vcsr(parms_Mat self); -int parms_MatCreate_dvcsr(parms_Mat self); -int parms_MatFree_dvcsr(parms_Mat *self); -int parms_MatView_vcsr(parms_Mat self, parms_Viewer v); -int parms_MatView_dvcsr(parms_Mat self, parms_Viewer v); -*/ -/** - * Free the parms_Mat object pointed to by self. - * - * @param self A pointer to a parms_Mat object. - * - * @return 0 on success. - */ -int parms_MatFree(parms_Mat *self) -{ - int m, i, nnz; - - (*self)->ref--; - if ((*self)->ref == 0 ) { - if(!(*self)->isserial) - parms_MatFree_dvcsr(self); - parms_MapFree(&(*self)->is); - if ((*self)->isalloc) { - m = (*self)->m; - for (i = 0; i < m; i++) { - nnz = (*self)->aux_data->nnzrow[i]; - if (nnz) { - PARMS_FREE((*self)->aux_data->pj[i]); - PARMS_FREE((*self)->aux_data->pa[i]); - } - } - PARMS_FREE((*self)->aux_data->pa); - PARMS_FREE((*self)->aux_data->pj); - PARMS_FREE((*self)->aux_data->nnzrow); - PARMS_FREE((*self)->aux_data); - PARMS_FREE((*self)->ops); - PARMS_FREE(*self); - } - } - return 0; -} - -/** - * Dump parms_Mat object. - * - * @param self A pointer to a parms_Mat object. - * @param v A parms_Viewer object. - * - * @return 0 on success. - */ -int parms_MatView(parms_Mat self, parms_Viewer v) -{ - if((self)->isserial) - return parms_MatView_vcsr(self, v); - else - return parms_MatView_dvcsr(self, v); -} - -/** - * Dump parms_Mat object in Coordinate format. - * - * @param self A pointer to a parms_Mat object. - * @param v A parms_Viewer object. - * - * @return 0 on success. - */ -int parms_MatViewCOO(parms_Mat self, parms_Viewer v) -{ - if((self)->isserial) - return parms_MatViewCOO_vcsr(self, v); - else - return parms_MatViewCOO_dvcsr(self, v); -} - -/** - * Set the communication type. - * - * Set the communication style across processors. - * communication style: - * -P2P point-to-point (data copied to/from auxilliary buffers). - * -DERIVED derived datatype. - * - * @param self A matrix object. - * @param ctype Communication style: - * - P2P point-to-point (data copied to/from - * auxilliary buffers). - * - DERIVED derived datatype. - * - * @return 0 on success. - */ -int parms_MatSetCommType(parms_Mat self, COMMTYPE ctype) -{ - return self->ops->setcommtype(self, ctype); -} - -/** - * Create a parms_Mat object. - * - * Create a parms_Mat object based on data distribution layout map. - * - * @param self A pointer to the parms_Mat object created. - * @param map A parms_Map object, which describes the data - * distribution among processors. - * - * @return 0 on success. - */ -int parms_MatCreate(parms_Mat *self, parms_Map map) -{ - parms_Mat new_mat; - - PARMS_NEW0((new_mat)); - new_mat->ref = 1; - PARMS_NEW0((new_mat)->ops); - new_mat->is = map; - map->ref++; - new_mat->type = MAT_NULL; - new_mat->ilutype =PCILU0; /*-- default local precon is ilu0. See parms_pc.c ---*/ - new_mat->issetup = false; - new_mat->isperm = map->isperm; - new_mat->isalloc = false; - new_mat->isreset = false; - new_mat->isassembled = false; - - new_mat->m = parms_MapGetLocalSize(map); - new_mat->n = new_mat->m; - new_mat->M = parms_MapGetGlobalSize(map); - new_mat->N = new_mat->M; - new_mat->isserial = map->isserial; - *self = new_mat; - - return 0; -} - -/** - * Perform \f$y = self \times x\f$. - * - * @param self A parms_Mat object. - * @param x A vector. - * @param y Another vector object. - * - * @return 0 on success. - */ -int parms_MatVec(parms_Mat self, FLOAT *x, FLOAT *y) -{ - return self->ops->apply(self, x, y); -} - -/** - * Set up parms_Mat object self. - * - * This is the most important function for the parms_Mat object. This - * function combines the function bdry and setup in the old version - * of pARMS. The function sets up the data structure needed by the - * distributed matrix-vector multiplication, divides the variables on - * the local processors into two categories: interior and interface - * variables. - * - * @param self A parms_Mat object. - * - * @return 0 on success. - */ -int parms_MatSetup(parms_Mat self) -{ - /* free and create new table for offdiagonal column count */ - if(self->odtable) - parms_TableFree(&self->odtable); - - if ((!self->issetup) && (self->type == MAT_NULL)) { - self->type = MAT_VCSR; - if(self->isserial){ - parms_MatCreate_vcsr(self); - } - else{ - if(!self->isreset) - parms_MatCreate_dvcsr(self); - } - } - - return self->ops->setup(self); -} - -/** - * Insert/add values to the parms_Mat object self. - * - * @param self A parms_Mat object. - * @param m The number of rows inserted. - * @param im An array of row global indices. - * @param ia An array of pointer to the beginning of each row in - * array ja. - * @param ja An array of column global indices. - * @param values An array of values. - * @param mode Insert value mode: - * - INSERT insert values to parms_Mat self. - * - ADD add values to parms_Mat self. - * - * NOTE: New entries are always inserted first, so mode does not - * matter if this is a new entry. Subsequent calls will either - * replace (mode = INSERT) or add (mode = ADD) to the existing - * entries in a particular position - * - * @return 0 on success. - */ -int parms_MatSetValues(parms_Mat self, int m, int *im, int *ia, - int *ja, FLOAT *values, INSERTMODE mode) -{ - parms_Map is; - parms_vcsr aux_data; - BOOL isserial, isalloc; - int i, j, k, offset, start, end, rindex, cindex; - int space, numinf, index, lrindex, lcindex, *rja; - int *perm, *rp, *cp, *odp,inc, lsize, size; - FLOAT *ra; - BOOL found; - - isalloc = self->isalloc; - is = self->is; - offset = is->start; - lsize = parms_MapGetLocalSize(is); - isserial = is->isserial; - - if (isalloc) { - aux_data = self->aux_data; - } - else { - self->isalloc = true; - PARMS_NEW(aux_data); - aux_data->n = lsize; - PARMS_NEWARRAY(aux_data->space, aux_data->n); - PARMS_NEWARRAY0(aux_data->nnzrow, aux_data->n); - PARMS_NEWARRAY(aux_data->pj, aux_data->n); - PARMS_NEWARRAY(aux_data->pa, aux_data->n); - for (i = 0; i < aux_data->n; i++) { - aux_data->space[i] = 8; - PARMS_NEWARRAY(aux_data->pj[i], aux_data->space[i]); - PARMS_NEWARRAY(aux_data->pa[i], aux_data->space[i]); - } - self->aux_data = aux_data; - /* create table for tracking off-diagonal column count */ - parms_TableCreate(&self->odtable, NULL, lsize); - } - - if (isserial) { - for (i = 0; i < m; i++) { - rindex = im[i] - offset; - ra = aux_data->pa[rindex]; - rja = aux_data->pj[rindex]; - start = ia[i] - offset; - end = ia[i+1] - offset; - for (j = start; j < end; j++) { - cindex = ja[j] - offset; - space = aux_data->space[rindex]; - found = false; - for (k = 0; k < aux_data->nnzrow[rindex]; k++) { - if (rja[k] == cindex) { - found = true; - index = k; - break; - } - } - if (found) { - if (mode == INSERT) { - ra[index] = values[j]; - } - else if (mode == ADD) { - ra[index] += values[j]; - } - } - else { /* insert a new entry */ - if (space == aux_data->nnzrow[rindex]) { - /* reallocate memory for holding new entry */ - space += 8; - PARMS_RESIZE(aux_data->pa[rindex], space); - PARMS_RESIZE(aux_data->pj[rindex], space); - aux_data->space[rindex] = space; - ra = aux_data->pa[rindex]; - rja = aux_data->pj[rindex]; - } - rja[aux_data->nnzrow[rindex]] = cindex; - ra[aux_data->nnzrow[rindex]++] = values[j]; - } - } - } - } - else { - perm = is->perm; - numinf = is->ninf; -/* Check if matrix is to be re-used with same non-zero pattern */ - if(self->isreset && (self->resetpattern == SAME_NONZERO_STRUCTURE)) - { - for (i = 0; i < m; i++) { - rindex = im[i] - offset; - rp = parms_MapGlobalToLocal(is, rindex); - /* this row resides the local processor */ - if (rp != NULL && *rp < lsize) { - /* calculate the local row index */ - lrindex = perm[*rp]; - ra = aux_data->pa[lrindex]; - rja = aux_data->pj[lrindex]; - - start = ia[i]-offset; - end = ia[i+1]-offset; - for (j = start; j < end; j++) { - cindex = ja[j] - offset; - /* the local processor contains this column index */ - cp = parms_MapGlobalToLocal(is, cindex); - if (cp != NULL && *cp < lsize) { - /* local column index */ - lcindex = perm[*cp]; - - found = false; - for (k = 0; k < aux_data->nnzrow[lrindex]; k++) { - if (rja[k] == lcindex) { - found = true; - index = k; - break; - } - } - if (found) { - if (mode == INSERT) { - ra[index] = values[j]; - } - else if (mode == ADD) { - ra[index] += values[j]; - } - } - } - else { - if(cp != NULL){ - lcindex = *cp; - - found = false; - for (k = 0; k < aux_data->nnzrow[lrindex]; k++) { - if (rja[k] == lcindex) { - found = true; - index = k; - break; - } - } - if (found) { - if (mode == INSERT) { - ra[index] = values[j]; - } - else if (mode == ADD) { - ra[index] += values[j]; - } - } - } - } - } - } - } - } - else{ - for (i = 0; i < m; i++) { - rindex = im[i] - offset; - rp = parms_MapGlobalToLocal(is, rindex); - /* this row resides the local processor */ - if (rp != NULL && *rp < lsize) { - /* calculate the local row index */ - lrindex = *rp; - ra = aux_data->pa[lrindex]; - rja = aux_data->pj[lrindex]; - - start = ia[i]-offset; - end = ia[i+1]-offset; - for (j = start; j < end; j++) { - cindex = ja[j] - offset; - /* the local processor contains this column index */ - cp = parms_MapGlobalToLocal(is, cindex); - if (cp != NULL && *cp < lsize) { - /* local column index */ - lcindex = *cp; - } - else { - if (perm[lrindex] == -1) { /* not marked yet */ - perm[lrindex] = lsize-1-numinf; - numinf++; - } - lcindex = -cindex-1; - } - space = aux_data->space[lrindex]; - found = false; - for (k = 0; k < aux_data->nnzrow[lrindex]; k++) { - if (rja[k] == lcindex) { - found = true; - index = k; - break; - } - } - if (found) { - if (mode == INSERT) { - ra[index] = values[j]; - } - else if (mode == ADD) { - ra[index] += values[j]; - } - } - else { /* insert the new entry */ - if (space == aux_data->nnzrow[lrindex]) { - /* reallocate memory for holding new entry */ - space += 8; - PARMS_RESIZE(aux_data->pa[lrindex], space); - PARMS_RESIZE(aux_data->pj[lrindex], space); - aux_data->space[lrindex] = space; - ra = aux_data->pa[lrindex]; - rja = aux_data->pj[lrindex]; - } - if (cp == NULL) { - size = parms_TableGetSize(is->table); - parms_TablePut(is->table, cindex, size); - /* Initialize column count */ - inc = 0; - parms_TablePut(self->odtable,cindex,inc); - } - else if((cp !=NULL) && (lcindex < 0)){ - /* update off-diagonal column count */ - odp = parms_TableGet(self->odtable, cindex); - inc = *odp + 1; - parms_TablePut(self->odtable,cindex,inc); - } - - rja[aux_data->nnzrow[lrindex]] = lcindex; - ra[aux_data->nnzrow[lrindex]++] = values[j]; - } - } - } - } - is->ninf = numinf; - } - } - - return 0; -} - - - -/** - * Insert/add values to the parms_Mat object self. This assumes matrix - * values are being added element-by-element - * - * @param self A parms_Mat object. - * @param m The number of rows inserted. - * @param im An array of row global indices. - * @param ia An array of pointer to the beginning of each row in - * array ja. - * @param ja An array of column global indices. - * @param values An array of values. - * @param mode Insert value mode: - * - INSERT insert values to parms_Mat self. - * - ADD add values to parms_Mat self. - * - * NOTE: New entries are always inserted first, so mode does not - * matter if this is a new entry. Subsequent calls will either - * replace (mode = INSERT) or add (mode = ADD) to the existing - * entries in a particular position - * - * @return 0 on success. - */ -int parms_MatSetElementMatrix(parms_Mat self, int m, int *im, int *ia, - int *ja, FLOAT *values, INSERTMODE mode) -{ - parms_Map is; - parms_vcsr aux_data, ext_data; - BOOL isserial, isalloc, isassembled; - int i, j, k, offset, start, end, rindex, cindex; - int space, numinf, index, lrindex, lcindex, *rja, *pj; - int *perm, *rp, *cp,*odp,inc, *ext_im, lsize, size, ext_nnz; - FLOAT *ra, *pa; - BOOL found; - - isalloc = self->isalloc; - is = self->is; - offset = is->start; - lsize = parms_MapGetLocalSize(is); - isserial = is->isserial; - isassembled = self->isassembled; - - if(isserial) - parms_MatSetValues(self, m, im, ia, ja, values, mode); - else{ - if (isalloc) { - aux_data = self->aux_data; - if(isassembled) - { - /* reallocate some data for the external data (contributions to - * other processors) from the current element matrix - */ - PARMS_NEW(ext_data); - is->n_ext = is->n_ext > 0 ? is->n_ext : lsize; - ext_data->n = is->n_ext; - PARMS_NEWARRAY(ext_data->space, ext_data->n); - PARMS_NEWARRAY0(ext_data->nnzrow, ext_data->n); - PARMS_NEWARRAY(ext_data->pj, ext_data->n); - PARMS_NEWARRAY(ext_data->pa, ext_data->n); - PARMS_NEWARRAY(ext_im, ext_data->n); - for (i = 0; i < ext_data->n; i++) { - ext_data->space[i] = 8; - PARMS_NEWARRAY(ext_data->pj[i], ext_data->space[i]); - PARMS_NEWARRAY(ext_data->pa[i], ext_data->space[i]); - } - self->ext_data = ext_data; - is->ext_im = ext_im; - /* reinitialize number of external contribution to zero */ - is->n_ext = 0; - /* reset */ - self->isassembled = false; - } - else{ - ext_data = self->ext_data; - ext_im = is->ext_im; - } - } - else { - self->isalloc = true; - PARMS_NEW(aux_data); - aux_data->n = lsize; - PARMS_NEWARRAY(aux_data->space, aux_data->n); - PARMS_NEWARRAY0(aux_data->nnzrow, aux_data->n); - PARMS_NEWARRAY(aux_data->pj, aux_data->n); - PARMS_NEWARRAY(aux_data->pa, aux_data->n); - for (i = 0; i < aux_data->n; i++) { - aux_data->space[i] = 8; - PARMS_NEWARRAY(aux_data->pj[i], aux_data->space[i]); - PARMS_NEWARRAY(aux_data->pa[i], aux_data->space[i]); - } - self->aux_data = aux_data; - /* create table to track off-diag column count */ - parms_TableCreate(&self->odtable,NULL,lsize); -/* Now allocate some data for the external data (contributions to - * other processors) from the current element matrix -*/ - PARMS_NEW(ext_data); - ext_data->n = lsize; - PARMS_NEWARRAY(ext_data->space, ext_data->n); - PARMS_NEWARRAY0(ext_data->nnzrow, ext_data->n); - PARMS_NEWARRAY(ext_data->pj, ext_data->n); - PARMS_NEWARRAY(ext_data->pa, ext_data->n); - PARMS_NEWARRAY(ext_im, ext_data->n); - for (i = 0; i < ext_data->n; i++) { - ext_data->space[i] = 8; - PARMS_NEWARRAY(ext_data->pj[i], ext_data->space[i]); - PARMS_NEWARRAY(ext_data->pa[i], ext_data->space[i]); - } - self->ext_data = ext_data; - is->ext_im = ext_im; - - } - - perm = is->perm; - numinf = is->ninf; -/* Check if matrix is to be re-used with same non-zero pattern */ - if(self->isreset && (self->resetpattern == SAME_NONZERO_STRUCTURE)) - { - for (i = 0; i < m; i++) { - rindex = im[i] - offset; - rp = parms_MapGlobalToLocal(is, rindex); - /* this row resides the local processor */ - if (rp != NULL && *rp < lsize) { - /* calculate the local row index */ - lrindex = perm[*rp]; - ra = aux_data->pa[lrindex]; - rja = aux_data->pj[lrindex]; - - start = ia[i]-offset; - end = ia[i+1]-offset; - for (j = start; j < end; j++) { - cindex = ja[j] - offset; - /* the local processor contains this column index */ - cp = parms_MapGlobalToLocal(is, cindex); - if (cp != NULL && *cp < lsize) { - /* local column index */ - lcindex = perm[*cp]; - - for (k = 0; k < aux_data->nnzrow[lrindex]; k++) { - if (rja[k] == lcindex) { - ra[k] = values[j]; - break; - } - } - } - else { - if(cp != NULL){ - lcindex = *cp; - - for (k = 0; k < aux_data->nnzrow[lrindex]; k++) { - if (rja[k] == lcindex) { - ra[k] = values[j]; - break; - } - } - - } - } - } - } - else{ - -/* This is an external contribution */ -/* first check if there is enough memory allocated */ - space = ext_data->n; - if(is->n_ext == space){ - /* reallocate memory for holding new entry */ - space += 8; - PARMS_RESIZE(ext_data->space, space); - PARMS_RESIZE(ext_data->nnzrow, space); - PARMS_RESIZE(ext_data->pj, space); - PARMS_RESIZE(ext_data->pa, space); - PARMS_RESIZE(is->ext_im, space); - for (k = ext_data->n; k < space; k++) { - ext_data->space[k] = 8; - ext_data->nnzrow[k] = 0; - PARMS_NEWARRAY(ext_data->pj[k], ext_data->space[k]); - PARMS_NEWARRAY(ext_data->pa[k], ext_data->space[k]); - } - ext_data->n = space; - ext_im = is->ext_im; - } - - rindex = 0; - for(k = 0; kn_ext; k++) /* check if row has been added already */ - { - if(ext_im[k] == im[i]) - break; - } - if(k < is->n_ext) // already added row - rindex = k; - else // new row contribution - { - rindex = is->n_ext; - ext_im[rindex] = im[i]; - } -/* now get row info */ - start = ia[i]-offset; - end = ia[i+1]-offset; - pj = ext_data->pj[rindex]; - pa = ext_data->pa[rindex]; - ext_nnz = ext_data->nnzrow[rindex]; - cindex = 0; - - for (j = start; j < end; j++) { - for(k = 0; k < ext_nnz; k++) /* check if column is available */ - { - if(pj[k] == ja[j]) - break; - } - if(k < ext_nnz){ - cindex = k; - pa[cindex] += values[j]; - } - else{ -/* check if space is enough */ - space = ext_data->space[rindex]; - if (space == ext_data->nnzrow[rindex]) { - /* reallocate memory for holding new entry */ - space += 8; - PARMS_RESIZE(ext_data->pa[rindex], space); - PARMS_RESIZE(ext_data->pj[rindex], space); - ext_data->space[rindex] = space; - pj = ext_data->pj[rindex]; - pa = ext_data->pa[rindex]; - } -/* enter new column */ - pj[ext_data->nnzrow[rindex]] = ja[j]; - pa[ext_data->nnzrow[rindex]++] = values[j]; - } - } -/* now update n_ext */ - if(rindex == is->n_ext) - is->n_ext++; - } - } - } - else{ - - for (i = 0; i < m; i++) { - rindex = im[i] - offset; - rp = parms_MapGlobalToLocal(is, rindex); - /* this row resides the local processor */ - if (rp != NULL && *rp < lsize) { - /* calculate the local row index */ - lrindex = *rp; - ra = aux_data->pa[lrindex]; - rja = aux_data->pj[lrindex]; - - start = ia[i]-offset; - end = ia[i+1]-offset; - for (j = start; j < end; j++) { - cindex = ja[j] - offset; - /* the local processor contains this column index */ - cp = parms_MapGlobalToLocal(is, cindex); - if (cp != NULL && *cp < lsize) { - /* local column index */ - lcindex = *cp; - } - else { - if (perm[lrindex] == -1) { /* nor marked yet */ - perm[lrindex] = lsize-1-numinf; - numinf++; - } - lcindex = -cindex-1; - } - space = aux_data->space[lrindex]; - found = false; - for (k = 0; k < aux_data->nnzrow[lrindex]; k++) { - if (rja[k] == lcindex) { - found = true; - ra[k] = values[j]; - break; - } - } - if (found == false) { /* insert the new entry */ - if (space == aux_data->nnzrow[lrindex]) { - /* reallocate memory for holding new entry */ - space += 8; - PARMS_RESIZE(aux_data->pa[lrindex], space); - PARMS_RESIZE(aux_data->pj[lrindex], space); - aux_data->space[lrindex] = space; - ra = aux_data->pa[lrindex]; - rja = aux_data->pj[lrindex]; - } - if (cp == NULL) { - size = parms_TableGetSize(is->table); - parms_TablePut(is->table, cindex, size); - /* Initialize column count */ - inc = 0; - parms_TablePut(self->odtable,cindex,inc); - } - else if((cp !=NULL) && (lcindex < 0)){ - /* update off-diagonal column count */ - odp = parms_TableGet(self->odtable, cindex); - inc = *odp + 1; - parms_TablePut(self->odtable,cindex,inc); - } - - rja[aux_data->nnzrow[lrindex]] = lcindex; - ra[aux_data->nnzrow[lrindex]++] = values[j]; - } - } - } - else{ - -/* This is an external contribution */ -/* first check if there is enough memory allocated */ - space = ext_data->n; - if(is->n_ext == space){ - /* reallocate memory for holding new entry */ - space += 8; - PARMS_RESIZE(ext_data->space, space); - PARMS_RESIZE(ext_data->nnzrow, space); - PARMS_RESIZE(ext_data->pj, space); - PARMS_RESIZE(ext_data->pa, space); - PARMS_RESIZE(is->ext_im, space); - for (k = ext_data->n; k < space; k++) { - ext_data->space[k] = 8; - ext_data->nnzrow[k] = 0; - PARMS_NEWARRAY(ext_data->pj[k], ext_data->space[k]); - PARMS_NEWARRAY(ext_data->pa[k], ext_data->space[k]); - } - ext_data->n = space; - ext_im = is->ext_im; - } - - rindex = 0; - for(k = 0; kn_ext; k++) /* check if row has been added already */ - { - if(ext_im[k] == im[i]) - break; - } - if(k < is->n_ext) // already added row - rindex = k; - else // new row contribution - { - rindex = is->n_ext; - ext_im[rindex] = im[i]; - } -/* now get row info */ - start = ia[i]-offset; - end = ia[i+1]-offset; - pj = ext_data->pj[rindex]; - pa = ext_data->pa[rindex]; - ext_nnz = ext_data->nnzrow[rindex]; - cindex = 0; - - for (j = start; j < end; j++) { - for(k = 0; k < ext_nnz; k++) /* check if column is available */ - { - if(pj[k] == ja[j]) - break; - } - if(k < ext_nnz){ - cindex = k; - pa[cindex] += values[j]; - } - else{ -/* check if space is enough */ - space = ext_data->space[rindex]; - if (space == ext_data->nnzrow[rindex]) { - /* reallocate memory for holding new entry */ - space += 8; - PARMS_RESIZE(ext_data->pa[rindex], space); - PARMS_RESIZE(ext_data->pj[rindex], space); - ext_data->space[rindex] = space; - pj = ext_data->pj[rindex]; - pa = ext_data->pa[rindex]; - } -/* enter new column */ - pj[ext_data->nnzrow[rindex]] = ja[j]; - pa[ext_data->nnzrow[rindex]++] = values[j]; - } - } -/* now update n_ext */ - if(rindex == is->n_ext) - is->n_ext++; - } - } - is->ninf = numinf; - } - } - return 0; -} - -/** - * Assembles the finite element matrix by updating off-processor - * contributions. - * - * @param self A parms_Mat object. - * @return 0 on success. - */ -int parms_MatAssembleElementMatrix(parms_Mat self) -{ - int i, j,mypid, npro, lsize, k, k1, k2, lnnz, gextnnz, n_ext; - int gnodv,index, nrow; - int *clp, *info,*disp; - MPI_Comm comm; - - parms_Map is; - parms_vcsr ext_data; - int *nnzstart=NULL, *ptrvrecv=NULL,*ia=NULL, *ja=NULL; - int *gextvars=NULL, *gextnnzrow=NULL; - int *gextpja=NULL, *ext_nnzrow, *nnzptr, *ext_im; - FLOAT *aa=NULL, *gextpa=NULL; - - /* extract information arrays */ - is = self->is; - npro = is->npro; - mypid = is->pid; - comm = is->comm; - lsize = parms_MapGetLocalSize(is); - -/* Check to see if there is any external data. If so, then update - * The local matrix before setting up the matrix. -*/ - ext_data = self->ext_data; - n_ext = is->n_ext; - ext_im = is->ext_im; - - PARMS_NEWARRAY(info, npro); - PARMS_NEWARRAY(disp, npro+1); - - MPI_Allgather(&n_ext, 1, MPI_INT, info, 1, MPI_INT, comm); - disp[0] = 0; - for (i = 0; i < npro; i++) { - disp[i+1] = disp[i] + info[i]; - } - /* total number of external variables */ - gnodv = disp[npro]; - - if(gnodv) /* complete update of matrix values before matrix setup */ - { - ext_nnzrow = ext_data->nnzrow; - /* First gather data for all processors */ - PARMS_NEWARRAY(gextvars, gnodv); - PARMS_NEWARRAY0(gextnnzrow, gnodv); - MPI_Allgatherv(ext_im, n_ext, MPI_INT, gextvars, - info, disp, MPI_INT, comm); - MPI_Allgatherv(ext_nnzrow, n_ext, MPI_INT, gextnnzrow, - info, disp, MPI_INT, comm); - - /* reshape pja and pa structs into 1D arrays */ - lnnz = 0; - for(i = 0; ipj[i][j]; - aa[k++] = ext_data->pa[i][j]; - } - } - /* get global nnz count for external data */ - PARMS_NEWARRAY(nnzptr, npro+1); - MPI_Allgather(&lnnz, 1, MPI_INT, info, 1, MPI_INT, comm); - nnzptr[0] = 0; - for (i = 0; i < npro; i++) { - nnzptr[i+1] = nnzptr[i] + info[i]; - } - gextnnz = nnzptr[npro]; - PARMS_NEWARRAY(gextpja, gextnnz); - PARMS_NEWARRAY(gextpa, gextnnz); - - MPI_Allgatherv(ja, lnnz, MPI_INT, gextpja, - info, nnzptr, MPI_INT, comm); -#if defined(DBL_CMPLX) - MPI_Allgatherv(aa, lnnz, MPI_CMPLX, gextpa, - info, nnzptr, MPI_CMPLX, comm); -#else - MPI_Allgatherv(aa, lnnz, MPI_DOUBLE, gextpa, - info, nnzptr, MPI_DOUBLE, comm); -#endif - /* Now loop over external variables to get matrix contributions */ - PARMS_NEWARRAY0(nnzstart, gnodv); - PARMS_NEWARRAY(ptrvrecv, gnodv); - k = 0; - k1 = 0; - k2 = 0; - for(i=0; istart; - clp = parms_MapGlobalToLocal(is, index); - if (clp != NULL && *clp < lsize) { - ptrvrecv[k] = j; - nnzstart[k++] = k2; - k1 += gextnnzrow[j]; - } - k2 += gextnnzrow[j]; - } - } - } - /* Allocate memory */ - nrow = k; - PARMS_RESIZE(nnzstart, nrow); - PARMS_NEWARRAY(ia, nrow+1); - PARMS_FREE(ja); - PARMS_FREE(aa); - - ia[0] = is->start; - /* Now loop again to actually set up csr matrix */ - for(i=0; innzrow); - PARMS_FREE(ext_data->space); - for(i=0;in; i++) - { - PARMS_FREE(ext_data->pa[i]); - PARMS_FREE(ext_data->pj[i]); - } - PARMS_FREE(ext_data->pa); - PARMS_FREE(ext_data->pj); - PARMS_FREE(ext_data); - PARMS_FREE(ext_im); - } -/* Done with matrix update */ - self->isassembled = true; - - return 0; -} - -/** - * Insert values to the parms_Mat object self. This assumes matrix - * values for this row have already been set, and are to be replaced - * by the new ones provided as input. - * - * @param self A parms_Mat object. - * @param m The number of rows inserted. - * @param im An array of row global indices. - * @param ia An array of pointer to the beginning of each row in - * array ja. - * @param ja An array of column global indices. - * @param values An array of values. - * @return 0 on success. - */ -int parms_MatResetRowValues(parms_Mat self, int m, int *im, int *ia, - int *ja, FLOAT *values) -{ - int i, j, k, rindex,lrindex, lsize, offset, n_ext, gnodv; - int npro,myid,gm, cindex; - int *rp,*ext_im,*disp=NULL,*info=NULL,*gvars=NULL,*rowptr=NULL; - int *cp, *odp, *pj, nnz, dec; - parms_Map is; - parms_vcsr aux_data, ext_data; - BOOL isserial, isalloc, isassembled; - MPI_Comm comm; - - isalloc = self->isalloc; - is = self->is; - offset = is->start; - lsize = parms_MapGetLocalSize(is); - isserial = is->isserial; - n_ext = is->n_ext; - ext_im = is->ext_im; - npro = is->npro; - myid = is->pid; - comm = is->comm; - isassembled = self->isassembled; - -/* Trivial case */ - if((isserial)||(!isalloc)){ - return parms_MatSetValues(self, m, im, ia, ja, values, INSERT); - } - -/* Take care of external contributions if any. Check to see - * if the external variables have been assembled prior to this - * function call -*/ - PARMS_NEWARRAY(disp, npro+1); - PARMS_NEWARRAY(info, npro); - - MPI_Allgather(&n_ext, 1, MPI_INT, info, 1, MPI_INT, comm); - disp[0] = 0; - for (i = 0; i < npro; i++) { - disp[i+1] = disp[i] + info[i]; - } - /* total number of external variables */ - gnodv = disp[npro]; - /* Check ... */ - if((gnodv) && (!isassembled)) - { - ext_data = self->ext_data; - /* First gather data for all processors */ - PARMS_NEWARRAY(rowptr, npro+1); - MPI_Allgather(&m, 1, MPI_INT, info, 1, MPI_INT, comm); - rowptr[0] = 0; - for (i = 0; i < npro; i++) { - rowptr[i+1] = rowptr[i] + info[i]; - } - gm = rowptr[npro]; - - PARMS_NEWARRAY(gvars, gm); - MPI_Allgatherv(im, m, MPI_INT, gvars, - info, rowptr, MPI_INT, comm); - - /* now loop over external variables to remove their contribution */ - for(i=0; innzrow[k] = 0; - break; - } - } - } - } - } - PARMS_FREE(gvars); - PARMS_FREE(rowptr); - } - PARMS_FREE(disp); - PARMS_FREE(info); -/* Done with external contributions. Now reset the row */ - -/* Check if matrix is to be re-used with same non-zero pattern */ - if(self->isreset && (self->resetpattern == SAME_NONZERO_STRUCTURE)) - { - parms_MatSetValues(self, m, im, ia, ja, values, INSERT); - } - else - { - aux_data = self->aux_data; - /* begin main loop over rows to be reset */ - for(i = 0; iperm[lrindex] != -1){ - is->perm[lrindex] = -1; - is->ninf = is->ninf - 1; - - /* loop over off-diagonal nodes to see if any needs to be removed from table */ - nnz = aux_data->nnzrow[lrindex]; - pj = aux_data->pj[lrindex]; - for(j = 0; j= 0) continue; - - cindex = -pj[j] - 1; - cp = parms_TableGet(is->table, cindex); - - if(cp != NULL && *cp >= lsize) - { - odp = parms_TableGet(self->odtable, cindex); - if(*odp == 0) - { - parms_TableRemoveFromLast(is->table, cindex); - } - else - { - /* Decrement column count and continue */ - dec = *odp - 1; - parms_TablePut(self->odtable,cindex,dec); - } - } - } - } - /* Reset nnz for current row to overwrite data */ - aux_data->nnzrow[lrindex] = 0; - /* Now call matsetvalues to insert values into row */ -// parms_MatSetValues(self, 1, &im[i], &ia[i], ja, values, INSERT); - } - } - /* Now call matsetvalues to insert values into rows */ - parms_MatSetValues(self, m, im, ia, ja, values, INSERT); - } - - return 0; -} - -/** - * Reset the matrix to be re-used. - * @param self A parms_Mat object. - * @param nonzerostructure The nonzero structure: - * SAME_NONZERO_STRUCTURE - * DIFFERENT_NONZERO_STRUCTURE - * - * @return 0 on success. - */ -int parms_MatReset(parms_Mat self, NNZSTRUCT nonzerostructure) -{ - parms_Map is; - parms_vcsr aux_data; - int i, j; - int index; - int lsize, nnz; - FLOAT *ra; - - is = self->is; - lsize = parms_MapGetLocalSize(is); - aux_data = self->aux_data; - -/*********************/ - if(self->issetup){ - - if(nonzerostructure == SAME_NONZERO_STRUCTURE) - { - self->resetpattern = SAME_NONZERO_STRUCTURE; - /* zero out previous entries */ - for(i=0; ipa[i]; - nnz = aux_data->nnzrow[i]; - for(j=0; jresetpattern = DIFFERENT_NONZERO_STRUCTURE; - /* Matrix structure is being re-used */ - PARMS_NEWARRAY(aux_data->space, aux_data->n); - /* Initially same non-zero structure */ - for (i = 0; i < aux_data->n; i++) { - aux_data->space[i] = aux_data->nnzrow[i]; - /* Now zero out entries by simply setting nnzrow[i] = 0 */ - aux_data->nnzrow[i] = 0; - } - /* reset permutation array - perm */ - for (i = 0; i < lsize; i++) { - is->perm[i] = -1; - } - /* reset number of interface variables to zero */ - is->ninf = 0; - /* reset number of external variables to zero (if any) */ - is->n_ext = 0; - - /* create new table for offdiagonal column count */ - parms_TableCreate(&self->odtable, NULL, lsize); - /* free hash table associated with previous data, and create new one */ - parms_TableFree(&is->table); - /* create new hash table for new data */ - parms_TableCreate(&is->table, NULL, lsize); - for(index = 0; index < lsize; index++) - { - parms_TablePut(is->table, is->lvars[index], index); - } - } - self->issetup = false; - self->isreset = true; - } - - return 0; -} - - -/** - * Get the diagonal part of the local matrix. - * - * @param self A parms_Mat object. - * @param mat The diagonal part of the local matrix. - * - * @return 0 on success. - */ -int parms_MatGetDiag(parms_Mat self, void **mat) -{ - return self->ops->getdiag(self, mat); -} -/** - * Get the diagonal part of the local matrix. - * - * @param self A parms_Mat object. - * @param mat The diagonal part of the local matrix. - * - * @return 0 on success. - */ -int parms_MatGetOffDiag(parms_Mat self, void **mat) -{ - return self->ops->getoffdiag(self, mat); -} - -/** - * Perform \f$z = alpha*self*x + beta*y\f$. - * - * @param self A matrix object. - * @param alpha A scalar. - * @param x A vector object. - * @param beta A scalar. - * @param y A vector object. - * @param z A vector stores the result. - * - * @return 0 on success. - */ -int parms_MatMVPY(parms_Mat self, FLOAT alpha, FLOAT *x, FLOAT beta, - FLOAT *y, FLOAT *z) -{ - return self->ops->mvpy(self, alpha, x, beta, y, z); -} - -/** - * Perform the multiplication of the off-diagonal matrix and the - * external vars. - * - * The local matrix can be written as follows: - * - * \f[ - * \left( - * \begin{array}{ccc} - * B & E & 0\\ - * F & C & M_{ext} - * \end{array} - * \right) - * \f], - * where \f$\left(\begin{array}{cc} - * B & E \\ - * F & C - * \end{array} - * \right)\f$ corresponds to the variables on the local PE. This - * function performs - * \f[ - * y[pos..n] = M_{ext} \times x_{ext} - * \f] - * - * @param self A matrix object. - * @param x A vector object. - * @param y A vector object. - * @param pos The offset of x from the beginning of the local - * vector. - * - * @return 0 on success. - */ -int parms_MatVecOffDiag(parms_Mat self, FLOAT *x, FLOAT *y, int pos) -{ - return self->ops->mvoffd(self, x, y, pos); -} - -/** - * Free the memory for the submatrix. - * - * @param self A parms_Mat object. - * @param mat The submatrix to be freed. - * - * @return 0 on success. - */ -int parms_MatFreeSubMat(parms_Mat self, void *mat) -{ - return self->ops->matfree(self, mat); -} - -/** - * Extend submatrix by including equations correspond to the - * immediate neighbouring variables. - * - * @param self A matrix object. - * @param handler A communication handler. - * @param start The beginning location of mat in the local matrix. - * @param mat The submatrix to be extended. - * @param n The size of extended matrix returned. - * @param ext_mat The extended matrix created. - * - * @return 0 on success. - */ -int parms_MatExtend(parms_Mat self, parms_Comm handler, int start, - void *mat, int *n, void **ext_mat) -{ - - return self->ops->extend(self, handler, start, mat, n, ext_mat); -} - -/** - * Get the local matrix. - * - * @param self A matrix object. - * @param mat The submatrix returned in a specific format. - * - * @return 0 on success. - */ -int parms_MatGetSubMat(parms_Mat self, void **mat) -{ - return self->ops->getlmat(self, mat); -} - -/** - * Get the communication handler. - * - * @param self A matrix object. - * @param handler The communication handler returned. - * - * @return 0 on success. - */ -int parms_MatGetCommHandler(parms_Mat self, parms_Comm *handler) -{ - return self->ops->gethandler(self, handler); -} diff --git a/lib/parms/src/parms_mat_dvcsr.c b/lib/parms/src/parms_mat_dvcsr.c deleted file mode 100755 index 8560d7bf6..000000000 --- a/lib/parms/src/parms_mat_dvcsr.c +++ /dev/null @@ -1,1443 +0,0 @@ -#include "include/parms_opt_impl.h" -#include "include/parms_comm_impl.h" - -/*! \typedef parms_dvcsr - \brief_dvcsr is a synonym for struct parms_dvcsr. - */ -/*! \struct parms_dvcsr - \brief - */ -typedef struct parms_dvcsr { - /*! \var diag_mat The diagonal matrix stored in vcsr format. - */ - parms_vcsr diag_mat; - /*! \var offd_mat The off-diagonal matrix stored in vcsr format. - */ - parms_vcsr offd_mat; - /*! \var mvhandler The parms_Comm object for the matrix-vector product. - */ - parms_Comm mvhandler; - -} *parms_dvcsr; - -static int MatVec_dvcsr(parms_Mat self, FLOAT *x, FLOAT *y) -{ - int lsize, i, j, index, *pj, length; - parms_dvcsr data; - parms_Comm handler; - parms_vcsr diag_mat, offd_mat; - parms_Map is; - FLOAT *pa, *offsetptr, s; - - parms_VecPerm(x, self->is); - parms_VecPerm(y, self->is); - - /* extract diagonal and off-diagonal matrix */ - data = (parms_dvcsr)self->data; - handler = data->mvhandler; - diag_mat = data->diag_mat; - offd_mat = data->offd_mat; - is = self->is; - lsize = is->lsize; - - parms_CommDataBegin(handler, x, 0); - - /* perform local matrix vector product */ - for (i = 0; i < lsize; i++) { - y[i] = 0.0; - pj = diag_mat->pj[i]; - pa = diag_mat->pa[i]; - for (j = 0; j < diag_mat->nnzrow[i]; j++) { - index = pj[j]; - y[i] += pa[j] * x[index]; - } - } - - /* off-diagonal part matrix-vector product */ - parms_CommDataEnd(handler); - offsetptr = handler->buf_recv - lsize; - for (i = 0; i < is->ninf; i++) { - pj = offd_mat->pj[i]; - pa = offd_mat->pa[i]; - - for (j = 0; j < offd_mat->nnzrow[i]; j++) - y[i+is->nint] += pa[j] * offsetptr[pj[j]]; - - } - parms_VecInvPerm(x, self->is); - parms_VecInvPerm(y, self->is); - - return 0; -} - -/** - * Setup the matrix - * - * This is the most important function in the package. The local - * variables are divided into two categories: interior variables (not - * receiving data from other processors) and interface variables - * (receiving data from other processors). The interior variables are - * divided into independent variables (not sending data to other - * processors) and output variables (sending data to other processors) - * for *asymmetric* pattern. The independent variables are listed first, - * followd by output variables, and interface variables. The member - * variable schur_start is the number of independent - * variables. i.e., the start position of "Schur" complement in the - * local linear system. communication handler for the matrix-vector - * product is also created. - * - * @param self A matrix object. - * - * @return 0 on success. - */ -static int MatSetup_dvcsr(parms_Mat self) -{ - parms_dvcsr data; - parms_vcsr diag_mat = NULL, offd_mat = NULL; - parms_vcsr aux_data; - parms_Map is; - parms_Comm handler; - int i, j, *perm, *iperm, mypid, pid, npro, lsize, k1, k2; - int *rja=NULL, *tja=NULL, nnz, maxnnz, start, end, nrow; - int gnodv, cindex, index, gindex; - int *ginfvars, *ginfv2p, *ginfv2ps, *odperm, *neword, *clp, *info, - *disp, *pj; - int npsendrecv, pos, *procslist=NULL; - int *ptrvsend, *ptrvrecv=NULL, *cmap=NULL; - FLOAT *ra=NULL, *tma=NULL, val; - MPI_Comm comm; - - /* extract information arrays */ - is = self->is; - npro = is->npro; - mypid = is->pid; - self->issetup = true; - data = (parms_dvcsr)self->data; - aux_data = self->aux_data; - comm = is->comm; - handler = data->mvhandler; - lsize = parms_MapGetLocalSize(is); - perm = is->perm; - iperm = is->iperm; - - /* free member space */ - if(self->aux_data->space) - PARMS_FREE(self->aux_data->space); - -/* allocate memory for the diagonal matrix */ - if(self->isreset) - { - if(self->resetpattern == SAME_NONZERO_STRUCTURE) - { - /* copy data into parallel matrix struct */ - offd_mat = data->offd_mat; - diag_mat = data->diag_mat; - /* diagonal part */ - for(i=0; in; i++) - { - PARMS_MEMCPY(diag_mat->pa[i], aux_data->pa[i], diag_mat->nnzrow[i]); - } - /* off diagonal part */ - for(i=0; in; i++) - { - index = i+is->nint; - nrow = diag_mat->nnzrow[index]; - PARMS_MEMCPY(offd_mat->pa[i], &aux_data->pa[index][nrow], offd_mat->nnzrow[i]); - } - return 0; - } - else - { - /* free offd_mat for previous data. No need to free diag_mat - * for previous data, since array size (lsize) does not change - */ - diag_mat = data->diag_mat; - offd_mat = data->offd_mat; - if(offd_mat->n){ - PARMS_FREE(offd_mat->nnzrow); - PARMS_FREE(offd_mat->pa); - PARMS_FREE(offd_mat->pj); - } - PARMS_FREE(offd_mat); - - /* free previous communication handler */ - parms_CommFree(&data->mvhandler); - /* now create new communication handler */ - parms_Comm new_handler; - parms_CommCreate(&new_handler, comm); - handler = data->mvhandler = new_handler; - /* free processor info and table data */ - if(is->vstable) parms_TableFree(&is->vstable); - if(is->vsend) PARMS_FREE(is->vsend); - } - } - else{ - PARMS_NEW(data->diag_mat); - diag_mat = data->diag_mat; - PARMS_NEWARRAY(diag_mat->nnzrow, lsize); - PARMS_NEWARRAY(diag_mat->pj, lsize); - PARMS_NEWARRAY(diag_mat->pa, lsize); - diag_mat->n = lsize; - } - - is->nint = lsize-is->ninf; - is->schur_start = is->nint; - k1 = 0; - k2 = is->nint; - - for (i = 0; i < lsize; i++) { - if (perm[i] == -1) { - perm[i] = k1++; - } - else { - perm[i] = k2++; - } - } - - /* set up inverse permutation array */ - for (i = 0; i < lsize; i++) { - k1 = perm[i]; - iperm[k1] = i; - } - is->isperm = true; - - /* permute the rows of the local matrix */ - for (i = 0; i < lsize; i++) { - if (aux_data->nnzrow[i]) { - PARMS_RESIZE(aux_data->pj[i], aux_data->nnzrow[i]); - PARMS_RESIZE(aux_data->pa[i], aux_data->nnzrow[i]); - } - k1 = perm[i]; - diag_mat->pj[k1] = aux_data->pj[i]; - diag_mat->pa[k1] = aux_data->pa[i]; - diag_mat->nnzrow[k1] = aux_data->nnzrow[i]; - } - - PARMS_MEMCPY(aux_data->pj, diag_mat->pj, lsize); - PARMS_MEMCPY(aux_data->pa, diag_mat->pa, lsize); - PARMS_MEMCPY(aux_data->nnzrow, diag_mat->nnzrow, lsize); - - /* permute the column indices of the matrix which are corresponding - to the interior variables */ - for (i = 0; i < is->nint; i++) { - rja = aux_data->pj[i]; - nnz = aux_data->nnzrow[i]; - ra = aux_data->pa[i]; - for (j = 0; j < nnz; j++) { - rja[j] = perm[rja[j]]; /* in C style */ - } - } - - /* permute the local column indices of the matrix which are - corresponding to the interface variables */ - maxnnz = 0; - for (i = is->nint; i < lsize; i++) { - rja = aux_data->pj[i]; - nnz = aux_data->nnzrow[i]; - if (maxnnz < nnz) { - maxnnz = nnz; - } - diag_mat->nnzrow[i] = 0; - for (j = 0; j < nnz; j++) { - if (rja[j] >= 0) { - diag_mat->nnzrow[i]++; - rja[j] = perm[rja[j]]; /* in C style */ - } - } - } - - if (maxnnz) { - PARMS_NEWARRAY(tja, maxnnz); - PARMS_NEWARRAY(tma, maxnnz); - } - - /* allocate memory for the off-diagonal matrix */ - PARMS_NEW(data->offd_mat); - offd_mat = data->offd_mat; - offd_mat->n = is->ninf; - if (is->ninf) { - PARMS_NEWARRAY(offd_mat->nnzrow, is->ninf); - PARMS_NEWARRAY(offd_mat->pj, is->ninf); - PARMS_NEWARRAY(offd_mat->pa, is->ninf); - } - handler->nodv = parms_TableGetSize(is->table) - lsize; - if (handler->nodv) { - PARMS_NEWARRAY(handler->odvlist, handler->nodv); - } - - /* list column indices of diagonal part first */ - for (i = is->nint; i < lsize; i++) { - rja = aux_data->pj[i]; - ra = aux_data->pa[i]; - nnz = aux_data->nnzrow[i]; - index = i-is->nint; - k1 = 0; - k2 = diag_mat->nnzrow[i]; - nrow = diag_mat->nnzrow[i]; - for (j = 0; j < nnz; j++) { - if (rja[j] >= 0) { - tja[k1] = rja[j]; - tma[k1++] = ra[j]; - } - else { - tja[k2] = -rja[j]-1; - clp = parms_MapGlobalToLocal(is, tja[k2]); - if (clp != NULL) { - handler->odvlist[*clp-lsize] = tja[k2]; - } - tma[k2++] = ra[j]; - } - } - PARMS_MEMCPY(rja, tja, nnz); - PARMS_MEMCPY(ra, tma, nnz); - offd_mat->pj[index] = &rja[nrow]; - offd_mat->pa[index] = &ra[nrow]; - offd_mat->nnzrow[index] = nnz-nrow; - } - if (maxnnz) { - PARMS_FREE(tja); - PARMS_FREE(tma); - } - - /* sort the column indices of the diagonal matrix in ascending - order */ - for (i = 0; i < lsize; i++) { - nnz = diag_mat->nnzrow[i]; - rja = diag_mat->pj[i]; - ra = diag_mat->pa[i]; - for (j = 0; j < nnz-1; j++) { - for (k1 = j+1; k1 < nnz; k1++) { - if (rja[j] > rja[k1]) { - index = rja[j]; - rja[j] = rja[k1]; - rja[k1] = index; - - val = ra[j]; - ra[j] = ra[k1]; - ra[k1] = val; - } - } - } - } - - PARMS_NEWARRAY(info, npro); - PARMS_NEWARRAY(disp, npro+1); - - MPI_Allgather(&handler->nodv, 1, MPI_INT, info, 1, MPI_INT, comm); - disp[0] = 0; - for (i = 0; i < npro; i++) { - disp[i+1] = disp[i] + info[i]; - } - - /* total number of interface variables */ - gnodv = disp[npro]; - if (gnodv) { - PARMS_NEWARRAY(ginfvars, gnodv); - PARMS_NEWARRAY0(ginfv2p, gnodv); - PARMS_NEWARRAY(ginfv2ps, gnodv); - MPI_Allgatherv(handler->odvlist, handler->nodv, MPI_INT, ginfvars, - info, disp, MPI_INT, comm); - - /* variables sent to other processors */ - for (i = 0; i < npro; i++) { - if (i != mypid) { - for (j = disp[i]; j < disp[i+1]; j++) { - clp = parms_MapGlobalToLocal(is, ginfvars[j]); - if (clp != NULL && *clp < lsize) { - ginfv2p[j] = mypid; - } - } - } - } - - MPI_Allreduce(ginfv2p, ginfv2ps, gnodv, MPI_INT, MPI_SUM, comm); - PARMS_FREE(ginfv2p); - PARMS_NEWARRAY(handler->procs_recv, npro); - for (i = 0; i < npro; i++) { - info[i] = 0; - } - /* processor IDs from which processor mypid receives data */ - handler->nprecv = 0; - for (i = disp[mypid]; i < disp[mypid+1]; i++) { - pid = ginfv2ps[i]; - if (info[pid] == 0) { - handler->procs_recv[handler->nprecv++] = pid; - } - info[pid]++; - } - - if (handler->nprecv) { - PARMS_RESIZE(handler->procs_recv, handler->nprecv); - - /* sort neighbouring processors received data from in ascending order */ - for (i = 0; i < handler->nprecv-1; i++) { - for (j = i+1; j < handler->nprecv; j++) { - if (handler->procs_recv[i] > handler->procs_recv[j]) { - pid = handler->procs_recv[i]; - handler->procs_recv[i] = handler->procs_recv[j]; - handler->procs_recv[j] = pid; - } - } - } - } - else { - PARMS_FREE(handler->procs_recv); - } - - PARMS_NEWARRAY(handler->ptrvrecv, handler->nprecv+1); - handler->ptrvrecv[0] = 0; - for (i = 0; i < handler->nprecv; i++) { - pid = handler->procs_recv[i]; - handler->ptrvrecv[i+1] = handler->ptrvrecv[i] + info[pid]; - info[pid] = i; - } - - if (handler->nodv) { - PARMS_NEWARRAY(odperm, handler->nodv); - PARMS_NEWARRAY(handler->buf_recv, handler->nodv); - - /* reorder external variables: the data received from the same - processor are listed consecutively, processor by processor */ - j = disp[mypid]; - for (i = 0; i < handler->nodv; i++) { - pid = ginfv2ps[i+j]; - index = info[pid]; - odperm[i] = handler->ptrvrecv[index]++; - } - for (i = handler->nprecv-1; i > 0; i--) { - handler->ptrvrecv[i] = handler->ptrvrecv[i-1]; - } - handler->ptrvrecv[0] = 0; - - /* reorder the external interface variables */ - PARMS_NEWARRAY(neword, handler->nodv); - for (i = 0; i < handler->nodv; i++) { - index = odperm[i]; - gindex = handler->odvlist[i]; - parms_TablePut(is->table, gindex, index+lsize); - neword[index] = gindex; - } - - PARMS_FREE(handler->odvlist); - - PARMS_FREE(odperm); - handler->odvlist = neword; - } - /* change the global column indices of entries in external - matrix to the local ones */ - for (i = 0; i < offd_mat->n; i++) { - nnz = offd_mat->nnzrow[i]; - pj = offd_mat->pj[i]; - for (j = 0; j < nnz; j++) { - cindex = pj[j]; - clp = parms_MapGlobalToLocal(is, cindex); - pj[j] = *clp; - } - } - PARMS_FREE(ginfv2ps); - PARMS_FREE(ginfvars); - } - else { - handler->nprecv = 0; - PARMS_NEWARRAY(handler->ptrvrecv, 1); - handler->ptrvrecv[0] = 0; - } - - /* set up send information */ - MPI_Allgather(&handler->nprecv, 1, MPI_INT, info, 1, MPI_INT, comm); - disp[0] = 0; - for (i = 0; i < npro; i++) { - disp[i+1] = disp[i] + info[i]; - } - - if (disp[npro]) { - PARMS_NEWARRAY(procslist, disp[npro]); - MPI_Allgatherv(handler->procs_recv, handler->nprecv, MPI_INT, - procslist, info, disp, MPI_INT, comm); - } - - handler->npsend = 0; - PARMS_NEWARRAY(handler->procs_send, npro); - for (i = 0; i < npro; i++) { - if (i != mypid) { - for (j = disp[i]; j < disp[i+1]; j++) { - if (procslist[j] == mypid) { - handler->procs_send[handler->npsend++] = i; - break; - } - } - } - } - - if (disp[npro]) { - PARMS_FREE(procslist); - } - - if (handler->npsend) { - PARMS_RESIZE(handler->procs_send, handler->npsend); - } - else { - PARMS_FREE(handler->procs_send); - } - - PARMS_NEWARRAY(handler->ptrvsend, handler->npsend+1); - handler->ptrvsend[0] = 0; - ptrvsend = handler->ptrvsend; - ptrvrecv = handler->ptrvrecv; - npsendrecv = handler->npsend + handler->nprecv; - if (npsendrecv) { - PARMS_NEWARRAY(handler->status_send, npsendrecv); - PARMS_NEWARRAY(handler->req_send, npsendrecv); - handler->status_recv = handler->status_send + handler->npsend; - handler->req_recv = handler->req_send + handler->npsend; - for (i = 0; i < handler->npsend; i++) { - MPI_Irecv(&disp[i], 1, MPI_INT, handler->procs_send[i], - 100, comm, &handler->req_send[i]); - } - - for (i = 0; i < handler->nprecv; i++) { - info[i] = ptrvrecv[i+1] - ptrvrecv[i]; - MPI_Isend(&info[i], 1, MPI_INT, handler->procs_recv[i], - 100, comm, &handler->req_recv[i]); - } - - MPI_Waitall(handler->nprecv, handler->req_recv, - handler->status_recv); - MPI_Waitall(handler->npsend, handler->req_send, - handler->status_send); - - for (i = 0; i < handler->npsend; i++) { - ptrvsend[i+1] = ptrvsend[i] + disp[i]; - } - - nnz = ptrvsend[handler->npsend]; - if (nnz) { - PARMS_NEWARRAY(handler->buf_send, nnz); - PARMS_NEWARRAY(handler->vlist_send, nnz); - } - - handler->mdata_send = 0; - if (handler->npsend) { - for (i = 0; i < handler->npsend; i++) { - nnz = ptrvsend[i+1] - ptrvsend[i]; - if (handler->mdata_send < nnz) { - handler->mdata_send = nnz; - } - } - } - - /* handler->vlist_send stores the variables to be sent to adjacent - processors in global labeling */ - for (i = 0; i < handler->npsend; i++) { - pos = ptrvsend[i]; - nnz = ptrvsend[i+1] - ptrvsend[i]; - MPI_Irecv(&handler->vlist_send[pos], nnz, MPI_INT, - handler->procs_send[i], 200, comm, - &handler->req_send[i]); - } - - for (i = 0; i < handler->nprecv; i++) { - pos = ptrvrecv[i]; - nnz = ptrvrecv[i+1] - ptrvrecv[i]; - MPI_Isend(&handler->odvlist[pos], nnz, MPI_INT, - handler->procs_recv[i], 200, comm, - &handler->req_recv[i]); - } - - MPI_Waitall(handler->nprecv, handler->req_recv, - handler->status_recv); - MPI_Waitall(handler->npsend, handler->req_send, - handler->status_send); - - /* change global labelings to local ones */ - is->ninf_send = 0; - parms_TableCreate(&is->vstable, NULL, is->ninf); - if (is->isperm) { - for (i = 0; i < handler->npsend; i++) { - for (j = ptrvsend[i]; j < ptrvsend[i+1]; j++) { - cindex = handler->vlist_send[j]; - clp = parms_MapGlobalToLocal(is, cindex); - index = perm[*clp]; - handler->vlist_send[j] = index; - clp = parms_TableGet(is->vstable, index); - if (clp == NULL) { - parms_TablePut(is->vstable, index, is->ninf_send++); - } - } - } - } - - /* vsend is an array to store variables sent to other processors */ - if (is->ninf_send) { - PARMS_NEWARRAY(is->vsend, is->ninf_send); - if (is->nint) { - PARMS_NEWARRAY(cmap, is->nint); - } - for (i = 0; i < is->nint; i++) { - cmap[i] = -1; - } - - for (i = 0; i < handler->npsend; i++) { - for (j = ptrvsend[i]; j < ptrvsend[i+1]; j++) { - index = handler->vlist_send[j]; - clp = parms_TableGet(is->vstable, index); - is->vsend[*clp] = index; - - } - } - for (i = 0; i < is->ninf_send; i++) { - if (is->vsend[i] < is->nint) { - cmap[is->vsend[i]] = --is->schur_start; - } - } - - /* reorder the local variables in the following order: - * 1. independent variables which are uncoupled with variables in - * adjacent processors. - * 2. variables sent to adjacent processors but the - * corresponding rows are decoupled with variables in - * the adjacent processors. (asymmetric pattern) - * 3. interface variables - coupled with variables in adjacent - * processors in the sense of receiving data from - * the adjacent processors. - */ - if (is->schur_start != is->nint) { - k1 = 0; - for (i = 0; i < is->nint; i++) { - if (cmap[i] == -1) { - cmap[i] = k1++; - } - } - /* permute column indices of the local matrix */ - for (i = 0; i < lsize; i++) { - nnz = diag_mat->nnzrow[i]; - pj = diag_mat->pj[i]; - for (j = 0; j < nnz; j++) { - if (pj[j] < is->nint) { - pj[j] = cmap[pj[j]]; - } - } - } - - /* permute rows which are corresponding to interior variables */ - for (i = 0; i < is->nint; i++) { - k1 = cmap[i]; - diag_mat->nnzrow[k1] = aux_data->nnzrow[i]; - diag_mat->pj[k1] = aux_data->pj[i]; - diag_mat->pa[k1] = aux_data->pa[i]; - } - PARMS_MEMCPY(aux_data->nnzrow, diag_mat->nnzrow, is->nint); - PARMS_MEMCPY(aux_data->pj, diag_mat->pj, is->nint); - PARMS_MEMCPY(aux_data->pa, diag_mat->pa, is->nint); - - /* update perm and iperm */ - for (i = 0; i < lsize; i++) { - if (perm[i] < is->nint) { - perm[i] = cmap[perm[i]]; - } - } - - for (i = 0; i < lsize; i++) { - k1 = perm[i]; - iperm[k1] = i; - } - - /* update vlist_send */ - for (i = 0; i < handler->npsend; i++) { - start = handler->ptrvsend[i]; - end = handler->ptrvsend[i+1]; - for (j = start; j < end; j++) { - if (handler->vlist_send[j] < is->nint) { - handler->vlist_send[j] = cmap[handler->vlist_send[j]]; - } - } - } - - /* update vsend */ - for (i = 0; i < is->ninf_send; i++) { - if (is->vsend[i] < is->nint) { - is->vsend[i] = cmap[is->vsend[i]]; - parms_TablePut(is->vstable, is->vsend[i], i); - } - } - } - if (is->nint) { - PARMS_FREE(cmap); - } - - } - - } - - PARMS_FREE(info); - PARMS_FREE(disp); - - return 0; -} - - -static int MatSetCommType_dvcsr(parms_Mat self, COMMTYPE ctype) -{ - parms_dvcsr data; - parms_Comm handler; - - data = (parms_dvcsr)self->data; - handler = data->mvhandler; - handler->ctype = ctype; - return 0; -} - -static int MatMVPY_dvcsr(parms_Mat self, FLOAT alpha, FLOAT *x, - FLOAT beta, FLOAT *y, FLOAT *z) -{ - int lsize, i, j, index, *pj, length; - parms_dvcsr data; - parms_Comm handler; - parms_vcsr diag_mat, offd_mat; - parms_Map is; - FLOAT *pa, *offsetptr, s; - -/* no need to permute z here, since it only stores the result */ - parms_VecPerm(x, self->is); - parms_VecPerm(y, self->is); - - /* extract diagonal and off-diagonal matrix */ - data = (parms_dvcsr)self->data; - handler = data->mvhandler; - diag_mat = data->diag_mat; - offd_mat = data->offd_mat; - is = self->is; - lsize = is->lsize; - - /* post receive and send actions */ - parms_CommDataBegin(handler, x, 0); - - /* perform local matrix vector product */ - for (i = 0; i < lsize; i++) { - s = beta * y[i]; - length = diag_mat->nnzrow[i]; - pj = diag_mat->pj[i]; - pa = diag_mat->pa[i]; - for (j = 0; j < length; j++) { - index = pj[j]; - s += alpha * pa[j] * x[index]; - } - z[i] = s; - } - - parms_CommDataEnd(handler); - - offsetptr = handler->buf_recv - lsize; - for (i = 0; i < is->ninf; i++) { - length = offd_mat->nnzrow[i]; - pj = offd_mat->pj[i]; - pa = offd_mat->pa[i]; - s = z[i+is->nint]; - for (j = 0; j < length; j++) { - s += alpha * pa[j] * offsetptr[pj[j]]; - } - z[i+is->nint] = s; - } - -/* inverse permutation. z now has permutation of y, so has to be inverted as well */ - parms_VecInvPerm(x, self->is); - parms_VecInvPerm(y, self->is); - parms_VecInvPerm(z, self->is); - return 0; -} - -static int MatVecSchur_dvcsr(parms_Mat self, FLOAT *x, FLOAT *y, int - pos) -{ - parms_dvcsr data; - parms_Comm handler; - parms_Map is; - parms_vcsr offd_mat; - FLOAT *offsetptr, *pa, s; - int i, j, length, *pj, lsize; - - is = self->is; - lsize = is->lsize; - data = (parms_dvcsr)self->data; - handler = data->mvhandler; - offd_mat = data->offd_mat; - - if ((x == NULL) || (y == NULL)) { - return 0; - } - - parms_CommDataBegin(handler, x, pos); - for (i = pos; i < is->nint; i++) { - y[i-pos] = 0.0; - } - parms_CommDataEnd(handler); - offsetptr = handler->buf_recv - lsize; - for (i = 0; i < is->ninf; i++) { - length = offd_mat->nnzrow[i]; - pj = offd_mat->pj[i]; - pa = offd_mat->pa[i]; - s = 0.0; - for (j = 0; j < length; j++) { - s += pa[j] * offsetptr[pj[j]]; - } - y[i+is->nint-pos] = s; - } - return 0; -} - -static int MatGetDiag_dvcsr(parms_Mat self, void **mat) -{ - parms_dvcsr data; - parms_vcsr diag_mat; - - data = (parms_dvcsr)self->data; - diag_mat = data->diag_mat; - if (self->ilutype == PCARMS) { - parms_vcsr diag; - int i, nnz; - - PARMS_NEW(diag); - diag->n = diag_mat->n; - PARMS_NEWARRAY(diag->nnzrow, diag->n); - PARMS_NEWARRAY(diag->pj, diag->n); - PARMS_NEWARRAY(diag->pa, diag->n); - for (i = 0; i < diag->n; i++) { - nnz = diag->nnzrow[i] = diag_mat->nnzrow[i]; - if (nnz) { - PARMS_NEWARRAY(diag->pj[i], nnz); - PARMS_NEWARRAY(diag->pa[i], nnz); - PARMS_MEMCPY(diag->pj[i], diag_mat->pj[i], nnz); - PARMS_MEMCPY(diag->pa[i], diag_mat->pa[i], nnz); - } - } - *mat = diag; - return 0; - } - *mat = diag_mat; - return 0; -} - -static int MatGetOffDiag_dvcsr(parms_Mat self, void **mat) -{ - parms_dvcsr data; - parms_vcsr offd_mat; - - data = (parms_dvcsr)self->data; - offd_mat = data->offd_mat; - *mat = offd_mat; - return 0; - -} - -static int MatGetSubMat_dvcsr(parms_Mat self, void **mat) -{ - *mat = self->aux_data; - return 0; -} - -static int MatExtend_dvcsr(parms_Mat self, parms_Comm handler, int - start, void *mat, int *nn, void - **ext_mat) -{ - parms_vcsr lmat, new_mat; - parms_Map is; - MPI_Request *sreq=NULL, *rreq=NULL; - MPI_Status *sstatus=NULL, *rstatus=NULL; - MPI_Comm comm; - FLOAT **a_send=NULL, *pa_rbuf=NULL, *pa_sbuf=NULL, *rowa=NULL; - int **ja_send=NULL; - int *ia_send=NULL, *pia_send=NULL, *pia_recv=NULL, *pj_rbuf=NULL, *pj_sbuf=NULL; - int *pindex, *rowj, *rbuf=NULL, *sbuf=NULL; - int lsize, jcol, i, j, index, nnz, nodv, numsend, numrecv; - int n, pos, lindex, tag, length, k, m; - - lmat = (parms_vcsr)mat; - PARMS_NEW(new_mat); - is = self->is; - lsize = is->lsize; - - MPI_Comm_dup(handler->comm, &comm); - - if (is->ninf_send) { - PARMS_NEWARRAY(a_send, is->ninf_send); - PARMS_NEWARRAY(ja_send, is->ninf_send); - PARMS_NEWARRAY(ia_send, is->ninf_send); - - /* copy the matrix corresponding to the interface variables to - working arrays. the column indices are stored in global numbering */ - for (i = 0; i < is->ninf_send; i++) { - index = is->vsend[i] - start; - nnz = ia_send[i] = lmat->nnzrow[index]; - PARMS_NEWARRAY(a_send[i], nnz); - PARMS_NEWARRAY(ja_send[i], nnz); - - nnz = lmat->nnzrow[index]; - for (j = 0; j < nnz; j++) { - a_send[i][j] = lmat->pa[index][j]; - jcol = lmat->pj[index][j]; - if (jcol < lsize) { - if (is->isperm) { - jcol = is->iperm[jcol]; - ja_send[i][j] = is->lvars[jcol]; - } - else { - ja_send[i][j] = is->lvars[jcol]; - } - } - else { - jcol = handler->odvlist[jcol-lsize]; - ja_send[i][j] = jcol; - } - } - } - } - - - numsend = handler->ptrvsend[handler->npsend]; - numrecv = handler->ptrvrecv[handler->nprecv]; - - nodv = handler->nodv; - n = lmat->n + nodv; - - new_mat->n = n; - PARMS_NEWARRAY(new_mat->nnzrow, n); - PARMS_NEWARRAY(new_mat->pj, n); - PARMS_NEWARRAY(new_mat->pa, n); - - /* copy the local matrix to new_mat */ - for (i = 0; i < lmat->n; i++) { - nnz = new_mat->nnzrow[i] = lmat->nnzrow[i]; - if (nnz) { - PARMS_NEWARRAY(new_mat->pj[i], nnz); - PARMS_NEWARRAY(new_mat->pa[i], nnz); - PARMS_MEMCPY(new_mat->pj[i], lmat->pj[i], nnz); - PARMS_MEMCPY(new_mat->pa[i], lmat->pa[i], nnz); - } - } - - if (handler->npsend+handler->nprecv) { - PARMS_NEWARRAY(sstatus, handler->npsend+handler->nprecv); - PARMS_NEWARRAY(sreq, handler->npsend+handler->nprecv); - rstatus = sstatus + handler->npsend; - rreq = sreq + handler->npsend; - } - - tag = 800; - - /* exchange external matrix */ - if (numrecv) { - PARMS_NEWARRAY(rbuf, numrecv); - } - - /* receive the number of entries for each row in the extended - matrix */ - for (i = 0; i < handler->nprecv; i++) { - pos = handler->ptrvrecv[i]; - length = handler->ptrvrecv[i+1] - handler->ptrvrecv[i]; - MPI_Irecv(&rbuf[pos], length, MPI_INT, handler->procs_recv[i], - tag, comm, &rreq[i]); - } - - if (numsend) { - PARMS_NEWARRAY(sbuf, numsend); - } - - for (i = 0; i < handler->npsend; i++) { - pos = handler->ptrvsend[i]; - length = handler->ptrvsend[i+1]-handler->ptrvsend[i]; - for (j = 0; j < length; j++) { - lindex = handler->vlist_send[pos+j]; - pindex = parms_TableGet(is->vstable, lindex); - lindex = *pindex; - sbuf[pos+j] = ia_send[lindex]; - } - MPI_Isend(&sbuf[pos], length, MPI_INT, handler->procs_send[i], - tag, comm, &sreq[i]); - } - MPI_Waitall(handler->nprecv, rreq, rstatus); - MPI_Waitall(handler->npsend, sreq, sstatus); - - for (i = 0; i < handler->nprecv; i++) { - pos = handler->ptrvrecv[i]; - length = handler->ptrvrecv[i+1] - handler->ptrvrecv[i]; - for (j = 0; j < length; j++) { - new_mat->nnzrow[lmat->n+pos+j] = rbuf[pos+j]; - } - } - - j = 0; - for (i = 0; i < nodv; i++) { - length = new_mat->nnzrow[i+lsize]; - j += length; - if (length) { - PARMS_NEWARRAY(new_mat->pj[i+lmat->n], length); - PARMS_NEWARRAY(new_mat->pa[i+lmat->n], length); - } - } - if (j) { - PARMS_NEWARRAY(pj_rbuf, j); - PARMS_NEWARRAY(pa_rbuf, j); - } - - PARMS_NEWARRAY(pia_send, handler->npsend+1); - PARMS_NEWARRAY(pia_recv, handler->nprecv+1); - pia_send[0] = 0; - pia_recv[0] = 0; - - /* receive column indices of each row */ - for (i = 0; i < handler->nprecv; i++) { - pos = handler->ptrvrecv[i]; - length = handler->ptrvrecv[i+1] - handler->ptrvrecv[i]; - k = 0; - for (j = 0; j < length; j++) { - m = new_mat->nnzrow[lmat->n+pos+j]; - k += m; - } - pia_recv[i+1] = pia_recv[i] + k; - MPI_Irecv(&pj_rbuf[pia_recv[i]], k, MPI_INT, - handler->procs_recv[i], tag, comm, &rreq[i]); - } - - k = 0; - for (i = 0; i < handler->npsend; i++) { - pos = handler->ptrvsend[i]; - length = handler->ptrvsend[i+1]-handler->ptrvsend[i]; - for (j = 0; j < length; j++) { - lindex = handler->vlist_send[pos+j]; - pindex = parms_TableGet(is->vstable, lindex); - lindex = *pindex; - m = ia_send[lindex]; - k += m; - } - } - - if (k) { - PARMS_NEWARRAY(pj_sbuf, k); - PARMS_NEWARRAY(pa_sbuf, k); - } - - for (i = 0; i < handler->npsend; i++) { - pos = handler->ptrvsend[i]; - length = handler->ptrvsend[i+1]-handler->ptrvsend[i]; - k = 0; - for (j = 0; j < length; j++) { - lindex = handler->vlist_send[pos+j]; - pindex = parms_TableGet(is->vstable, lindex); - lindex = *pindex; - m = ia_send[lindex]; - PARMS_MEMCPY(&pj_sbuf[pia_send[i]+k], ja_send[lindex], m); - k += m; - } - pia_send[i+1] = pia_send[i] + k; - MPI_Isend(&pj_sbuf[pia_send[i]], k, MPI_INT, handler->procs_send[i], - tag, comm, &sreq[i]); - } - MPI_Waitall(handler->nprecv, rreq, rstatus); - MPI_Waitall(handler->npsend, sreq, sstatus); - - for (i = 0; i < handler->nprecv; i++) { - pos = handler->ptrvrecv[i]; - length = handler->ptrvrecv[i+1] - handler->ptrvrecv[i]; - k = 0; - for (j = 0; j < length; j++) { - m = new_mat->nnzrow[lmat->n+pos+j]; - PARMS_MEMCPY(new_mat->pj[lmat->n+pos+j], &pj_rbuf[pia_recv[i]+k], - m); - k += m; - } - } - - /* receive values of each row */ - -#if defined(DBL_CMPLX) - for (i = 0; i < handler->nprecv; i++) { - k = pia_recv[i+1] - pia_recv[i]; - MPI_Irecv(&pa_rbuf[pia_recv[i]], k, MPI_CMPLX, - handler->procs_recv[i], tag, comm, &rreq[i]); - } - - for (i = 0; i < handler->npsend; i++) { - pos = handler->ptrvsend[i]; - length = handler->ptrvsend[i+1]-handler->ptrvsend[i]; - k = 0; - for (j = 0; j < length; j++) { - lindex = handler->vlist_send[pos+j]; - pindex = parms_TableGet(is->vstable, lindex); - lindex = *pindex; - m = ia_send[lindex]; - PARMS_MEMCPY(&pa_sbuf[pia_send[i]+k], a_send[lindex], m); - k += m; - } - MPI_Isend(&pa_sbuf[pia_send[i]], k, MPI_CMPLX, - handler->procs_send[i], tag, comm, &sreq[i]); - } -#else - for (i = 0; i < handler->nprecv; i++) { - k = pia_recv[i+1] - pia_recv[i]; - MPI_Irecv(&pa_rbuf[pia_recv[i]], k, MPI_DOUBLE, - handler->procs_recv[i], tag, comm, &rreq[i]); - } - - for (i = 0; i < handler->npsend; i++) { - pos = handler->ptrvsend[i]; - length = handler->ptrvsend[i+1]-handler->ptrvsend[i]; - k = 0; - for (j = 0; j < length; j++) { - lindex = handler->vlist_send[pos+j]; - pindex = parms_TableGet(is->vstable, lindex); - lindex = *pindex; - m = ia_send[lindex]; - PARMS_MEMCPY(&pa_sbuf[pia_send[i]+k], a_send[lindex], m); - k += m; - } - MPI_Isend(&pa_sbuf[pia_send[i]], k, MPI_DOUBLE, - handler->procs_send[i], tag, comm, &sreq[i]); - } -#endif - - MPI_Waitall(handler->nprecv, rreq, rstatus); - MPI_Waitall(handler->npsend, sreq, sstatus); - - MPI_Comm_free(&comm); - for (i = 0; i < handler->nprecv; i++) { - pos = handler->ptrvrecv[i]; - length = handler->ptrvrecv[i+1] - handler->ptrvrecv[i]; - k = 0; - for (j = 0; j < length; j++) { - m = new_mat->nnzrow[lmat->n+pos+j]; - PARMS_MEMCPY(new_mat->pa[lmat->n+pos+j], &pa_rbuf[pia_recv[i]+k], - m); - k += m; - } - } - - /* free temporary arrays */ - if (handler->npsend) { - PARMS_FREE(pj_sbuf); - PARMS_FREE(pa_sbuf); - } - - if (handler->nprecv) { - PARMS_FREE(pj_rbuf); - PARMS_FREE(pa_rbuf); - } - - if (numrecv) { - PARMS_FREE(rbuf); - } - - if (numsend) { - PARMS_FREE(sbuf); - } - - - PARMS_FREE(pia_recv); - PARMS_FREE(pia_send); - - if (handler->npsend+handler->nprecv) { - PARMS_FREE(sreq); - PARMS_FREE(sstatus); - } - - if (is->ninf_send) { - for (i = 0; i < is->ninf_send; i++) { - if (ia_send[i]) { - PARMS_FREE(ja_send[i]); - PARMS_FREE(a_send[i]); - } - } - - PARMS_FREE(ia_send); - PARMS_FREE(ja_send); - PARMS_FREE(a_send); - } - - /* change the global column indices into local ones */ - for (i = lmat->n; i < n; i++) { - nnz = new_mat->nnzrow[i]; - rowj = new_mat->pj[i]; - rowa = new_mat->pa[i]; - k = 0; - for (j = 0; j < nnz; j++) { - jcol = rowj[j]; - pindex = parms_TableGet(is->table, jcol); - if (pindex) { - if (*pindex < lsize) { - rowj[j] = is->perm[*pindex] - start; - } - else if (*pindex >= lsize) { - rowj[j] = *pindex - start; - } - if (k < j) { - rowj[k] = rowj[j]; - rowa[k] = rowa[j]; - } - k++; - } - } - new_mat->nnzrow[i] = k; - if (k) { - PARMS_RESIZE(new_mat->pj[i], k); - PARMS_RESIZE(new_mat->pa[i], k); - } - } - *ext_mat = new_mat; - *nn = n; - return 0; -} - -static int MatFreeSubMat_dvcsr(parms_Mat self, void *mat) -{ - parms_vcsr vmat; - int i, nnz, isalloc; - - isalloc = self->isalloc; - - if(isalloc){ - vmat = (parms_vcsr)mat; - for (i = 0; i < vmat->n; i++) { - nnz = vmat->nnzrow[i]; - if (nnz) { - PARMS_FREE(vmat->pj[i]); - PARMS_FREE(vmat->pa[i]); - } - } - - if (vmat->n) { - PARMS_FREE(vmat->nnzrow); - PARMS_FREE(vmat->pj); - PARMS_FREE(vmat->pa); - } - - PARMS_FREE(vmat); - - mat = NULL; - } - - return 0; -} - -static int MatGetHandler_dvcsr(parms_Mat self, parms_Comm *handler) -{ - parms_dvcsr data; - - data = (parms_dvcsr)self->data; - *handler = data->mvhandler; - return 0; -} - -static struct parms_Mat_ops parms_matops_dvcsr = { - MatVec_dvcsr, - MatSetup_dvcsr, - MatSetCommType_dvcsr, - MatMVPY_dvcsr, - MatGetDiag_dvcsr, - MatGetOffDiag_dvcsr, - MatGetSubMat_dvcsr, - MatExtend_dvcsr, - MatVecSchur_dvcsr, - MatFreeSubMat_dvcsr, - MatGetHandler_dvcsr -}; - -int parms_MatFree_dvcsr(parms_Mat *self) -{ - parms_dvcsr data; - parms_vcsr diag_mat, offd_mat; - - data = (parms_dvcsr)(*self)->data; - - parms_CommFree(&data->mvhandler); - diag_mat = data->diag_mat; - PARMS_FREE(diag_mat->nnzrow); - PARMS_FREE(diag_mat->pa); - PARMS_FREE(diag_mat->pj); - PARMS_FREE(diag_mat); - - offd_mat = data->offd_mat; - if (offd_mat->n) { - PARMS_FREE(offd_mat->nnzrow); - PARMS_FREE(offd_mat->pa); - PARMS_FREE(offd_mat->pj); - } - PARMS_FREE(offd_mat); - - PARMS_FREE(data); - return 0; -} - -int parms_MatView_dvcsr(parms_Mat self, parms_Viewer v) -{ - int i, j, lsize, pid, npro, length; - int *rja, *lvlist, *iperm, index; - FLOAT *ra; - parms_dvcsr data; - parms_vcsr diag_mat, offd_mat; - parms_Map is; - FILE *fp; - - is = self->is; - lvlist = is->lvars; - iperm = is->iperm; - lsize = is->lsize; - pid = is->pid; - npro = is->npro; - data = (parms_dvcsr)self->data; - - parms_ViewerGetFP(v, &fp); - - if (pid == 0) { - fprintf(fp, "There are %d processors available\n", npro); - fprintf(fp, "The format of output local equations is:\n"); - fprintf(fp, "(pid,local_row_index)=>(pid,global_row_index)\n"); - fprintf(fp, "(pid,local_row_index, local_column_index) = value\n"); - } - fprintf(fp, "\n"); - - for (i = 0; i < lsize; i++) { - index = iperm[i]; - fprintf(fp, "(%d,%d)=>(%d,%d)\n", pid, i, pid, lvlist[index]); - } - fprintf(fp, "\n"); - - diag_mat = data->diag_mat; - offd_mat = data->offd_mat; - fprintf(fp, "Local diagonal matrix on processor %d is\n", pid); - -#if defined(DBL_CMPLX) - for (i = 0; i < lsize; i++) { - length = diag_mat->nnzrow[i]; - rja = diag_mat->pj[i]; - ra = diag_mat->pa[i]; - for (j = 0; j < length; j++) { - fprintf(fp, "(%d,%d,%d) = (%f, %f) ", pid, i, rja[j], creal(ra[j]), cimag(ra[j])); - } - fprintf(fp, "\n"); - } - - fprintf(fp, "Local off-diagonal matrix on processor %d is \n", pid); - for (i = 0; i < is->ninf; i++) { - length = offd_mat->nnzrow[i]; - rja = offd_mat->pj[i]; - ra = offd_mat->pa[i]; - for (j = 0; j < length; j++) { - fprintf(fp, "(%d,%d,%d) = (%f, %f) ", pid, i, rja[j], creal(ra[j]), cimag(ra[j])); - } - fprintf(fp, "\n"); - } -#else - for (i = 0; i < lsize; i++) { - length = diag_mat->nnzrow[i]; - rja = diag_mat->pj[i]; - ra = diag_mat->pa[i]; - for (j = 0; j < length; j++) { - fprintf(fp, "(%d,%d,%d) = %f ", pid, i, rja[j], ra[j]); - } - fprintf(fp, "\n"); - } - - fprintf(fp, "Local off-diagonal matrix on processor %d is \n", pid); - for (i = 0; i < is->ninf; i++) { - length = offd_mat->nnzrow[i]; - rja = offd_mat->pj[i]; - ra = offd_mat->pa[i]; - for (j = 0; j < length; j++) { - fprintf(fp, "(%d,%d,%d) = %f ", pid, i, rja[j], ra[j]); - } - fprintf(fp, "\n"); - } -#endif - fprintf(fp, "\n"); - parms_ViewerStoreFP(v, fp); - parms_CommView(data->mvhandler, v); - return 0; -} - - -int parms_MatViewCOO_dvcsr(parms_Mat self, parms_Viewer v) -{ - int i, j, lsize, pid, npro, length; - int *rja; - - FLOAT *ra; - parms_dvcsr data; - parms_vcsr diag_mat, offd_mat; - parms_Map is; - FILE *fp; - - is = self->is; - lsize = is->lsize; - pid = is->pid; - npro = is->npro; - data = (parms_dvcsr)self->data; - - parms_ViewerGetFP(v, &fp); - - if (pid == 0) { - fprintf(fp, "There are %d processors available\n", npro); - fprintf(fp, "The format of output local equations is:\n"); - fprintf(fp, "local_row_index local_column_index value\n"); - } - - fprintf(fp, "\n"); - - diag_mat = data->diag_mat; - offd_mat = data->offd_mat; - -#if defined(DBL_CMPLX) - for (i = 0; i < lsize; i++) { - length = diag_mat->nnzrow[i]; - rja = diag_mat->pj[i]; - ra = diag_mat->pa[i]; - for (j = 0; j < length; j++) { - fprintf(fp, "%d %d %f %f \n", i, rja[j], creal(ra[j]), cimag(ra[j])); - } - } - - for (i = 0; i < is->ninf; i++) { - length = offd_mat->nnzrow[i]; - rja = offd_mat->pj[i]; - ra = offd_mat->pa[i]; - for (j = 0; j < length; j++) { - fprintf(fp, "%d %d %f %f \n", i+lsize-is->ninf, rja[j], creal(ra[j]), cimag(ra[j])); - } - } -#else - for (i = 0; i < lsize; i++) { - length = diag_mat->nnzrow[i]; - rja = diag_mat->pj[i]; - ra = diag_mat->pa[i]; - for (j = 0; j < length; j++) { - fprintf(fp, "%d %d %f \n", i, rja[j], ra[j]); - } - } - - for (i = 0; i < is->ninf; i++) { - length = offd_mat->nnzrow[i]; - rja = offd_mat->pj[i]; - ra = offd_mat->pa[i]; - for (j = 0; j < length; j++) { - fprintf(fp, "%d %d %f \n", i+lsize-is->ninf, rja[j], ra[j]); - } - } -#endif - parms_ViewerStoreFP(v, fp); - return 0; -} - - -int parms_MatCreate_dvcsr(parms_Mat self) -{ - parms_dvcsr data; - parms_Comm mvhandler; - MPI_Comm comm; - - PARMS_MEMCPY(self->ops, &parms_matops_dvcsr, 1); - PARMS_NEW(data); - comm = self->is->comm; - parms_CommCreate(&mvhandler, comm); - data->mvhandler = mvhandler; - - self->data = data; - return 0; -} diff --git a/lib/parms/src/parms_mat_vcsr.c b/lib/parms/src/parms_mat_vcsr.c deleted file mode 100755 index a5a5eec91..000000000 --- a/lib/parms/src/parms_mat_vcsr.c +++ /dev/null @@ -1,213 +0,0 @@ -#include "include/parms_opt_impl.h" -#include "include/parms_mat_impl.h" - -static int MatVec_vcsr(parms_Mat self, FLOAT *x, FLOAT *y) -{ - int lsize, i, j, *pj, length; - parms_vcsr matrix; - parms_Map is; - FLOAT *pa; - - - /* extract diagonal and off-diagonal matrix */ - matrix = self->aux_data; - is = self->is; - lsize = parms_MapGetLocalSize(is); - - for (i = 0; i < lsize; i++) { - y[i] = 0.0; - pj = matrix->pj[i]; - pa = matrix->pa[i]; - for (j = 0; j < matrix->nnzrow[i]; j++) { - y[i] += pa[j] * x[pj[j]]; - } - } - - return 0; -} - -static int MatMVPY_vcsr(parms_Mat self, FLOAT alpha, FLOAT *x, - FLOAT beta, FLOAT *y, FLOAT *z) -{ - int lsize, i, j, index, *pj, nnz; - parms_vcsr matrix; - parms_Map is; - FLOAT *pa, s; - - /* extract diagonal and off-diagonal matrix */ - matrix = self->aux_data; - is = self->is; - lsize = is->lsize; - - for (i = 0; i < lsize; i++) { - s = beta * y[i]; - nnz = matrix->nnzrow[i]; - pj = matrix->pj[i]; - pa = matrix->pa[i]; - for (j = 0; j < nnz; j++) { - index = pj[j]; - s += alpha * pa[j] * x[index]; - } - z[i] = s; - } - - return 0; -} - -static int MatSetup_vcsr(parms_Mat self) -{ - /* free member space in aux_data */ - if(self->aux_data->space) - PARMS_FREE(self->aux_data->space); - self->issetup = true; - self->is->ninf_send = 0; - self->is->schur_start = self->is->lsize; - return 0; -} - -static int MatGetDiag_vcsr(parms_Mat self, void **mat) -{ - if (self->ilutype == PCARMS) { - parms_vcsr diag, diag_mat; - int i, nnz; - - diag_mat = self->aux_data; - PARMS_NEW(diag); - diag->n = diag_mat->n; - PARMS_NEWARRAY(diag->nnzrow, diag->n); - PARMS_NEWARRAY(diag->pj, diag->n); - PARMS_NEWARRAY(diag->pa, diag->n); - for (i = 0; i < diag->n; i++) { - nnz = diag->nnzrow[i] = diag_mat->nnzrow[i]; - if (nnz) { - PARMS_NEWARRAY(diag->pj[i], nnz); - PARMS_NEWARRAY(diag->pa[i], nnz); - PARMS_MEMCPY(diag->pj[i], diag_mat->pj[i], nnz); - PARMS_MEMCPY(diag->pa[i], diag_mat->pa[i], nnz); - } - } - *mat = diag; - return 0; - } - *mat = self->aux_data; - return 0; -} - -static struct parms_Mat_ops parms_matops_vcsr = { - MatVec_vcsr, - MatSetup_vcsr, - 0, - MatMVPY_vcsr, - MatGetDiag_vcsr, - MatGetDiag_vcsr, - 0, - 0, - 0, - 0 -}; - -int parms_MatView_vcsr(parms_Mat self, parms_Viewer v) -{ - int i, j, lsize, pid, length, *rja; - FLOAT *ra; - parms_vcsr matrix; - parms_Map is; - FILE *fp; - - is = self->is; - lsize = parms_MapGetLocalSize(is); - pid = is->pid; - parms_ViewerGetFP(v, &fp); - - fprintf(fp, "There is one processor available\n"); - fprintf(fp, "The format of output local equations is:\n"); - fprintf(fp, "(pid,local_row_index)=>(pid,global_row_index)\n"); - fprintf(fp, "(pid,local_row_index, local_column_index) = value\n"); - fprintf(fp, "\n"); - - for (i = 0; i < lsize; i++) { - fprintf(fp, "(%d,%d) => (%d, %d)\n", pid, i, pid, i); - } - - matrix = self->aux_data; - fprintf(fp, "Local matrix on processor %d is\n", pid); -#if defined(DBL_CMPLX) - for (i = 0; i < lsize; i++) { - length = matrix->nnzrow[i]; - rja = matrix->pj[i]; - ra = matrix->pa[i]; - for (j = 0; j < length; j++) { - fprintf(fp, "(%d,%d,%d) = (%f, %f) ", pid, i, rja[j], creal(ra[j]), cimag(ra[j])); - } - fprintf(fp, "\n"); - } -#else - for (i = 0; i < lsize; i++) { - length = matrix->nnzrow[i]; - rja = matrix->pj[i]; - ra = matrix->pa[i]; - for (j = 0; j < length; j++) { - fprintf(fp, "(%d,%d,%d) = %f ", pid, i, rja[j], ra[j]); - } - fprintf(fp, "\n"); - } -#endif - parms_ViewerStoreFP(v, fp); - return 0; -} - -int parms_MatViewCOO_vcsr(parms_Mat self, parms_Viewer v) -{ - int i, j, lsize, pid, length, *rja; - FLOAT *ra; - parms_vcsr matrix; - parms_Map is; - FILE *fp; - - is = self->is; - lsize = parms_MapGetLocalSize(is); - pid = is->pid; - parms_ViewerGetFP(v, &fp); - - fprintf(fp, "There is one processor available\n"); - fprintf(fp, "The format of output local equations is:\n"); - fprintf(fp, "(local_row_index local_column_index value\n"); - fprintf(fp, "\n"); -/* - for (i = 0; i < lsize; i++) { - fprintf(fp, "(%d,%d) => (%d, %d)\n", pid, i, pid, i); - } -*/ - matrix = self->aux_data; - fprintf(fp, "Local diagonal matrix on processor %d is\n", pid); -#if defined(DBL_CMPLX) - for (i = 0; i < lsize; i++) { - length = matrix->nnzrow[i]; - rja = matrix->pj[i]; - ra = matrix->pa[i]; - for (j = 0; j < length; j++) { - fprintf(fp, "%d %d %f %f \n", i, rja[j], creal(ra[j]), cimag(ra[j])); - } - fprintf(fp, "\n"); - } -#else - for (i = 0; i < lsize; i++) { - length = matrix->nnzrow[i]; - rja = matrix->pj[i]; - ra = matrix->pa[i]; - for (j = 0; j < length; j++) { - fprintf(fp, "%d %d %f \n", i, rja[j], ra[j]); - } - fprintf(fp, "\n"); - } -#endif - parms_ViewerStoreFP(v, fp); - return 0; -} - - -int parms_MatCreate_vcsr(parms_Mat self) -{ - PARMS_MEMCPY(self->ops, &parms_matops_vcsr, 1); - return 0; -} diff --git a/lib/parms/src/parms_mem.c b/lib/parms/src/parms_mem.c deleted file mode 100755 index a06037d0d..000000000 --- a/lib/parms/src/parms_mem.c +++ /dev/null @@ -1,178 +0,0 @@ -/*-------------------------------------------------------------------- - parms_calloc : allocate an array of count entries of size bytes - each. - parms_free : free the memory pointed by a pointer. - parms_malloc : return a pointer to the allocated memory. - parms_resize : rechange the size of the memory pointed to by a - pointer. - - Those functions are NOT used directly. You should use macros - PARMS_NEW, PARMS_NEWARRAY, PARMS_NEWARRAY0 instead. - - $Id: parms_mem.c,v 1.1.1.1 2006-11-27 22:28:01 zzli Exp $ ---------------------------------------------------------------------*/ - -#include "parms_mem.h" - -#ifdef C99 - -/** - * Return a pointer to the allocated memory. - * - * @param size The size of memory allocated. - * @param line The line number at which a error occurs. - * @param func The function name within which a error occurs. - * @param fname The file name in which a error occurs. - * - * @return A pinter to the allocated memory. - */ -void *parms_malloc(long size, int line, const char *func, - const char *fname) -{ - void *ptr; - - if (size <= 0) { - fprintf(stderr, "Error: size = %ld at line %d in function %s at file %s\n", size, line, func, fname); - exit(1); - } - ptr = malloc(size); - if (ptr == NULL) { - fprintf(stderr, "Error: size = %ld at line %d in function %s at file %s\n", size, line, func, fname); - exit(1); - } - return ptr; -} - -/** - * Allocate a memory for count etries each with size bytes. - * - * @param count The number of entries allocated. - * @param size The size in bytes of each entry. - * @param line The line number at which a error occurs. - * @param func The function name within which a error occurs. - * @param fname The file name in which a error occurs. - * - * @return A pointer to the allocated memory. - */ -void *parms_calloc(long count, long size, const int line, const char *func, - const char *fname) -{ - void *ptr; - - if (count <= 0 || size <= 0) { - fprintf(stderr, "Error: count = %ld, size = %ld at line %d in function %s at file %s\n", count, size, line, func, fname); - exit(1); - } - - ptr = calloc(count, size); - if (ptr == NULL) { - fprintf(stderr, "Error: count = %ld, size = %ld at line %d in function %s at file %s\n", count, size, line, func, fname); - exit(1); - } - return ptr; -} - -/** - * Rechange the size of the memory allocated. - * - * @param ptr The original pointer. - * @param size The new size of the memory. - * @param line The line number at which a error occurs. - * @param func The function name within which a error occurs. - * @param fname The file name in which a error occurs. - * - * @return A pointer to the memory of size bytes. - */ -void *parms_resize(void *ptr, long size, const int line, - const char *func, const char *fname) -{ - if (ptr == NULL) { - fprintf(stderr, "Error: ptr is a NULL pointer when reallocing memory at line %d in function %s at file %s\n", line, func, fname); - exit(1); - } - if (size <= 0) { - fprintf(stderr, "Error: size = %ld at line %d in function %s at file %s\n", size, line, func, fname); - exit(1); - } - ptr = realloc(ptr, size); - return ptr; -} - -/** - * Free the memory pointed by ptr. - * - * @param ptr A pointer. - * @param line The line number at which a error occurs. - * @param func The function name within which a error occurs. - * @param fname The file name in which a error occurs. - */ -void parms_free(void *ptr, const int line, const char *func, const char *fname) -{ - if (ptr == NULL) { - fprintf(stderr, "Cannot free NULL at line %d in function %s at file %s\n", line, func, fname); - exit(1); - } - free(ptr); -} - -#else - -void *parms_malloc(long size, int line, const char *fname) -{ - void *ptr; - - if (size <= 0) { - fprintf(stderr, "Error: size = %ld at line %d in file %s\n", size, line, fname); - exit(1); - } - ptr = malloc(size); - if (ptr == NULL) { - fprintf(stderr, "Error: size = %ld at line %d in file %s\n", size, line, fname); - exit(1); - } - return ptr; -} - -void *parms_calloc(long count, long size, const int line, const char *fname) -{ - void *ptr; - - if (count <= 0 || size <= 0) { - fprintf(stderr, "Error: count = %ld, size = %ld at line %d in file %s\n", count, size, line, fname); - exit(1); - } - - ptr = calloc(count, size); - if (ptr == NULL) { - fprintf(stderr, "Error: count = %ld, size = %ld at line %d in file %s\n", count, size, line, fname); - exit(1); - } - return ptr; -} - -void *parms_resize(void *ptr, long size, const int line, - const char *fname) -{ - if (ptr == NULL) { - fprintf(stderr, "Error: ptr is a NULL pointer when reallocing memory at line %d in file %s\n", line, fname); - exit(1); - } - if (size <= 0) { - fprintf(stderr, "Error: size = %ld at line %d in file %s\n", size, line, fname); - exit(1); - } - ptr = realloc(ptr, size); - return ptr; -} - -void parms_free(void *ptr, const int line, const char *fname) -{ - if (ptr == NULL) { - fprintf(stderr, "Cannot free NULL at line %d in file %s\n", line, fname); - exit(1); - } - free(ptr); -} - -#endif - diff --git a/lib/parms/src/parms_operator.c b/lib/parms/src/parms_operator.c deleted file mode 100755 index 49173021f..000000000 --- a/lib/parms/src/parms_operator.c +++ /dev/null @@ -1,90 +0,0 @@ -/*-------------------------------------------------------------------- - - parms_OperatorCreate - parms_OperatorFree - parms_OperatorApply - parms_OperatorLsol - parms_OperatorInvS - parms_OperatorAscend - parms_OperatorGetSchurPos - parms_OperatorGetNnz - parms_OperatorView - - Users are NOT encouraged to call those functions directly. - - $Id: parms_operator.c,v 1.1.1.1 2006-11-27 22:28:01 zzli Exp $ - ------------------------------------------------------------------*/ -#include "include/parms_mat_impl.h" -#include "./include/parms_opt_impl.h" - -int parms_OperatorFree(parms_Operator *self) -{ - - (*self)->ref--; - if ((*self)->ref == 0 ) { - (*self)->ops->operator_free(self); - PARMS_FREE((*self)->ops); - PARMS_FREE(*self); - } - return 0; -} - -int parms_OperatorCreate(parms_Operator *self) -{ - parms_Operator newOpt; - - PARMS_NEW0((newOpt)); - newOpt->ref = 1; - PARMS_NEW0((newOpt)->ops); - - newOpt->ops->apply = 0; - newOpt->ops->lsol = 0; - newOpt->ops->invs = 0; - newOpt->ops->getu = 0; - newOpt->ops->ascend = 0; - *self = newOpt; - return 0; -} - -int parms_OperatorView(parms_Operator self, parms_Viewer v) -{ - - return self->ops->operator_view(self, v); -} - -int parms_OperatorApply(parms_Operator self, FLOAT *y, FLOAT *x) -{ - return self->ops->apply(self, y, x); -} - -int parms_OperatorLsol(parms_Operator self, FLOAT *y, FLOAT *x) -{ - return self->ops->lsol(self, y, x); -} - -int parms_OperatorInvS(parms_Operator self, FLOAT *y, FLOAT *x) -{ - return self->ops->invs(self, y, x); -} - -/* used by PCSCHURRAS (AF) */ -int parms_OperatorGetU(parms_Operator self, void **mat) -{ - return self->ops->getu(self, mat); -} - -int parms_OperatorAscend(parms_Operator self, FLOAT *y, FLOAT *x) -{ - return self->ops->ascend(self, y, x); -} - -int parms_OperatorGetSchurPos(parms_Operator self) -{ - return self->ops->getssize(self); -} - -void parms_OperatorGetNnz(parms_Operator self, int *nnz_mat, int - *nnz_pc) -{ - self->ops->getnnz(self, nnz_mat, nnz_pc); -} diff --git a/lib/parms/src/parms_pc.c b/lib/parms/src/parms_pc.c deleted file mode 100755 index 29616d5db..000000000 --- a/lib/parms/src/parms_pc.c +++ /dev/null @@ -1,683 +0,0 @@ -/*-------------------------------------------------------------------- - parms_PCCreate : create a preconditioner object. - parms_PCCreateAbstract : create an abstract preconditioner object. - parms_PCFree : free the memory for the pc. - parms_PCILU : Perform the ilu factorization for the local preconditioner. - parms_PCSetBsize : set the block size for ARMS. - parms_PCSetFill : set the fill-in parameter for ILUT and - ARMS. - parms_PCSetILUType : set the type of ILU. - parms_PCSetInnerEps : set the convergence tolerance for the - inner GMRES. - parms_PCSetInnerKSize : set the restart size for the inner GMRES. - parms_PCSetInnerMaxits : set the maximum iteration counts for the - inner GMRES. - parms_PCSetNlevels : set the number of levels for ARMS. - parms_PCSetOP : set matrix for the preconditioner object. - parms_PCSetParams : set parameters for the preconditioner - object. - parms_PCSetTol : set the drop tolerance for ILUT and ARMS. - parms_PCSetTolInd : set the drop tolerance for finding - independent sets in ARMS - parms_PCSetType : set the type of preconditioner. - parms_PCSetup : set up the preconditioner. - parms_PCApply : applying preconditioner. - parms_PCGetRatio : get the ratio of the number of nonzeros of - the original matrix to that of the - preconditioning matrix. - parms_PCView : dump the preconditioner object. - - A code fragment for using preconditioning functions: - - parms_PC pc; - parms_mat mat; - - // create a preconditioner - parms_PCCreate(&pc, mat); - // set the type of the preconditioner. - parms_PCSetType(pc, PCRAS); - // set ILU type - parms_PCSetILUType(pc, PCILUT); - // set fill-in parameters - parms_PCSetFill(pc, fill); - // set drop tolerances - parms_PCSetTol(pc, tol); - // set up the preconditioning matrix. - parms_PCSetup(pc); - ... - // free the memory for pc. - parms_PCFree(&pc); - - $Id: parms_pc.c,v 1.1.1.1 2006-11-27 22:28:01 zzli Exp $ - ------------------------------------------------------------------*/ -#include "./include/parms_pc_impl.h" -#include "./include/parms_opt_impl.h" - -/*------------Protos-------------*/ -/* -int parms_ilu0_vcsr(parms_Mat self, parms_FactParam param, void *data, parms_Operator *op); -int parms_iluk_vcsr(parms_Mat self, parms_FactParam param, void *data, parms_Operator *op); -int parms_ilut_vcsr(parms_Mat self, parms_FactParam param, void *data, parms_Operator *op); -int parms_arms_vcsr(parms_Mat self, parms_FactParam param, void *data, parms_Operator *op); - -int parms_PCCreate_BJ(parms_PC self); -int parms_PCCreate_Schur(parms_PC self); -int parms_PCCreate_RAS(parms_PC self); -*/ -/*----------End Protos------------------*/ - -/** - * Free the memory for the preconditioner object pointed to by self. - * - * @param self A pointer to the memory for the preconditioner object. - * - * @return 0 on success. - */ -int parms_PCFree(parms_PC *self) -{ - (*self)->ref--; - if ((*self)->ref == 0 ) { - (*self)->ops->pc_free(self); - parms_MatFree(&(*self)->A); - if ((*self)->isperm) { - PARMS_FREE((*self)->perm); - PARMS_FREE((*self)->iperm); - } - PARMS_FREE((*self)->param); - PARMS_FREE((*self)->ops); - PARMS_FREE(*self); - } - - return 0; -} - -/** - * Set up the preconditioner (create the preconditioning matrix). - * - * @param self A preconditioner object. - * - * @return 0 on success. - */ -int parms_PCSetup(parms_PC self) -{ - parms_FactParam param; - - param = self->param; - -/*--- Define default preconditioner (Block Jacobi/ILU0) ---*/ - if(self->isiluset == false){ - parms_PCSetILUType(self, PCILU0); - } - if(self->istypeset == false){ - parms_PCSetType(self, PCBJ); - } -/*--- end definition of default precon ---*/ - - if (self->pctype != PCSCHUR) { - param->ipar[4] = 0; - param->ipar[5] = 0; - } - - /* reuse preconditioner (AF) - if (self->issetup == false) { - self->issetup = true; - //self->ops->setup(self); - } - else - { - // Re-use the precon data struct - self->issetup = false; - self->istypeset = false; - self->ops->pc_free(&self); - parms_PCSetType(self, self->pctype); - self->issetup = true; - self->ops->setup(self); - } */ - - - self->issetup = true; - self->ops->setup(self); - - return 0; -} - -/** - * Create a preconditioner object based on the matrix A. - * - * @param self A preconditioner object. - * @param A A matrix object. - * - * @return 0 on success. - */ -int parms_PCCreate(parms_PC *self, parms_Mat A) -{ - parms_PC new_pc; - int i; - - PARMS_NEW0((new_pc)); - new_pc->ref = 1; - PARMS_NEW0((new_pc)->ops); - new_pc->isiluset = false; - new_pc->istypeset = false; - new_pc->issetup = false; - new_pc->isopset = true; - new_pc->isperm = false; - new_pc->A = A; - A->ref++; - PARMS_NEW(new_pc->param); - /* set up default values for */ - new_pc->param->mc = 1; - new_pc->param->isalloc = false; - for (i = 0; i < 7; i++) { - new_pc->param->lfil[i] = 10; - } - new_pc->param->ipar[0] = 5; - new_pc->param->ipar[1] = 1; - new_pc->param->ipar[2] = 20; - new_pc->param->ipar[3] = 0; - new_pc->param->ipar[4] = 0; - new_pc->param->ipar[5] = 0; - for (i = 6; i < 18; i++) { - new_pc->param->ipar[i] = 0; - } - - for (i = 0; i < 7; i++) { - new_pc->param->droptol[i] = 0.001; - } - new_pc->param->tolind = 0.05; - - new_pc->param->pgfpar[0] = 0.001; - new_pc->param->pgfpar[1] = 0.001; - - *self = new_pc; - return 0; -} - -/** - * Create an abstract preconditioner object. - * - * @param self A pointer to the preconditioner object. - * - * @return 0 on success. - */ -int parms_PCCreateAbstract(parms_PC *self) -{ - parms_PC new_pc; - - PARMS_NEW(new_pc); - PARMS_NEW0((new_pc)->ops); - new_pc->isiluset = false; - new_pc->istypeset = false; - new_pc->issetup = false; - new_pc->isopset = false; - new_pc->isperm = false; - PARMS_NEW(new_pc->param); - new_pc->param->isalloc = false; - - *self = new_pc; - return 0; -} - -/** - * Solve \f$self z = y\f$ - * - * @param self A preconditioner object. - * @param y A right-hand-side vector. - * @param z The solution vector. - * - * @return 0 on success. - */ -int parms_PCApply(parms_PC self, FLOAT *y, FLOAT *z) -{ - return self->ops->apply(self, y, z); -} - -/** - * Set the matrix to create the preconditioning matrix. - * - * @param self A preconditioner object. - * @param A The matrix to be used for creating PC. - * - * @return 0 on success. - */ -int parms_PCSetOP(parms_PC self, parms_Mat A) -{ - - self->A = A; - if(!self->isopset){ - A->ref++; - self->isopset = true; - } -// self->issetup = false; - return 0; -} - -/** - * Dump preconditioner object self. - * - * @param self A preconditioner object. - * @param v A viewer object. - */ -void parms_PCView(parms_PC self, parms_Viewer v) -{ - self->ops->pc_view(self, v); -} - -/** - * Set preconditioner type. - * - * Currently supported preconditioners: - * \f{tabular}{ll} - * PCBJ & block Jacobi \\ - * PCRAS & restricted additive Schwarz \\ - * PCSCHUR & Schur complement - * \f} - * - * @param self A preconditioner object. - * @param pctype The type of preconditioner. - * - PCBJ block Jacobi - * - PCRAS restricted additive Schwarz - * - PCSCHUR Schur complement - * - * @return 0 on success. - */ -int parms_PCSetType(parms_PC self, PCTYPE pctype) -{ - parms_Map is; - parms_Mat A; - BOOL isserial; - - if (self->istypeset && self->pctype == pctype) { - return 0; - } - - A = self->A; - is = A->is; - isserial = is->isserial; - if (self->issetup) { - self->ops->pc_free(&self); - } - - if (isserial) { - self->pctype = PCBJ; - parms_PCCreate_BJ(self); - self->istypeset = true; - self->issetup = false; - } - else { - self->pctype = pctype; - if(pctype == PCBJ) - parms_PCCreate_BJ(self); - else if(pctype == PCRAS) - parms_PCCreate_RAS(self); - else if(pctype == PCSCHUR) - parms_PCCreate_Schur(self); - else if(pctype == PCSCHURRAS) - parms_PCCreate_Schurras(self); - else - { - printf("ERROR: Invalid choice of preconditioner - (Check PCTYPE)! \n"); - PARMS_ABORT(15); - } - self->istypeset = true; - self->issetup = false; - } - return 0; -} - -/** - * Set local preconditioner type. - * - * Supported ILU preconditioners: - * - * \f{tabular}{ll} - * PCILU0 & ILU0 \\ - * PCILUK & ILUK \\ - * PCILUT & ILUT in SPARSKIT \\ - * PCARMS & ARMS implemented by Yousef Saad - * \f} - - * @param self A preconditioner object. - * @param pcstype The type of local preconditioner: - * - PCILU0 - * - PCILUK - * - PCILUT - * - PCARMS - * - * @return 0 on success. - */ -int parms_PCSetILUType(parms_PC self, PCILUTYPE pcilutype) -{ - - if (self->isiluset && self->pcilutype == pcilutype) { - return 0; - } - if (self->issetup) { - self->ops->pc_free(&self); - } - self->pcilutype = pcilutype; - self->A->ilutype = pcilutype; - self->isiluset = true; - self->issetup = false; - return 0; -} - -/* Perform ILU factorization for the local preconditioner*/ -int parms_PCILU(parms_PC self, parms_FactParam param, void *mat, - parms_Operator *op) -{ - int type = self->pcilutype; - - if(type == PCARMS) - parms_arms_vcsr(self->A, param, mat, op); - else - if(*op == NULL){ - if(type == PCILUT) - parms_ilut_vcsr(self->A, param, mat, op); - else if(type == PCILUK) - parms_iluk_vcsr(self->A, param, mat, op); - else if(type == PCILU0) - parms_ilu0_vcsr(self->A, param, mat, op); - else - { - printf("ERROR: Invalid choice of local preconditioner - (check pcilutype for parms_PCSetILUType(...))\n"); - PARMS_ABORT(16); - } - } - else - parms_ilu_update(self->A, param, mat, op); - - return 0; -} - -/** - * Set parameters for the preconditioner object. - * - * Supported parameters: - * - tol drop tolerance - * - fil fill-in - * - nlev number of levels - * - bsize block size for finding independent sets in ARMS. - * - tolind drop tolerance for finding independent sets. - * - iksize the restart size for the inner GMRES. - * - imax the number of iterations for the inner GMRES. - * - * @param self A preconditioner object. - * @param nflags The number of parameters. - * @param params A pointer to parameters. - * - * @return 0 on success. - */ -int parms_PCSetParams(parms_PC self, int nflags, char **params) -{ - int i, j, k; - - for (i = 0, j = 0; i < nflags; i++) { - if (!strcmp(params[j], "fill")) { - for (k = 0; k < 7; k++) { - self->param->lfil[k] = atoi(params[++j]); - } - } - else if (!strcmp(params[j], "tol")) { - for (k = 0; k < 6; k++) { - self->param->droptol[k] = atof(params[++j]); - } - } - else if (!strcmp(params[j], "nlev")) { - self->param->ipar[0] = atoi(params[++j]); - } - else if (!strcmp(params[j], "bsize")) { - self->param->ipar[2] = atoi(params[++j]); - } - else if (!strcmp(params[j], "iksize")) { - self->param->ipar[4] = atoi(params[++j]); - } - else if (!strcmp(params[j], "imax")) { - self->param->ipar[5] = atoi(params[++j]); - } - else if (!strcmp(params[j], "tolind")) { - self->param->tolind = atof(params[++j]); - } - } - return 0; -} - -/** - * Set fill-in parameter for ILUT and ARMS. - * - * @param self A preconditioner object. - * @param fill A int array of size 7. - * - fill[0] amount of fill-in kept in \f$L_{B}\f$. - * - fill[1] amount of fill-in kept in \f$U_{B}\f$. - * - fill[2] amount of fill-in kept in \f$E L^{-1}\f$. - * - fill[3] amount of fill-in kept in \f$U^{-1}_{B} F\f$. - * - fill[4] amount of fill-in kept in \f$S\f$. - * - fill[5] amount of fill-in kept in \f$L_S\f$. - * - fill[6] amount of fill-in kept in \f$U_S\f$. - * - * @return 0 on success. - */ -int parms_PCSetFill(parms_PC self, int *fill) -{ - int i; - - for (i = 0; i < 7; i++) { - self->param->lfil[i] = fill[i]; - } - return 0; -} - -/** - * Set the number of levels for ILUK and ARMS. - * - * @param self A preconditioner object. - * @param nlevel The number of levels. - * - * @return 0 on success. - */ -int parms_PCSetNlevels(parms_PC self, int nlev) -{ - self->param->ipar[0] = nlev; - return 0; -} - -/** - * Set the type of permutation in ARMS - * - * @param self A preconditioner object. - * @param type Permutation type. - * - 1 non-symmetric permutaion. - * - 0 symmetric permutation. - * - * @return 0 on success. - */ -int parms_PCSetPermType(parms_PC self, int type) -{ - self->param->ipar[1] = type; - return 0; -} - -/** - * Set the block size for ARMS. - * - * @param self A preconditioner object. - * @param bsize The block size for ARMS. - * - * @return 0 on success. - */ -int parms_PCSetBsize(parms_PC self, int bsize) -{ - self->param->ipar[2] = bsize; - return 0; -} - -/** - * Set the drop tolerance for ILUT preconditioner. - * - * @param self A preconditioner object. - * @param tol A double array of size 7. - * - tol[0] threshold for dropping in L_{B}. - * - tol[1] threshold for dropping in U_{B}. - * - tol[2] threshold for dropping in L^{-1} F - * - tol[3] threshold for dropping in E U^{-1} - * - tol[4] threshold for dropping in Schur complement - * - tol[5] threshold for dropping in L in last block - * - tol[6] threshold for dropping in U in last block - * - * @return 0 on success. - */ -int parms_PCSetTol(parms_PC self, double *dt) -{ - PARMS_MEMCPY(self->param->droptol, dt, 7); - return 0; -} - -/** - * Set the restart size for the inner GMRES. - * - * @param self A preconditioner object. - * @param im The restart size of the inner GMRES. - * - * @return 0 on success. - */ -int parms_PCSetInnerKSize(parms_PC self, int im) -{ - self->param->ipar[4] = im; - return 0; -} - -/** - * Set the maximum iteration counts for the inner GMRES. - * - * @param self A preconditioner object. - * @param imax The maximum iteration counts. - * - * @return 0 on success. - */ -int parms_PCSetInnerMaxits(parms_PC self, int imaxits) -{ - self->param->ipar[5] = imaxits; - return 0; -} - -/** - * Set the convergence tolerance for the inner GMRES. - * - * @param self A preconditioner object. - * @param eps The convergence tolerance. - * - * @return 0 on success. - */ -int parms_PCSetInnerEps(parms_PC self, REAL ieps) -{ - self->param->pgfpar[0] = ieps; - self->param->pgfpar[1] = ieps; - return 0; -} - -/** - * Set the tolerance for finding independent sets. - * - * @param self A preconditioner object. - * @param tolind The drop tolerance for finding independent sets. - * - * @return 0 on success. - */ -int parms_PCSetTolInd(parms_PC self, REAL tolind) -{ - self->param->tolind = tolind; - return 0; -} - -/** - * Set permutation and scaling options for interlevel blocks - * - * @param self A preconditioner object. - * @param meth Options: - * -meth[0] nonsummetric permutations of 1: yes. affects rperm - * USED FOR LAST SCHUR COMPLEMENT - * -meth[1] permutations of columns 0:no 1: yes. So far this is - * USED ONLY FOR LAST BLOCK [ILUTP instead of ILUT]. - * (so ipar[11] does no matter - enter zero). If - * ipar[15] is one then ILUTP will be used instead - * of ILUT. Permutation data stored in: perm2. - * -meth[2] diag. row scaling. 0:no 1:yes. Data: D1 - * -meth[3] diag. column scaling. 0:no 1:yes. Data: D2 - * @param flag - * -1 interlevel block. - * -0 last block. - * - * @return 0 on success. - */ -int parms_PCSetPermScalOptions(parms_PC self, int *meth, int flag) -{ - if (flag) { - PARMS_MEMCPY(&self->param->ipar[10], meth, 4); - } - else { - PARMS_MEMCPY(&self->param->ipar[14], meth, 4); - } - - return 0; -} - -/** - * Get the ratio of the number of nonzero entries of the - * preconditioning matrix to that of the original matrix. - * - * @param self A preconditioner. - * @param ratio A pointer to the ratio. - * - * @return 0 on success. - */ -int parms_PCGetRatio(parms_PC self, double *ratio) -{ - return self->ops->getratio(self, ratio); -} - -/** - * Return the name of a preconditioner. - * - * @param self A preconditioner. - * @param name The name of preconditioner. - * - * @return 0 on success. - */ -int parms_PCGetName(parms_PC self, char **name) -{ - if(self->pctype == PCBJ) - *name = "Block Jacobi"; - else if(self->pctype == PCSCHUR) - *name = "Schur Complement based Preconditioner"; - else if(self->pctype == PCRAS) - *name = "Restricted Additive Schwarz"; - else if(self->pctype == PCSCHURRAS) - *name = "Schur Complement + Restricted Additive Schwarz"; - else - *name = "Unknown Preconditioner"; - return 0; -} - -/** - * Return the name of a local preconditioner. - * - * @param self A preconditioner. - * @param iluname The name of local ILU preconditioner. - * - * @return 0 on success. - */ -int parms_PCILUGetName(parms_PC self, char **iluname) -{ - if(self->pcilutype == PCILU0) - *iluname = "ILU0"; - else if(self->pcilutype == PCILUK) - *iluname = "ILUK"; - else if(self->pcilutype == PCILUT) - *iluname = "ILUT"; - else if(self->pcilutype == PCARMS) - *iluname = "ARMS"; - else - *iluname = "Unknown Local Preconditioner"; - - return 0; -} diff --git a/lib/parms/src/parms_pc_bj.c b/lib/parms/src/parms_pc_bj.c deleted file mode 100755 index 4f756dcc1..000000000 --- a/lib/parms/src/parms_pc_bj.c +++ /dev/null @@ -1,143 +0,0 @@ -#include "./include/parms_pc_impl.h" -#include "./include/parms_opt_impl.h" - -typedef struct bj_data { - parms_Operator op; - BOOL issetup; -} *bj_data; - -/** Free the memory for struct bj_data. - * - * \param self A preconditioner object. - * \return 0 on success. - */ -static int pc_bj_free(parms_PC *self) -{ - bj_data pc_data; - parms_Operator op; - - pc_data = (bj_data)(*self)->data; - op = pc_data->op; - parms_OperatorFree(&op); - PARMS_FREE(pc_data); - (*self)->param->isalloc = false; - return 0; -} - -/** Dump BJ preconditioner. - * - * \param self A preconditioner object. - * \param v A viewer object. - * \return 0 on success. - */ -static int pc_bj_view(parms_PC self, parms_Viewer v) -{ - bj_data pc_data; - - pc_data = (bj_data)self->data; - parms_OperatorView(pc_data->op, v); - return 0; -} - -/** Set up block Jacobi preconditioner. - * - * \param self A preconditioner object. - * \return 0 on success. - */ -static int pc_bj_setup(parms_PC self) -{ - bj_data pc_data; - parms_Mat A; - void *diag_mat; - parms_FactParam param; - parms_Operator op; - - A = self->A; - param = self->param; - pc_data = (bj_data)self->data; - if(pc_data->issetup) - op = pc_data->op; - else{ - param->start = 0; - param->n = A->is->lsize; - param->schur_start = -1; - pc_data->issetup = true; - op = NULL; - } - - /* reuse lu-factorization (AF) */ - parms_MatGetDiag(A, &diag_mat); - parms_PCILU(self, param, diag_mat, &op); - - - pc_data->op = op; - - return 0; -} - -/** Apply preconditioner BJ to the vector y. - * - * \x = self^{-1}y. - * - * \param self A preconditioner object. - * \param y A right-hand-side vector. - * \param x Solution vector. - * \return 0 - */ -static int pc_bj_apply(parms_PC self, FLOAT *y, FLOAT *x) -{ - bj_data pc_data; - parms_Operator op; - - pc_data = (bj_data)self->data; - op = pc_data->op; - parms_OperatorApply(op, y, x); - return 0; -} - -/** Get the ratio of the number of nonzero entries of the - * preconditioning matrix to that of the original matrix. - * - * \param self A preconditioner. - * \param ratio A pointer to the ratio. - * \return 0 on success. - */ -static int pc_bj_getratio(parms_PC self, double *ratio) -{ - bj_data pc_data; - parms_Operator op; - int nnz_mat, nnz_pc; - int gnnz_mat, gnnz_pc; - - pc_data = (bj_data)self->data; - op = pc_data->op; - parms_OperatorGetNnz(op, &nnz_mat, &nnz_pc); - MPI_Allreduce(&nnz_mat, &gnnz_mat, 1, MPI_INT, MPI_SUM, - MPI_COMM_WORLD); - MPI_Allreduce(&nnz_pc, &gnnz_pc, 1, MPI_INT, MPI_SUM, - MPI_COMM_WORLD); - *ratio = (double)gnnz_pc/(double)gnnz_mat; - return 0; -} - -/** Create a block Jacobi preconditioner. - * - * \param self A preconditioner object. - * \return 0 on success - */ -int parms_PCCreate_BJ(parms_PC self) -{ - bj_data data; - - PARMS_NEW(data); - data->issetup = false; - - self->data = data; - self->ops->pc_free = pc_bj_free; - self->ops->pc_view = pc_bj_view; - self->ops->apply = pc_bj_apply; - self->ops->setup = pc_bj_setup; - self->ops->getratio = pc_bj_getratio; - return 0; -} - diff --git a/lib/parms/src/parms_pc_ras.c b/lib/parms/src/parms_pc_ras.c deleted file mode 100755 index 0d89136bd..000000000 --- a/lib/parms/src/parms_pc_ras.c +++ /dev/null @@ -1,226 +0,0 @@ -#include "./include/parms_comm_impl.h" -#include "./include/parms_pc_impl.h" -#include "./include/parms_opt_impl.h" - -typedef struct ras_data { - parms_Operator op; - parms_Comm handler; - parms_Map is; - BOOL issetup; - FLOAT *rbuf; - int nloc; - int n; - int nodv; - int nsend; -} *ras_data; - -/** Free the memory for struct ras_data. - * - * \param self A preconditioner object. - * \return 0 on success. - */ -static int pc_ras_free(parms_PC *self) -{ - ras_data pc_data; - parms_Operator op; - - pc_data = (ras_data)(*self)->data; - op = pc_data->op; - parms_OperatorFree(&op); - // parms_MapFree(&pc_data->is); - PARMS_FREE(pc_data); - (*self)->param->isalloc = false; - return 0; -} - -/** Dump RAS preconditioner. - * - * \param self A preconditioner object. - * \param v A viewer object. - * \return 0 on success. - */ -static int pc_ras_view(parms_PC self, parms_Viewer v) -{ - ras_data pc_data; - - pc_data = (ras_data)self->data; - parms_OperatorView(pc_data->op, v); - return 0; -} - -/** Set up RAS preconditioner. - * - * \param self A preconditioner object. - * \return 0 on success. - */ -static int pc_ras_setup(parms_PC self) -{ - ras_data pc_data; - parms_Mat A; - parms_Comm handler; - parms_FactParam param; - parms_Operator op; - void *lmat, *mat_ext; - - /* get the matrix */ - A = self->A; - /* get communication handler */ - pc_data = (ras_data)self->data; - - /* set parameters used for ILU factorization */ - param = self->param; - if(pc_data->issetup) { - handler = pc_data->handler; - op = pc_data->op; - } - else{ - parms_MatGetCommHandler(A, &handler); - pc_data->handler = handler; - - pc_data->nloc = A->is->lsize; - pc_data->nodv = parms_CommGetNumRecv(handler); - pc_data->nsend = parms_CommGetNumSend(handler); - parms_CommGetRecvBuf(handler, &pc_data->rbuf); - op = NULL; - - param->start = 0; - if (param->ipar[4] != 0) { - param->ipar[4] = 0; - } - if (param->ipar[5] != 0) { - param->ipar[5] = 0; - } - - pc_data->issetup = true; - } - - /* get the local matrix */ - parms_MatGetSubMat(A, &lmat); - - /* extend the local matrix by including the equations correspond to - the immediate neighbouring variables */ - parms_MatExtend(A, handler, 0, lmat, &pc_data->n, &mat_ext); - - // parms_MapCreateFromLocal(&pc_data->is, pc_data->n, 0); - - param->n = pc_data->n; - param->schur_start = param->n; - - /* ILU factorization */ - parms_PCILU(self, param, mat_ext, &op); - - /* free the memory for the mat_ext */ - if (self->pcilutype != PCARMS) { - parms_MatFreeSubMat(A, mat_ext); - } - - - pc_data->op = op; - return 0; -} - -/** Apply RAS to vector y. - * - * \f$x = self^{-1}y\f$. - * - * \param self A RAS preconditioner. - * \param y A right-hand-side vector. - * \param x Solution vector. - * \return 0 on success. - */ -static int pc_ras_apply(parms_PC self, FLOAT *y, FLOAT *x) -{ - ras_data pc_data; - parms_Operator op; - parms_Comm handler; - FLOAT *rbuf; - FLOAT *x_ext; - FLOAT *y_ext; - int nodv, nsend, n, nloc; - - pc_data = (ras_data)self->data; - op = pc_data->op; - handler = pc_data->handler; - rbuf = pc_data->rbuf; - nsend = pc_data->nsend; - nodv = pc_data->nodv; - n = pc_data->n; - nloc = pc_data->nloc; - - y_ext = (FLOAT *)malloc((n+1)*sizeof(FLOAT)); - x_ext = (FLOAT *)malloc((n+1)*sizeof(FLOAT)); - - - /* exchange interface variables */ - if (nsend) { - parms_CommDataBegin(handler, y, 0); - } - /* copy local variables to the extended vector */ - PARMS_MEMCPY(y_ext, y, pc_data->nloc); - - if (nodv) { - parms_CommDataEnd(handler); - } - /* copy received external interface variables to the extended - vector */ - if (pc_data->n-pc_data->nloc) { - PARMS_MEMCPY(&y_ext[nloc], rbuf, pc_data->n-pc_data->nloc); - } - - /* solve the extended linear system */ - parms_OperatorApply(op, y_ext, x_ext); - - /* ignore external variables -RAS */ - - PARMS_MEMCPY(x, x_ext, nloc); - free(x_ext); - free(y_ext); - - return 0; -} -/** Get the ratio of the number of nonzero entries of the - * preconditioning matrix to that of the original matrix. - * - * \param self A preconditioner. - * \param ratio A pointer to the ratio. - * \return 0 on success. - */ -static int pc_ras_getratio(parms_PC self, double *ratio) -{ - ras_data pc_data; - parms_Operator op; - int nnz_mat, nnz_pc; - int gnnz_mat, gnnz_pc; - - pc_data = (ras_data)self->data; - op = pc_data->op; - parms_OperatorGetNnz(op, &nnz_mat, &nnz_pc); - MPI_Allreduce(&nnz_mat, &gnnz_mat, 1, MPI_INT, MPI_SUM, - MPI_COMM_WORLD); - MPI_Allreduce(&nnz_pc, &gnnz_pc, 1, MPI_INT, MPI_SUM, - MPI_COMM_WORLD); - *ratio = (double)gnnz_pc/(double)gnnz_mat; - return 0; -} - - -/** Create a RAS preconditioner. - * - * \param self A preconditioner object. - * \return 0 on success. - */ -int parms_PCCreate_RAS(parms_PC self) -{ - ras_data data; - - PARMS_NEW(data); - data->issetup = false; - - self->data = data; - self->ops->pc_free = pc_ras_free; - self->ops->pc_view = pc_ras_view; - self->ops->apply = pc_ras_apply; - self->ops->setup = pc_ras_setup; - self->ops->getratio = pc_ras_getratio; - return 0; -} diff --git a/lib/parms/src/parms_pc_schur.c b/lib/parms/src/parms_pc_schur.c deleted file mode 100755 index ee1cb2b44..000000000 --- a/lib/parms/src/parms_pc_schur.c +++ /dev/null @@ -1,552 +0,0 @@ -#include "./include/parms_pc_impl.h" -#include "./include/parms_opt_impl.h" -#if defined(__ICC) -#include -#else -#if defined(C99) -#include -#else -#include -#endif -#endif - -typedef struct schur_data { - parms_Operator op; - int n, schur_start, nrow; - int im, maxits, in_iters; - MPI_Comm comm; - BOOL issetup; - double pgfpar[2]; - FLOAT **vv, **hh, *s, *rs, *hcol, *z2, *wk; - REAL *c; -} *schur_data; - -/** Free the memory for struct schur_data. - * - * \param self A preconditioner object. - * \return 0 on success. - */ -static int pc_schur_free(parms_PC *self) -{ - schur_data pc_data; - parms_Operator op; - int i; - - pc_data = (schur_data)(*self)->data; - op = pc_data->op; - parms_OperatorFree(&op); - - if (pc_data->nrow) { - PARMS_FREE(pc_data->z2); - PARMS_FREE(pc_data->wk); - for (i = 0; i < pc_data->im+1; i++) { - PARMS_FREE(pc_data->vv[i]); - } - } - PARMS_FREE(pc_data->vv); - for (i = 0; i < pc_data->im; i++) { - PARMS_FREE(pc_data->hh[i]); - } - PARMS_FREE(pc_data->hh); - PARMS_FREE(pc_data->c); - PARMS_FREE(pc_data->s); - PARMS_FREE(pc_data->rs); - PARMS_FREE(pc_data->hcol); - MPI_Comm_free(&pc_data->comm); - PARMS_FREE(pc_data); - (*self)->param->isalloc = false; - return 0; -} - -/** Dump the Schur preconditioner. - * - * \param self A preconditioner object. - * \param v A viewer. - * \return 0 on success. - */ -static int pc_schur_view(parms_PC self, parms_Viewer v) -{ - schur_data pc_data; - - pc_data = (schur_data)self->data; - parms_OperatorView(pc_data->op, v); - return 0; -} - -/** Set up data for the Schur preconditioner. - * - * Malloc memories for working arrays in the inner GMRES. - * - * \param self A preconditioner object. - * \return 0 on success. - */ -static int pc_schur_setup(parms_PC self) -{ - schur_data pc_data; - parms_Mat A; - void *diag_mat; - parms_FactParam param; - parms_Operator op; - int nrow, i; - - A = self->A; - - /* Set parameters for the ILU factorization: - ILUs in pARMS can perform factorizatoin based on the matrix - merged implicitly by several submatrices. - start - the location in the merged matrix of the first row - index of the submatrix. - n - the size of the merged matrix. - schur_start - the beginning row index of the Schur in the merged - matrix - */ - param = self->param; - param->start = 0; - param->n = A->is->lsize; - param->schur_start = -1; - - pc_data = (schur_data)self->data; - - if(pc_data->issetup) - op = pc_data->op; - else{ - op = NULL; - } - - /* Get diagonal part of the matrix */ - parms_MatGetDiag(A, &diag_mat); - - /* Perform local ILU-type factorization */ - parms_PCILU(self, param, diag_mat, &op); - - if(!pc_data->issetup){ - - pc_data->op = op; - MPI_Comm_dup(A->is->comm, &pc_data->comm); - - /* get Krylov subspace size */ - pc_data->im = param->ipar[4]; - if (pc_data->im == 0) { - pc_data->im = 5; - } - - /* maximum inner iteration counts */ - pc_data->maxits = param->ipar[5]; - if (pc_data->maxits == 0) { - pc_data->maxits = 5; - } - - /* - malloc memory for krylov subspace basis vv and Hessenberg matrix - */ - PARMS_NEWARRAY(pc_data->vv, pc_data->im+1); - - /* Set the size of the matrix */ - pc_data->n = param->n; - - /* Set the beginning location of the Schur complement */ - pc_data->schur_start = parms_OperatorGetSchurPos(op); -/* pc_data->schur_start = A->is->schur_start; */ - - /* The size of local Schur complement */ - nrow = pc_data->n - pc_data->schur_start; - pc_data->nrow = nrow; - - /* Allocate memories for auxiliary arrays. */ - if(nrow) { - for(i = 0; i < pc_data->im+1; i++) { - PARMS_NEWARRAY(pc_data->vv[i], nrow); - } - PARMS_NEWARRAY(pc_data->z2, nrow); - PARMS_NEWARRAY(pc_data->wk, nrow); - } - else { - pc_data->z2 = NULL; - pc_data->wk = NULL; - pc_data->vv[0] = NULL; - } - - PARMS_NEWARRAY(pc_data->hh, pc_data->im); - for(i = 0; i < pc_data->im; i++) { - PARMS_NEWARRAY(pc_data->hh[i], pc_data->im+1); - } - - PARMS_NEWARRAY(pc_data->c, pc_data->im); - PARMS_NEWARRAY(pc_data->s, pc_data->im); - PARMS_NEWARRAY(pc_data->rs, pc_data->im+1); - PARMS_NEWARRAY(pc_data->hcol, pc_data->im+1); - - pc_data->issetup = true; - - } - return 0; -} - -/** Apply the preconditioner to the vector y. - * - * This preconditioner is actually the lsch_xx preconditioners in old - * version of pARMS. - * - * \param self A preconditioner object. - * \param y A right-hand-side vector. - * \param x Solution vector. - * \return 0 on success. - */ -static int pc_schur_apply(parms_PC self, FLOAT *y, FLOAT *x) -{ - /*-------------------------------------------------------------------- - APPROXIMATE LU-SCHUR LEFT PRECONDITONER - *-------------------------------------------------------------------- - Schur complement left preconditioner. This is a preconditioner for - the global linear system which is based on solving approximately the - Schur complement system. More precisely, an approximation to the - local Schur complement is obtained (implicitly) in the form of an - LU factorization from the LU factorization L_i U_i of the local - matrix A_i. Solving with this approximation amounts to simply doing - the forward and backward solves with the bottom part of L_i and U_i - only (corresponding to the interface variables). This is done using - a special version of the subroutine lusol0 called lusol0_p. Then it - is possible to solve for an approximate Schur complement system - using GMRES on the global approximate Schur complement system - (which is preconditionied by the diagonal blocks represented by - these restricted LU matrices). - -------------------------------------------------------------------- - Coded by Y. Saad - Aug. 1997 - updated Nov. 14, 1997. - C version coded by Zhongze Li, Jan. 2001, updated Sept, 17th, 2001 - Revised by Zhongze Li, June 1st, 2006. - -------------------------------------------------------------------- - */ - schur_data pc_data; - parms_Operator op; - parms_Map is; - parms_Mat A; - parms_FactParam param; - MPI_Comm comm; - int schur_start,nrow, im,i,jj,i1,k,k1,its,j,ii,maxits,incx=1,ierr; - int if_in_continue,if_out_continue; - FLOAT *z2, *wk; - FLOAT **vv, **hh, *s, *rs, *hcol, t1, alpha; - REAL *c; - REAL eps,eps1,t,ro,tloc; - -#if defined(DBL_CMPLX) - FLOAT rot; -#else - REAL gam; -#endif - static int iters = 0; - - /* retrieve the specific data structure for Schur based preconditioners */ - pc_data = (schur_data)self->data; - - /* get the matrix A */ - A = self->A; - - is = A->is; - - /* get the dimension of the local matrix */ - /* n = pc_data->n; */ - - /* get the beginning location of the Schur complement */ - schur_start = pc_data->schur_start; - - /* get the size of the Schur complement */ - nrow = pc_data->nrow; - - /* get krylov space size */ - im = pc_data->im; - - /* get maximal iteration number */ - maxits = pc_data->maxits; - - /* get tolerance */ - param = self->param; - eps = param->pgfpar[0]; - eps1 = eps; - - /* get communicator */ - comm = pc_data->comm; - - /* get working arrays */ - vv = pc_data->vv; - hh = pc_data->hh; - c = pc_data->c; - s = pc_data->s; - rs = pc_data->rs; - hcol = pc_data->hcol; - z2 = pc_data->z2; - wk = pc_data->wk; - - /* retrieve the operator */ - op = pc_data->op; - /* compute the right-hand side of Schur Complement System - it turns out that this is the bottom part of inv(A_i)*rhs - */ - - parms_OperatorLsol(op, y, x); - - /* GCOPY(nrow, &x[schur_start], incx, wk, incx); */ - for (i = 0; i EPSILON) { - t1 = 1.0/t; - /* GSCAL(nrow, t1, vv[i1], incx); */ - for (i = 0; i < nrow; i++) vv[i1][i] *= t1; - } - /* - done with modified gram Schmidt and Arnoldi step.. - now update factorization of hh - */ - if(i != 0) { - /* perform previous transformations on i-th column of h */ -#if defined(DBL_CMPLX) - for(k = 1; k <= i; k++) { - k1 = k-1; - t1 = hh[i][k1]; - hh[i][k1] = c[k1]*t1 + s[k1]*hh[i][k]; - hh[i][k] = -conj(s[k1])*t1 + c[k1]*hh[i][k]; - } -#else - for(k = 1; k <= i; k++) { - k1 = k-1; - t1 = hh[i][k1]; - hh[i][k1] = c[k1]*t1 + s[k1]*hh[i][k]; - hh[i][k] = -s[k1]*t1 + c[k1]*hh[i][k]; - } -#endif - } - -#if defined(DBL_CMPLX) -/*-----------get next plane rotation------------ */ - zclartg(hh[i][i], hh[i][i1], &c[i], &s[i], &rot); - rs[i1] = -conj(s[i])*rs[i]; - rs[i] = c[i]*rs[i]; - hh[i][i] = rot; - ro = cabs(rs[i1]); -#else - gam = sqrt(hh[i][i]*hh[i][i] + hh[i][i1]*hh[i][i1]); - /* - if gamma is zero then any small value will do ... - will affect only residual estimate - */ - if(fabs(gam-ZERO) < EPSILON) gam = EPSMAC; - /* get next plane rotation */ - c[i] = hh[i][i] / gam; - s[i] = hh[i][i1] / gam; - rs[i1] = -s[i]*rs[i]; - rs[i] = c[i]*rs[i]; - - /* determine residual norm and test for convergence */ - hh[i][i] = c[i]*hh[i][i] + s[i]*hh[i][i1]; - ro = fabs(rs[i1]); -#endif - - if(i+1 >= im || ro <= eps1 || its >= maxits) - if_in_continue = 0; - } - - /* now compute solution. first solve upper triangular system. */ - for(ii = i; ii >= 0; --ii) { - t1 = rs[ii]; - for(j = ii+1; j <= i; j++) { - t1 = t1 - hh[j][ii]*rs[j]; - } - rs[ii] = t1 / hh[ii][ii]; - } - - /* form linear combination of v(*,i)'s to update solution */ - for(j = 0; j < i+1; j++) { - for (i=0; i= maxits) { - ierr = 1; - if_out_continue = 0; - } - /* else compute residual vector (from the v's) and continue.. */ - else { -#if defined(DBL_CMPLX) - for(j = 0; j <= i; j++) { - jj= i1-j+1; - rs[jj-1] = -conj(s[jj-1])*rs[jj]; - rs[jj] = c[jj-1]*rs[jj]; - } -#else - for(j = 0; j <= i; j++) { - jj= i1-j+1; - rs[jj-1] = -s[jj-1]*rs[jj]; - rs[jj] = c[jj-1]*rs[jj]; - } -#endif - for(j = 0; j <= i1; j++) { - t1 = rs[j]; - if(j == 0) t1 = t1-1.0; - /* GAXPY(nrow, t1, vv[j], incx, vv[0], incx); */ - for (i = 0; i < nrow; i++) vv[0][i] += t1 * vv[j][i]; - } - } - } - /* - post-processing to compute interior variables.. - compute RHS for last system. similar to Schur iteration - first get a vector of the form (0,y)^T - */ - parms_MatVecOffDiag(A, &x[schur_start], z2, schur_start); - - /* GCOPY(nrow, wk, incx, &x[schur_start], incx); */ - for (i = 0; i < nrow; i++) x[schur_start+i] = wk[i]; - for (i = 0; i < is->ninf; i++) { - x[i+is->nint] -= z2[i+is->nint-schur_start]; - } - /* solve the Schur complement system */ - parms_OperatorInvS(op, &x[schur_start], &x[schur_start]); - /* backward sweep */ - parms_OperatorAscend(op, x, x); - - iters += its; - pc_data->in_iters = iters; - - return ierr; -} - -/** Get the ratio of the number of nonzero entries of the - * preconditioning matrix to that of the original matrix. - * - * \param self A preconditioner. - * \param ratio A pointer to the ratio. - * \return 0 on success. - */ -static int pc_schur_getratio(parms_PC self, double *ratio) -{ - schur_data pc_data; - parms_Operator op; - int nnz_mat, nnz_pc; - int gnnz_mat, gnnz_pc; - - pc_data = (schur_data)self->data; - op = pc_data->op; - parms_OperatorGetNnz(op, &nnz_mat, &nnz_pc); - MPI_Allreduce(&nnz_mat, &gnnz_mat, 1, MPI_INT, MPI_SUM, - MPI_COMM_WORLD); - MPI_Allreduce(&nnz_pc, &gnnz_pc, 1, MPI_INT, MPI_SUM, - MPI_COMM_WORLD); - *ratio = (double)gnnz_pc/(double)gnnz_mat; - return 0; -} - -/** Create a Schur-complement based preconditioner. - * - * \param self A preconditioner object. - * \return 0 on success. - */ -int parms_PCCreate_Schur(parms_PC self) -{ - schur_data data; - - PARMS_NEW(data); - self->data = data; - data->issetup = false; - self->ops->pc_free = pc_schur_free; - self->ops->pc_view = pc_schur_view; - self->ops->apply = pc_schur_apply; - self->ops->setup = pc_schur_setup; - self->ops->getratio = pc_schur_getratio; - return 0; -} diff --git a/lib/parms/src/parms_pc_schurras.c b/lib/parms/src/parms_pc_schurras.c deleted file mode 100755 index 505dfc1c2..000000000 --- a/lib/parms/src/parms_pc_schurras.c +++ /dev/null @@ -1,469 +0,0 @@ -#include "./include/parms_pc_impl.h" -#include "./include/parms_opt_impl.h" -#if defined(__ICC) -#include -#else -#if defined(C99) -#include -#else -#include -#endif -#endif - -/* Forward declarations */ -int parms_OperatorGetU(parms_Operator self, void **mat); -int parms_MatGetOffDiag(parms_Mat self, void **mat); -int parms_CommGetOdvlist(parms_Comm self, int **odvlist); - -typedef struct schurras_data { - - parms_Operator op_out,op_in; - int n, schur_start, nrow, nloc, n_ext, nodv, nsend; - FLOAT *x_ext, *y_ext; - BOOL issetup; - parms_Comm handler; - parms_Mat S; - FLOAT *rbuf; - -} *schurras_data; - -static int parms_PC_GetS(parms_PC self, parms_Operator op,parms_Mat *mat); -static int parms_PC_GetExtendSchur(parms_PC self, parms_Operator op,void **mat); -/** Free the memory for struct schur_data. - * - * \param self A preconditioner object. - * \return 0 on success. - */ -static int pc_schurras_free(parms_PC *self) -{ - schurras_data pc_data; - parms_Operator op; - int i; - - pc_data = (schurras_data)(*self)->data; - op = pc_data->op_out; - parms_OperatorFree(&op); - op = pc_data->op_in; - parms_OperatorFree(&op); - parms_MatFree(&pc_data->S); - - if (pc_data->n_ext) { - PARMS_FREE(pc_data->x_ext); - PARMS_FREE(pc_data->y_ext); - } - PARMS_FREE(pc_data); - (*self)->param->isalloc = false; - return 0; -} - -/** Dump the Schur preconditioner. - * - * \param self A preconditioner object. - * \param v A viewer. - * \return 0 on success. - */ -static int pc_schurras_view(parms_PC self, parms_Viewer v) -{ - schurras_data pc_data; - - pc_data = (schurras_data)self->data; - // parms_OperatorView(pc_data->op_out, v); - parms_OperatorView(pc_data->op_in, v); - return 0; -} - -/** Set up data for the Schur preconditioner. - * - * Malloc memories for working arrays in the inner GMRES. - * - * \param self A preconditioner object. - * \return 0 on success. - */ -static int pc_schurras_setup(parms_PC self) -{ - schurras_data pc_data; - parms_Mat A,S; - parms_PC newpc; - parms_Viewer v; - void *diag_mat,*schur_mat,*mat_ext; - parms_FactParam param; - parms_Operator op,opras; - parms_Comm handler; - parms_Operator newOpt; - int nrow, i,n_ext,ipar[2]; - - - A = self->A; - pc_data = (schurras_data)self->data; - - /* Schur part I */ - - /* Perform restricted Gauss Elimination*/ - param = self->param; - - param->start = 0; - param->n = A->is->lsize; //n = size of the Submatrix - param->schur_start = A->is->schur_start; - param->isalloc = false; - - parms_MatGetDiag(A, &diag_mat); - op = NULL; - parms_PCILU(self, param, diag_mat, &op); - pc_data->op_out = op; - /* Perform global Schur matrix */ - parms_PC_GetS(self,pc_data->op_out,&S); - - /* rAS-part of the preconditioner */ - - /* get communicator of global Schur matrix */ - parms_MatGetCommHandler(S,&handler); - - /* get Si' = extended local part of global Schur matrix */ - parms_MatGetSubMat(S,&schur_mat); - parms_MatExtend(S,handler,0,schur_mat,&n_ext,&mat_ext); - - pc_data->handler = handler; - pc_data->nodv = parms_CommGetNumRecv(handler); - pc_data->nsend = parms_CommGetNumSend(handler); - parms_CommGetRecvBuf(handler,&pc_data->rbuf); - - /* perform ILU-factorization of Si' */ - - param->n = n_ext; - param->schur_start = param->n; - param->isalloc = false; - - PARMS_NEW(newpc); - newpc->A = S; - newpc->pcilutype = self->pcilutype; - op = NULL; - parms_PCILU(newpc,param,mat_ext,&op); - pc_data->op_in = op; - PARMS_FREE(newpc); - - /* free the memory for the mat_ext */ - if (self->pcilutype != PCARMS) { - parms_MatFreeSubMat(S, mat_ext); - } - - /* set pc_data */ - pc_data->S = S; - pc_data->n_ext = n_ext; - pc_data->nrow = nrow = S->is->lsize; - pc_data->schur_start = parms_OperatorGetSchurPos(pc_data->op_out); - pc_data->nloc = A->is->lsize; - pc_data->n = pc_data->nloc + pc_data->nodv; - - if(n_ext){ - PARMS_NEWARRAY(pc_data->x_ext, n_ext); - PARMS_NEWARRAY(pc_data->y_ext, n_ext); - } - pc_data->issetup = true; - return 0; -} - -/** Apply the preconditioner to the vector y. - * - * This preconditioner is actually the lsch_xx preconditioners in old - * version of pARMS. - * - * \param self A preconditioner object. - * \param y A right-hand-side vector. - * \param x Solution vector. - * \return 0 on success. - */ -static int pc_schurras_apply(parms_PC self, FLOAT *y, FLOAT *x) -{ - /*-------------------------------------------------------------------- - APPROXIMATE LU-SCHUR LEFT PRECONDITONER - *-------------------------------------------------------------------- - Schur complement left preconditioner. This is a preconditioner for - the global linear system which is based on solving approximately the - Schur complement system. More precisely, an approximation to the - local Schur complement is obtained (implicitly) in the form of an - LU factorization from the LU factorization L_i U_i of the local - matrix A_i. Solving with this approximation amounts to simply doing - the forward and backward solves with the bottom part of L_i and U_i - only (corresponding to the interface variables). This is done using - a special version of the subroutine lusol0 called lusol0_p. Then it - is possible to solve for an approximate Schur complement system - using GMRES on the global approximate Schur complement system - (which is preconditionied by the diagonal blocks represented by - these restricted LU matrices). - -------------------------------------------------------------------- - Coded by Y. Saad - Aug. 1997 - updated Nov. 14, 1997. - C version coded by Zhongze Li, Jan. 2001, updated Sept, 17th, 2001 - Revised by Zhongze Li, June 1st, 2006. - -------------------------------------------------------------------- - */ - schurras_data pc_data; - parms_Operator op_in,op_out; - parms_Map is; - parms_Mat A,S; - parms_FactParam param; - MPI_Comm comm; - parms_Comm handler; - int schur_start,nrow, im,i,jj,i1,k,k1,its,j,ii,maxits,incx=1,ierr; - int if_in_continue,if_out_continue; - FLOAT *rbuf,*x_ext, *y_ext; - int nodv, nsend,n,nloc, n_ext; - REAL *c; - REAL eps,eps1,t,ro,tloc; - REAL gam; - - static int iters = 0; - - /* retrieve the specific data structure for Schur based preconditioners */ - pc_data = (schurras_data)self->data; - - /* get the matrix A */ - A = self->A; - is = A->is; - /* get the dimension of the local matrix */ - n = pc_data->n; - - /* get the beginning location of the Schur complement */ - schur_start = pc_data->schur_start; - - /* get the size of the Schur complement */ - nrow = pc_data->nrow; - - /* get tolerance */ - param = self->param; - eps = param->pgfpar[0]; - eps1 = eps; - - x_ext = pc_data->x_ext; - y_ext = pc_data->y_ext; - - // retrieve the operators - op_in = pc_data->op_in; - op_out = pc_data->op_out; - - // get ras-stuff - handler = pc_data->handler; - rbuf = pc_data->rbuf; - nsend = pc_data->nsend; - nodv = pc_data->nodv; - nloc = pc_data->nloc; - n_ext = pc_data->n_ext; - - S = pc_data->S; - - parms_OperatorLsol(op_out, y, x); - - ///////////////////////// Vernachlaessigung von Eij ///////////////// - // parms_OperatorInvS(op_out,x,x); - ///////////////////////////////////////////////////////////////////// - - - - ///////////////////////////// Si' mit ILU /////////////////////// - // matrix-vector product - if (nsend) { - parms_CommDataBegin(handler,&x[schur_start], 0); - } - PARMS_MEMCPY(y_ext,&x[schur_start],nrow); - if (nodv) { - parms_CommDataEnd(handler); - PARMS_MEMCPY(&y_ext[nrow],rbuf,nodv);//pc_data->n_ext - pc_data->nrow); - } - - parms_OperatorApply(op_in,y_ext,x_ext); - - PARMS_MEMCPY(&x[schur_start],x_ext,nrow); - // backward sweep - - parms_OperatorAscend(op_out, x, x); - - - return 0; -} - -/** Get the ratio of the number of nonzero entries of the - * preconditioning matrix to that of the original matrix. - * - * \param self A preconditioner. - * \param ratio A pointer to the ratio. - * \return 0 on success. - */ -static int pc_schurras_getratio(parms_PC self, double *ratio) -{ - schurras_data pc_data; - parms_Operator op; - int nnz_mat, nnz_pc; - int gnnz_mat, gnnz_pc; - - pc_data = (schurras_data)self->data; - op = pc_data->op_in; - parms_OperatorGetNnz(op, &nnz_mat, &nnz_pc); - MPI_Allreduce(&nnz_mat, &gnnz_mat, 1, MPI_INT, MPI_SUM, - MPI_COMM_WORLD); - MPI_Allreduce(&nnz_pc, &gnnz_pc, 1, MPI_INT, MPI_SUM, - MPI_COMM_WORLD); - *ratio = (double)gnnz_pc/(double)gnnz_mat; - return 0; -} - -/** Create a Schur-complement based preconditioner. - * - * \param self A preconditioner object. - * \return 0 on success. - */ -int parms_PCCreate_Schurras(parms_PC self) -{ - schurras_data data; - - PARMS_NEW(data); - data->issetup = false; - - self->data = data; - self->ops->pc_free = pc_schurras_free; - self->ops->pc_view = pc_schurras_view; - self->ops->apply = pc_schurras_apply; - self->ops->setup = pc_schurras_setup; - self->ops->getratio = pc_schurras_getratio; - - return 0; -} - - - -int parms_OperatorGetU(parms_Operator, void **); -int parms_MatGetOffDiag(parms_Mat, void **); -int parms_CommGetOdvlist(parms_Comm, int **); -static int parms_PC_GetS(parms_PC self, parms_Operator op,parms_Mat *mat) -{ - - int i,j,k; - int n,n_schur,start; - int npro,pid,nodv,nsend,offset; - int *cc,*displs,*idx,*idxloc,*counts; - int cnt1,*nnz,cnt2,newsize; - int *rowj,*offsetptr,*clp; - FLOAT *rowa; - FILE *fptr; - char fname[15]; - parms_Map is,is_schur; - parms_Mat S; - parms_Viewer v; - void *U_loc,*Offdiag; - parms_Table newidx; - parms_vcsr U_vcsr,Offdiag_vcsr; - - MPI_Comm comm; - parms_Comm handler; - - /* get local Schur Matrix - it's part of matrix U after restricted Gauss Elimination*/ - parms_OperatorGetU(op,&U_loc); - U_vcsr = (parms_vcsr)U_loc; - n = U_vcsr->n; - - /* get entries of Interface-Interface-Submatrix Eij */ - parms_MatGetOffDiag(self->A,&Offdiag); - Offdiag_vcsr = (parms_vcsr)Offdiag; - - start = parms_OperatorGetSchurPos(op); - n_schur = n - start; - - /* communicator-data */ - is = self->A->is; - npro = is->npro; - pid = is->pid; - comm = is->comm; - offset = is->start; - - parms_MatGetCommHandler(self->A,&handler); - nodv = parms_CommGetNumRecv(handler); - parms_CommGetOdvlist(handler,&offsetptr); - - - /* displs is local displacement of global Schur matrix S */ - /* cc[i] contains size of local Schur matrix S_i */ - /* newsize = size of global Schur matrix */ - PARMS_NEWARRAY(cc,npro); - PARMS_NEWARRAY(displs,npro+1); - - MPI_Allgather(&n_schur,1,MPI_INT,cc,1,MPI_INT,comm); - displs[0] = 0; - for(i = 0; i < npro; i++) - displs[i+1] = displs[i] + cc[i]; - newsize = displs[npro]; - - /* idx contains global indices of the Schur matrix components */ - PARMS_NEWARRAY(idx,newsize); - PARMS_NEWARRAY(idxloc,is->lsize); - - parms_MapGetGlobalIndices(is,idxloc); - for(i = 0; i < n_schur; i++) - idx[displs[pid]+i] = idxloc[is->iperm[start+i]]-offset; - MPI_Allgatherv(&idx[displs[pid]],n_schur,MPI_INT,idx,cc,displs,MPI_INT,comm); - PARMS_FREE(idxloc); - - /* Create IndexSet-Table of new Ordering */ - parms_TableCreate(&newidx,NULL,newsize); - for(i = 0; i < newsize; i++) - parms_TablePut(newidx,idx[i],i); - - /* now idx contains the indices of local rows of the global Schur matrix */ - PARMS_RESIZE(idx, n_schur); - for(i = 0; i < n_schur; i++) - idx[i] = displs[pid]+i; - - /* create global Schur matrix S */ - parms_MapCreateFromPetsc(&is_schur, n_schur, newsize, comm); - parms_MatCreate(&S, is_schur); - - /*count number of entries of S */ - cnt1 = 0; - for(i = 0; i < n_schur; i++) - cnt1 += U_vcsr->nnzrow[start+i] + Offdiag_vcsr->nnzrow[i]; - - PARMS_NEWARRAY(nnz,n_schur+1); - PARMS_NEWARRAY(rowj,cnt1); - PARMS_NEWARRAY(rowa,cnt1); - - /* fill global Schur Matrix */ - nnz[0] =0; - for(i = 0; i < n_schur; i++) { - cnt1 = U_vcsr->nnzrow[start+i]; - if(cnt1){ - /* entries local Schur matrix */ - PARMS_MEMCPY(&rowa[nnz[i]],U_vcsr->pa[start+i],cnt1); - for(j = 0; j < cnt1; j++) - rowj[nnz[i]+j] = U_vcsr->pj[start+i][j] - start + displs[pid]; - } - cnt2 = Offdiag_vcsr->nnzrow[i]; - if(cnt2){ - /* entries of Eij */ - PARMS_MEMCPY(&rowa[nnz[i]+cnt1],Offdiag_vcsr->pa[i],cnt2); - for(j = 0; j < cnt2; j++){ - clp = parms_TableGet(newidx,offsetptr[Offdiag_vcsr->pj[i][j] - is->lsize]); - rowj[nnz[i]+cnt1+j] = *clp; - } - } - nnz[i+1] = nnz[i]+cnt1+cnt2; - } - parms_MatSetValues(S,n_schur,idx,nnz,rowj,rowa,INSERT); - parms_MatSetup(S); - - PARMS_FREE(cc); - PARMS_FREE(displs); - PARMS_FREE(rowj); - PARMS_FREE(rowa); - PARMS_FREE(nnz); - PARMS_FREE(idx); - parms_TableFree(&newidx); - parms_MapFree(&is_schur); - - /*parms_ViewerCreate(&v,"pschur"); - parms_MatView(S,v); - parms_ViewerFree(&v);*/ - - *mat = S; - - return 0; - -} - - diff --git a/lib/parms/src/parms_qsplit.c b/lib/parms/src/parms_qsplit.c deleted file mode 100755 index 55cbb8e4b..000000000 --- a/lib/parms/src/parms_qsplit.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "parms_sys.h" -#include "DDPQ/protos.h" -#if defined(C99) -#include -#else -#include -#endif - -int qsplitCF(FLOAT *a, int *ind, int n, int ncut) -{ - /*---------------------------------------------------------------------- - | does a quick-sort split of a real array. - | on input a[0 : (n-1)] is a real array - | on output is permuted such that its elements satisfy: - | - | abs(a[i]) >= abs(a[ncut-1]) for i < ncut-1 and - | abs(a[i]) <= abs(a[ncut-1]) for i > ncut-1 - | - | ind[0 : (n-1)] is an integer array permuted in the same way as a. - |---------------------------------------------------------------------*/ - REAL abskey; - FLOAT tmp; - int j, itmp, first, mid, last; - first = 0; - last = n-1; - if (ncut last) return 0; - /* outer loop -- while mid != ncut */ - label1: - mid = first; - abskey = ABS_VALUE(a[mid]); - for (j=first+1; j<=last; j++) { - if (ABS_VALUE(a[j]) > abskey) { - tmp = a[++mid]; - itmp = ind[mid]; - a[mid] = a[j]; - ind[mid] = ind[j]; - a[j] = tmp; - ind[j] = itmp; - } - } - /* interchange */ - tmp = a[mid]; - a[mid] = a[first]; - a[first] = tmp; - itmp = ind[mid]; - ind[mid] = ind[first]; - ind[first] = itmp; - /* test for while loop */ - if (mid == ncut) return 0; - if (mid > ncut) - last = mid-1; - else - first = mid+1; - goto label1; -} diff --git a/lib/parms/src/parms_solver.c b/lib/parms/src/parms_solver.c deleted file mode 100755 index a140ab6f3..000000000 --- a/lib/parms/src/parms_solver.c +++ /dev/null @@ -1,295 +0,0 @@ -/*-------------------------------------------------------------------- - parms_SolverApply : solve the linear system of equation - parms_SolverCreate : create a parms_Solver object. - parms_SolverFree : free the memory for the parms_Solver object. - parms_SolverGetIts : get the iteration counts. - parms_SolverGetMat : get the matrix for the linear system solved. - parms_SolverGetPC : get the preconditioning matrix. - parms_SolverSetParam : set the parameters for the solver (maxits, - tolerance, etc.) - parms_SolverSetType : set the type of the solver. FGMRES and DGMRES - are supplied in the package. - dgmres removed YS - parms_SolverView : dump the maximum iteration count and the - iteration counts - - A code fragment for using solver functions: - - parms_Solver solver; - parms_Mat A; - parms_PC pc; - - // create a solver - parms_SolverCreate(&solver, A, pc); - - // set the maximum number of iterations - parms_SolverSetParam(solver, MAXITS, "300"); - // set the restart size of FGMRES or DGMRES - parms_SolverSetParam(solver, KSIZE, "60"); - // set the convergence tolerance - parms_SolverSetParam(solver, DTOL, "1.0e-6"); - // set the number of eigenvectors - parms_SolverSetParam(solver, NEIG, "8"); - - // solver the linear system - parms_SolverApply(solver, rhs, x); - - // free the memory for solver - parms_SolverFree(&solver); - - $Id: parms_solver.c,v 1.5 2006-12-01 20:44:20 zzli Exp $ - -------------------------------------------------------------------*/ -#include -#include "parms_vec.h" -#include "parms_viewer.h" -#include "parms_mat_impl.h" -#include "parms_pc_impl.h" -#include "parms_solver_impl.h" - -/*int fgmres_create(parms_Solver self);*/ -/* extern int dgmres_create(parms_Solver self); */ - -int parms_SolverView(parms_Solver self, parms_Viewer v) -{ - self->ops->solver_view(self, v); - - return 0; -} - -/** - * Create a parms_Solver object. - * - * @param self A pointer to the parms_Solver object created. - * @param A The matrix of the linear system. - * @param pc The preconditioner. - * - * @return 0 on success. - */ - -int parms_SolverCreate(parms_Solver *self, parms_Mat A, parms_PC pc) -{ - parms_Solver new_solver; - - PARMS_NEW0((new_solver)); - new_solver->ref = 1; - PARMS_NEW0((new_solver)->ops); - new_solver->ops->apply = 0; - new_solver->ops->setksize = 0; - new_solver->istypeset = false; - new_solver->A = A; - A->ref++; - new_solver->pc = pc; - pc->ref++; - new_solver->maxits = 100; - new_solver->tol = 1.0e-6; - *self = new_solver; - return 0; -} - -/** - * Solve the equation \f$Ax = y\f$. - * - * @param self A parms_Solver object. - * @param x The solution vector. - * @param y The right-hand-side vector. - * - * @return 0 on success. - */ -int parms_SolverApply(parms_Solver self, FLOAT *y, FLOAT *x) -{ -// if(self->istypeset == true){ -// self->ops->solver_free(&self); -// self->istypeset = false; -// } - if (self->istypeset == false) { -/* Default solver - fgmres */ - parms_SolverSetType(self, SOLFGMRES); - } - - return self->ops->apply(self, y, x); -} - -/** - * Compute the local residual \f$ r = y - Ax \f$. - * - * @param self A parms_Solver object. - * @param x The solution vector. - * @param y The right-hand-side vector. - * @param r The computed residual vector. - * - * @return 0 on success. - */ - -int parms_SolverGetResidual(parms_Solver self, FLOAT *y, FLOAT *x, FLOAT *r) -{ - return self->ops->getresidual(self, y, x, r); -} - -/** - * Compute the local residual 2-norm \f$ ||r = y - Ax|| \f$. - * - * @param self A parms_Solver object. - * @param x The solution vector. - * @param y The right-hand-side vector. - * @param rnorm The 2-norm of the residual vector. - * - * @return 0 on success. - */ - -int parms_SolverGetResidualNorm2(parms_Solver self, FLOAT *y, FLOAT *x, REAL *rnorm) -{ - return self->ops->getresidualnorm2(self, y, x, rnorm); -} - -/** - * Set the type of the solver. - * - * Only FGMRES solver is available in the package. - * - * @param self A parms_Solver object. - * @param stype The type of Krylov subspace. - * -SOLFGMRES - * -SOLDGMRES - * - * @return 0 on success. - */ - -int parms_SolverSetType(parms_Solver self, SOLVERTYPE stype) -{ - if (self->istypeset && self->stype == stype) { - return 0; - } - if (self->istypeset) { - self->ops->solver_free(&self); - } - if(stype == SOLFGMRES) - fgmres_create(self); - else if(stype == SOLGMRES) - gmres_create(self); - else if(stype == SOLBICGS) - bicgstab_create(self); - else if(stype == SOLPBICGS) - pbicgstab_create(self); - else if(stype == SOLPBICGS_RAS) - pbicgstabras_create(self); - else if(stype == SOLBICGS_RAS) - bicgstabras_create(self); - else if(stype == SOLCG) - cg_create(self); - else{ - printf("ERROR: Invalid choice of solver - (Check SOLVERTYPE for parms_SolverSetType(...) \n"); - PARMS_ABORT(17); - } - self->stype = stype; - self->istypeset = true; - return 0; -} - -/** - * Set parameter for the solver. - * - * Set the maximum iteration counts, the restart size of GMRES, and - * the convergence tolerance. - * - * @param self A parms_Solver object. - * @param ptype The type of parameter. - * -MAXITS maximum iteration counts. - * -KSIZE restart size of GMRES. - * -DTOL converence tolerance. - * -NEIG number of eigenvectors. - * @param param Parameters for the solver. - */ -void parms_SolverSetParam(parms_Solver self, PARAMTYPE paramtype, char - *param) -{ - - if (self->istypeset == false) { - parms_SolverSetType(self, SOLFGMRES); - } - if (paramtype == MAXITS) { - self->maxits = atoi(param); - } - else if (paramtype == KSIZE) { - self->ops->setksize(self, atoi(param)); - } - else if (paramtype == DTOL) { - self->tol = strtod(param, (char **)NULL); - } - else if (paramtype == NEIG) { - self->ops->setneig(self, atoi(param)); - } -} - -/** - * Free the memory for the parms_Solver object. - * - * @param self A pointer to the parms_Solver object to be freed. - * - * @return 0 on success. - */ -int parms_SolverFree(parms_Solver *self) -{ - (*self)->ref--; - if ((*self)->ref == 0 ) { - parms_MatFree(&(*self)->A); - parms_PCFree(&(*self)->pc); - (*self)->ops->solver_free(self); - PARMS_FREE((*self)->ops); - PARMS_FREE(*self); - } - return 0; -} - -/** - * Get the iteration counts. - * - * @param self A parms_Solver object. - * - * @return The iteration counts. - */ -int parms_SolverGetIts(parms_Solver self) -{ - return self->its; -} - -/** - * Get the matrix of the linear system. - * - * @param self A parms_Solver object. - * @param A A pointer to the matrix returned. - * - * @return 0 on success. - */ -int parms_SolverGetMat(parms_Solver self, parms_Mat *A) -{ - - if (self->ref == 1 ) { - *A = self->A; - return 0; - } - else { - *A = NULL; - return -1; - } -} - -/** - * Get the preconditioning matrix. - * - * @param self A parms_Solver object. - * @param PC A pointer to the preconditioning matrix. - * - * @return 0 on success. - */ -int parms_SolverGetPC(parms_Solver self, parms_PC *PC) -{ - if (self->ref == 1 ) { - *PC = self->pc; - return 0; - } - else { - *PC = NULL; - return -1; - } -} - diff --git a/lib/parms/src/parms_table.c b/lib/parms/src/parms_table.c deleted file mode 100755 index 001921032..000000000 --- a/lib/parms/src/parms_table.c +++ /dev/null @@ -1,283 +0,0 @@ -/*-------------------------------------------------------------------- - parms_TableCreate : create a hash table. - parms_TableFree : free the memory for the table. - parms_TableGet : get the corresponding value for a given key. - parms_TableGetSize : get the total number of entries in the table. - parms_TablePut : put the pair (key, value) into the table. - - A code fragment for using table functions: - - parms_Table table; - int key, value, *p; - - // create a hash table using the default hash function - parms_TableCreate(&table, NULL, tsize); - // put pair(key, value) into the table - parms_TablePut(table, key, value); - // get the corresponding value for a given key. - p = parms_TableGet(table, key); - if (p != NULL) { - value = *p; - } - // free the memory for the table object. - parms_TableFree(&table); - - $Id: parms_table.c,v 1.1.1.1 2006-11-27 22:28:01 zzli Exp $ - ------------------------------------------------------------------*/ - -#include "include/parms_table_impl.h" - -static unsigned hashfunc(int key) -{ - unsigned k; - - k = key; - return k; -} - -/** - * Create a hash table. - * - * Create a hash table with hf as the hash function. - * - * @param newT A pointer to the hash table created. - * @param hf The hash function. If hf is null, the default hash - * function is used. - * @param size The number of entries stored in the table. - * - * @return 0 on success. - */ -int parms_TableCreate(parms_Table *newT, HashFcn hf, int tsize) -{ - int i, lwr; - parms_Table table; - static int primes[] = {389, 389, 769, 1543, 3079, 6151, 12289, - 24593, 49157, 98317, 196613, 393241, 786433, - 1572869, 3145739, 6291469, 12582917, - 25165843, 50331653, 100663319, 201326611, - 402653189, 805306457, 1610612741}; - - /* find the maximum prime less than tsize */ - for (i = 1; lwr = (primes[i]*2)/3, lwr < tsize; i++); - - /* malloc the space for the table */ - table = PARMS_ALLOC(sizeof(*table) + - sizeof(table->Slots[0])*primes[i-1]); - table->space = primes[i-1]; - table->Slots = (struct Slot **)(table+1); - for (i = 0; i < table->space; i++) { - table->Slots[i] = NULL; - } - - table->hf = hf ? hf : hashfunc; - table->size = 0; - *newT = table; - return 0; -} - -/** - * Get the corresponding value for a given key. - * - * If return NULL, then the entry with key is not in the table, - * otherwise return a pointer to the value. - * - * @param table A parms_Table object. - * @param key The key value. - * - * @return A pointer to the value. - */ -void *parms_TableGet(parms_Table table, int key) -{ - int index; - struct Slot *p; - - index = table->hf(key) % table->space; - for (p = table->Slots[index]; p; p = p->link) { - if (p->key == key) { - break; - } - } - return p ? &p->value : NULL; -} - -/** - * Put the pair (key, value) into the table. - * - * @param table A parms_Table object. - * @param key The key of the pair. - * @param val The value of the pair. - * - * @return 0 on success. - */ -int parms_TablePut(parms_Table self, int key, int value) -{ - int index; - struct Slot *p; - - index = self->hf(key) % self->space; - for (p = self->Slots[index]; p; p = p->link) { - if (p->key == key) { - break; - } - } - if (p == NULL) { /* new entry */ - PARMS_NEW(p); - p->key = key; - p->value = value; - p->link = self->Slots[index]; - self->Slots[index] = p; - self->size++; - } - else { - p->key = key; - p->value = value; - } - return 0; -} - -/** - * Remove the slot corresponding to the key from the table. - * NOTE: This assumes that the key/value pairs are stored in - * a way that needs to be preserved after the removal. For instance, - * the pARMS matrix object stores the keys as global indices, and - * values as local indices. Thus each of these are unique, and the - * local indices are increasing with respect to the size of the table - * or count of entries in the table. Thus removing an entry requires - * that this relation is preserved. So this removes the slot by first - * swapping values with the last entered entry in the table, before - * actually removing the slot. - * - * @param table A parms_Table object. - * @param key The key of the pair to be removed. - * - * @return 0 on success. - */ -int parms_TableRemoveFromLast(parms_Table self, int key) -{ - int index, i, size; - struct Slot *p, *p1, *p2, *p3, *plast; - - int rank; - MPI_Comm_rank(MPI_COMM_WORLD,&rank); - - index = self->hf(key) % self->space; - /* p1 points to the physical entry just before slot p */ - p1 = self->Slots[index]; - for (p = self->Slots[index]; p; p = p->link) { - if (p->key == key) { - break; - } - p1 = p; - } - if(p != NULL) - { - /* Search for last entered entry to swap values with p*/ - /* This entry is most likely at the leftmost entry */ - size = self->size; - plast = p;/*initialization*/ - for (i = 0; i < self->space; i++) { - plast = self->Slots[i]; - if(plast && plast->value == size-1) break; - } - /* Now swap values of p and plast to maintain table - * consistency - */ - - parms_TableSwap(self,p->key,plast->key); - - /* Now break links by letting p1 link to the next physical entry after p */ - p2 = p->link; - if(p1 == p)/* p is starting slot */ - self->Slots[index] = p2; - else - p1->link = p2; - /* Now free p to actually remove entry */ - PARMS_FREE(p); - - /* now decrement table size */ - self->size--; - - /* Now update last entered entry of table row - * containing plast, by swapping with other table - * row entries, till appropriate position is reached. - * This ensures that entries to the left of a table row are - * always of larger value (or are the latest entered entries) - * of the table row. - */ - p3 = plast->link; - for(p3 = plast; p3->link; p3 = p3->link) - { - if((p3->link)->value <= p3->value) break; - /*swap*/ - parms_TableSwap(self,p3->key,(p3->link)->key); - } - - } - - return 0; -} - -/** - * Swap value of key1 with that of key2. - * - * @param table A parms_Table object. - * @param key1, key2 keys to be swapped. - * - * @return 0 on success. - */ -int parms_TableSwap(parms_Table self, int key1, int key2) -{ - int *value1, *value2, temp1, temp2; - - value1 = parms_TableGet(self,key1); - value2 = parms_TableGet(self,key2); - - if(value1 == NULL || value2 == NULL) - { - if(value1==NULL)printf("Cannot swap with a null entry: parms_TableSwap error...key: %d\n", key1); - if(value2==NULL)printf("Cannot swap with a null entry: parms_TableSwap error...key: %d\n", key2); - MPI_Abort(MPI_COMM_WORLD, 1); - } - else - { - temp1 = *value1; - temp2 = *value2; - parms_TablePut(self, key1, temp2); - parms_TablePut(self, key2, temp1); - } - return 0; -} - -/** - * Free the memory for the table object pointed by self. - * - * @param table A pointer to the parms_Table object. - * - * @return 0 on success. - */ -int parms_TableFree(parms_Table *self) -{ - struct Slot *p1, *p2; - int i; - - for (i = 0; i < (*self)->space; i++) { - for (p1 = (*self)->Slots[i]; p1; p1 = p2) { - p2 = p1->link; - PARMS_FREE(p1); - } - } - PARMS_FREE(*self); - return 0; -} - -/** - * Get the number of entries stored in the table. - * - * @param table A hash table object. - * - * @return The number of entries stored in the table. - */ -int parms_TableGetSize(parms_Table self) -{ - return self->size; -} diff --git a/lib/parms/src/parms_timer.c b/lib/parms/src/parms_timer.c deleted file mode 100755 index c2781873b..000000000 --- a/lib/parms/src/parms_timer.c +++ /dev/null @@ -1,199 +0,0 @@ -/*-------------------------------------------------------------------- - dwalltime : return the ellapsed time in seconds since - the Epoch. - parms_TimerCreate : create a parms_Timer object. - parms_TimerFree : free the memory for the parms_Timer object. - parms_TimerGet : get the elapsed time since the last call - to parms_TimerReset, parms_TimerResetDelay, - or parms_TimerRestart. - parms_TimerPause : pause the timer. - parms_TimerRestart : restart the timer. - parms_TimerReset : reset the timer to 0. - parms_TimerResetDelay : suspend the timer. - parms_TimerView : dump the parms_Timer object. - - A code fragment for using timer functions: - - parms_Timer t; - - // create a timer. - parms_TimerCreate(&t); - parms_TimerReset(t); - ... fragment code 1 - // time spent on the fragment code 1. - parms_TimerGet(t); - parms_TimerPause(t); - ... - ... - parms_TimerRestart(t); - ... framgment code 2 - // total time spent on fragment code1 and code2. - parms_TimerGet(t) - // free the memory for t - parms_TimerFree(&t); - - $Id: parms_timer.c,v 1.1.1.1 2006-11-27 22:28:01 zzli Exp $ - ------------------------------------------------------------------*/ -#include -#include -#include -#include -#include "./include/parms_timer_impl.h" -#include "parms_viewer.h" - -/** - * Return the elapsed time since the Epoch. - * - * @return The wall-clock time in seconds. - */ -static double dwalltime() -{ -#ifdef USE_MPI - int flag; - MPI_Initialized(&flag); - if (flag) { - return MPI_Wtime(); - } - else { - struct timeval tval; - double t; - - gettimeofday(&tval, NULL); - t = (double)(tval.tv_sec + tval.tv_usec / 1.0e6); - return t; - } -#else - if (true) { - struct timeval tval; - double t; - - gettimeofday(&tval, NULL); - t = (double)tval.tv_sec + (double)tval.tv_usec/1.0e6; - return t; - } -#endif -} - -/** - * Reset the timer to 0. - * - * Set the elapsed time to zero and set the initial time of the - * timer. - * - * @param self A parms_Timer object. - * - * @return 0 on success. - */ -int parms_TimerReset(parms_Timer self) -{ - self->elapsed_time = 0.0; - self->initial_time = dwalltime(); - return 0; -} - -/** - * Create a parms_Timer object. - * - * @param self A pointer to the parms_Timer object created. - */ -void parms_TimerCreate(parms_Timer *self) -{ - parms_Timer new_timer; - - PARMS_NEW0((new_timer)); - new_timer->ref = 1; - parms_TimerReset(new_timer); - *self = new_timer; -} - -/** - * Reset the elapsed time of self to delay. - * - * @param self A parms_Timer object. - * @param delay Reset the elapsed time to delay seconds. - * - * @return 0 on success. - */ -int parms_TimerResetDelay(parms_Timer self, double delay) -{ - parms_TimerReset(self); - self->elapsed_time = delay; - return 0; -} - -/** - * Pause the parms_Timer object self. - * - * @param self A parms_Timer object. - * - * @return 0 on success. - */ -int parms_TimerPause(parms_Timer self) -{ - self->elapsed_time = parms_TimerGet(self); - return 0; -} - -/** - * Restart the timer. - * - * @param self A parms_Timer object. - * - * @return 0 on success. - */ -int parms_TimerRestart(parms_Timer self) -{ - self->initial_time = dwalltime(); - return 0; -} - -/** - * Return The wall-clock time since the last call to - * parms_TimerReset, parms_TimerResetDelay, parms_TimerRestart. - * - * @param self A parms_Timer object. - * - * @return The elapsed wall-clock time in seconds. - */ -double parms_TimerGet(parms_Timer self) -{ - double current, time; - - current = dwalltime(); - time = current - self->initial_time; - return (time + self->elapsed_time); -} - -/** - * Free the memory for the parms_Timer object - * - * @param self A pointer to the parms_Timer object - * - * @return 0 on success. - */ -int parms_TimerFree(parms_Timer *self) -{ - - PARMS_FREE(*self); - return 0; -} - -/** - * Dump parms_Timer self via parms_Viewer object v. - * - * @param self A parms_Timer object. - * @param v A parms_Viewer object. - * - * @return 0 on success. - */ -int parms_TimerView(parms_Timer self, parms_Viewer v) -{ - FILE *fp; - - parms_ViewerGetFP(v, &fp); - fprintf(fp, "init_time = %f elapsed =%f\n", self->initial_time, - self->elapsed_time); - - return 0; -} - diff --git a/lib/parms/src/parms_vec.c b/lib/parms/src/parms_vec.c deleted file mode 100755 index 5616eb2ae..000000000 --- a/lib/parms/src/parms_vec.c +++ /dev/null @@ -1,805 +0,0 @@ -/*-------------------------------------------------------------------- - parms_VecAXPY : y = y + alpha x - parms_VecAYPX : y = scalar y + x - parms_VecDOT : inner product of two vectors. - parms_VecDotArray : inner products for the vector to an array - of vector object. - - parms_VecGetNorm2 : get the 2-norm of a vector. - parms_VecPerm : permute a vector object. - - parms_VecScale : scale the components of a vector. - A code fragment for using vector functions: - - parms_Vec vec; - parms_Map map; - - // assume PE0 contains global variables (0, 2, 4, 6, 8), PE1 - // contains global variables (1, 3, 5, 7, 9). Both PE0 and PE1 call - // the following parms_VecSetValues function. Each PE will pick and - // insert variables which belong to the local PE according to the - // partionting information stored in map. - - // all vector computation functions can be called - parms_VecGetNorm2(vec, &value, map); - parms_VecDot(vec, vec, &value, map); - ... - - $Id: parms_vec.c,v 1.3 2007-05-10 14:11:21 zzli Exp $ - ------------------------------------------------------------------*/ -#include "parms_sys.h" -#include "parms_viewer.h" -#include "parms_vec.h" -#include "parms_map_impl.h" -#if defined(__ICC) -#include -#else -#if defined(C99) -#include -#else -#include -#endif -#endif - -typedef struct ext_vec_data { - /*! \var im The external variables in global indexing - */ - int *im; - /*! \var values The external variable contribution. - */ - FLOAT *values; - /*! \var n Number if external variable contribution. - */ - int n; - /*! \var space Size of work space. - */ - int space; -} *ext_vec_data; - - -/** - * Scale a vector. - * - * All components of parms_Vec object self times scalar. - * \f$self = scalar \times self\f$. - * - * @param self A vector object. - * @param scalar A scalar. - * - * @return 0 on success. - */ -int parms_VecScale(FLOAT *self, FLOAT scalar, parms_Map map) -{ - int lsize, i; - - lsize = parms_MapGetLocalSize(map); - for (i = 0; i < lsize; i++) - { - self[i] *= scalar; - } - - return 0; -} - - -/** - * Perform \f$self := scalar \times x + self\f$. - * - * @param self A vector object. - * @param x Another vector object. - * @param scalar A scalar. - * - * @return 0 on success. - */ -int parms_VecAXPY(FLOAT *self, FLOAT *x, FLOAT scalar, parms_Map map) -{ - int i, lsize; - - lsize = parms_MapGetLocalSize(map); - - - for (i = 0; i < lsize; i++) - { - self[i] += scalar * x[i]; - } - - - return 0; -} - -/** - * Perform \f$self = scalar \times self + x\f$. - * - * @param self A vector object. - * @param x Another vector object. - * @param scalar A scalar. - * - * @return 0 on success. - */ -int parms_VecAYPX(FLOAT *self, FLOAT *x, FLOAT scalar, parms_Map map) -{ - int lsize, i; - lsize = parms_MapGetLocalSize(map); - - for (i = 0; i < lsize; i++) { - self[i] = scalar * self[i] + x[i]; - } - - return 0; -} - - -/* Dot product on local PE -*/ -static int vec_LDot(FLOAT *self, FLOAT *x, FLOAT *value, parms_Map map) -{ - int lsize, i; - - lsize = parms_MapGetLocalSize(map); - -#if defined(DBL_CMPLX) - FLOAT dot = 0.0 + 0.0*I; - for (i = 0; i < lsize; i++) - dot += self[i] * x[i]; -#else - FLOAT dot = 0.0; - for (i = 0; i < lsize; i++) { - dot += self[i] * x[i]; - } -#endif - - *value = dot; - - - return 0; -} - -/* Dot product on local PE - uses complex conjugate -*/ -static int vec_LDotc(FLOAT *self, FLOAT *x, FLOAT *value, parms_Map map) -{ - int lsize, i; - - lsize = parms_MapGetLocalSize(map); - -#if defined(DBL_CMPLX) - FLOAT dot = 0.0 + 0.0*I; - for (i = 0; i < lsize; i++) - dot += self[i] * conj(x[i]); -#else - FLOAT dot = 0.0; - for (i = 0; i < lsize; i++) { - dot += self[i] * x[i]; - } -#endif - - *value = dot; - - return 0; -} - -/** - * Perform the (global) inner product of two vectors. - * - * value = self x^{T}. If self - * - * - * @param self A vector object. - * @param x Another vector object. - * @param value The inner product returned. - * - * @return 0 on success. - */ -int parms_VecDOT(FLOAT *self, FLOAT *x, FLOAT *value, parms_Map is) -{ - MPI_Comm comm; - - comm = is->comm; - - if (is->isserial) { - vec_LDot(self, x, value, is); - } - else { - FLOAT ldot; - vec_LDot(self, x, &ldot, is); - -#if defined(DBL_CMPLX) - MPI_Allreduce(&ldot, value, 1, MPI_CMPLX, MPI_CMPLX_SUM, comm); -#else - MPI_Allreduce(&ldot, value, 1, MPI_DOUBLE, MPI_SUM, comm); -#endif - } - - return 0; -} - -/** - * Perform the (global) inner product of two vectors. - * - * If self and x are real vectors, value = self x^{T}. If self - * and x are complex vectors, value = self \overline{x}^{T}. - * - * @param self A vector object. - * @param x Another vector object. - * @param value The inner product returned. - * - * @return 0 on success. - */ -int parms_VecDOTC(FLOAT *self, FLOAT *x, REAL *value, parms_Map is) -{ - MPI_Comm comm; - - comm = is->comm; - - if (is->isserial) { -#if defined(DBL_CMPLX) - FLOAT val; - vec_LDotc(self, x, &val, is); - *value = creal(val); -#else - vec_LDot(self, x, value, is); -#endif - } - else { -#if defined(DBL_CMPLX) - FLOAT ldot; - REAL lval; - vec_LDotc(self, x, &ldot, is); - lval = creal(ldot); - - MPI_Allreduce(&lval, value, 1, MPI_DOUBLE, MPI_SUM, comm); -#else - FLOAT ldot; - vec_LDot(self, x, &ldot, is); - MPI_Allreduce(&ldot, value, 1, MPI_DOUBLE, MPI_SUM, comm); -#endif - } - - return 0; -} - -/** - * Return the 2-norm of the vector. - * - * @param self A vector object. - * @param value The 2-norm returned. - * - * @return 0 on success. - */ -int parms_VecGetNorm2(FLOAT *self, REAL *value, parms_Map is) -{ - - if (is->isserial) { - - FLOAT dot; - vec_LDotc(self, self, &dot, is); - *value = ABS_VALUE(dot); - *value = sqrt(*value); - - } - else { - REAL dot; -#if defined(DBL_CMPLX) - parms_VecDOTC(self, self, &dot, is); -#else - parms_VecDOT(self, self, &dot, is); -#endif - *value = fabs(dot); - *value = sqrt(*value); - } - return 0; -} - -/** - * Perform the inner product between self and an array of vectors. - * - * The pseudo code: - * - * \f{verbatim} - * for (i = 0; i < n; i++) { - * result[i] = self * vecarray[i]; - * } - * \f} - * - * @param self A vector object. - * @param n The size of vecarray. - * @param vecarray An array of vector objects. - * @param aux An auxiliary array. - * @param result An array of size n to store inner products. - * - * @return 0 on success. - */ -int parms_VecDotArray(FLOAT *self, int n, FLOAT - **vecarray, FLOAT *result, parms_Map is) -{ - int i; - FLOAT *aux; - MPI_Comm comm; - - comm = is->comm; - if (is->isserial) { - for (i = 0; i < n; i++) - vec_LDotc(self, vecarray[i], &result[i], is); - } - else { - PARMS_NEWARRAY(aux, n); - for (i = 0; i < n; i++) - vec_LDotc(self, vecarray[i], &aux[i], is); -#if defined(DBL_CMPLX) - MPI_Allreduce(aux, result, n, MPI_CMPLX, MPI_CMPLX_SUM, comm); -#else - MPI_Allreduce(aux, result, n, MPI_DOUBLE, MPI_SUM, comm); -#endif - PARMS_FREE(aux); - } - return 0; -} - -int parms_VecPerm(FLOAT *self, parms_Map map) -{ - int *perm; - int size, i, k, rank; - FLOAT *newvec; - - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - size = parms_MapGetLocalSize(map); - if (map->isperm && (map->isvecperm == false)) { - perm = map->perm; - PARMS_NEWARRAY(newvec, size); - for (i = 0; i < size; i++) { - k = perm[i]; - newvec[k] = self[i]; - } - memcpy(self, newvec, size*sizeof(FLOAT)); - - PARMS_FREE(newvec); - } - return 0; -} - -int parms_VecPermAux(FLOAT *self, FLOAT *aux, parms_Map map) -{ - int *perm; - int size, i, k, rank; - - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - size = parms_MapGetLocalSize(map); - if (map->isperm) { - perm = map->perm; - for (i = 0; i < size; i++) { - k = perm[i]; - aux[k] = self[i]; - } - } - else - { - for (i = 0; i < size; i++) { - aux[i] = self[i]; - } - } - return 0; -} - -int parms_VecInvPerm(FLOAT *self, parms_Map map) -{ - int *perm; - int size, i, k, rank; - FLOAT *newvec; - - size = parms_MapGetLocalSize(map); - if (map->isperm && (map->isvecperm == false)) { - perm = map->iperm; - PARMS_NEWARRAY(newvec, size); - for (i = 0; i < size; i++) { - k = perm[i]; - newvec[k] = self[i]; - } - memcpy(self, newvec, size*sizeof(FLOAT)); - - PARMS_FREE(newvec); - } - return 0; -} - -int parms_VecInvPermAux(FLOAT *self, FLOAT *aux, parms_Map map) -{ - int *perm; - int size, i, k, rank; - - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - size = parms_MapGetLocalSize(map); - if (map->isperm) { - perm = map->iperm; - for (i = 0; i < size; i++) { - k = perm[i]; - aux[k] = self[i]; - } - } - else - { - for (i = 0; i < size; i++) { - aux[i] = self[i]; - } - } - return 0; -} - -/** - * Insert values to parms_Vec object self. - * - * A pseudo code from the global point of view: - * - * \f{verbatim} - * for (i = 0; i < m; i++) { - * self[im[i]] = values[i]; - * } - * \f} - * - * @param self A vector object. - * @param m The number of variables to be inserted. - * @param im An array of global variable indices. - * @param value An array of values to be inserted to self.s - * @param mode The style of set values: - * -ADD add values to parms_Vec object self. - * -INSERT assign values to parms_Vec object self. - * - * @return 0 on success. - */ -int parms_VecSetValues(FLOAT *self, int m, int *im, FLOAT - *values, INSERTMODE mode, parms_Map map) -{ - int lsize, rindex, index, lrindex, *rp, i; - int offset = map->start; - - lsize = parms_MapGetLocalSize(map); - - if (!map->isserial) { - if (map->isvecperm) { - for (i = 0; i < m; i++) { - rindex = im[i] - offset; - rp = parms_MapGlobalToLocal(map, rindex); - if ((rp != NULL) && ((index = *rp) < lsize)) { - lrindex = map->perm[index]; - if (mode == ADD) { - self[lrindex] += values[i]; - } - else if (mode == INSERT) { - self[lrindex] = values[i]; - } - } - } - } - else { - for (i = 0; i < m; i++) { - rindex = im[i] - offset; - rp = parms_MapGlobalToLocal(map, rindex); - if ((rp != NULL) && ((lrindex = *rp) < lsize)) { - if (mode == ADD) { - self[lrindex] += values[i]; - } - else if(mode == INSERT) { - self[lrindex] = values[i]; - } - } - } - } - } - else { - for (i = 0; i < m; i++) { - rindex = im[i] - offset; - if (map->isvecperm) { - rindex = map->perm[rindex]; - } - if (mode == ADD) { - self[rindex] += values[i]; - } - else if(mode == INSERT) { - self[rindex] = values[i]; - } - } - } - return 0; -} - -/** - * Insert values to parms_Vec object self. This assumes the vector - * values are being set element-by-element. A call to parms_VecAssembleElementVector - * is required to complete the vector once all entries have been added. - * - * A pseudo code from the global point of view: - * - * \f{verbatim} - * for (i = 0; i < m; i++) { - * self[im[i]] = values[i]; - * } - * \f} - * - * @param self A vector object. - * @param m The number of variables to be inserted. - * @param im An array of global variable indices. - * @param value An array of values to be inserted to self.s - * @param mode The style of set values: - * -ADD add values to parms_Vec object self. - * -INSERT assign values to parms_Vec object self. - * - * @return 0 on success. - */ -int parms_VecSetElementVector(FLOAT *self, int m, int *im, FLOAT - *values, INSERTMODE mode, parms_Map map) -{ - int lsize, rindex, index, lrindex, *rp, i,pos,k; - int offset = map->start, space; - ext_vec_data vdata; - - lsize = parms_MapGetLocalSize(map); - - if(map->isserial) { -/* call vecsetvalues for serial version */ - return parms_VecSetValues(self, m, im, values, mode, map); - } - -/* Parallel call */ - if(map->isdatalloc) - { - vdata = (ext_vec_data)map->data; - } - else - { - /* Allocate some memory for vdata */ - map->isdatalloc = true; - PARMS_NEW(vdata); - vdata->space = lsize; - PARMS_NEWARRAY(vdata->im, vdata->space); - PARMS_NEWARRAY0(vdata->values, vdata->space); - vdata->n = 0; - map->data = vdata; - } - - if (map->isvecperm) { - for (i = 0; i < m; i++) { - rindex = im[i] - offset; - rp = parms_MapGlobalToLocal(map, rindex); - if ((rp != NULL) && ((index = *rp) < lsize)) { - lrindex = map->perm[index]; - if (mode == ADD) { - self[lrindex] += values[i]; - } - else if (mode == INSERT) { - self[lrindex] = values[i]; - } - } - else{ - /* external contribution - first check if there is enough memory*/ - space = vdata->space; - if(vdata->n == space){ - /* reallocate memory for holding new entry */ - space += 10; - PARMS_RESIZE(vdata->im, space); - PARMS_RESIZE(vdata->values, space); - vdata->space = space; - } - - pos = 0; - for(k = 0; kn; k++) /* check if row has been added already */ - { - if(vdata->im[k] == im[i]) - break; - } - if(k < vdata->n) // already added row - { - pos = k; - vdata->values[pos] += values[i]; - } - else // new row contribution - { - pos = vdata->n; - vdata->im[vdata->n] = im[i]; - vdata->values[vdata->n++] = values[i]; - } - } - } - } - else { - for (i = 0; i < m; i++) { - rindex = im[i] - offset; - rp = parms_MapGlobalToLocal(map, rindex); - if ((rp != NULL) && ((lrindex = *rp) < lsize)) { - if (mode == ADD) { - self[lrindex] += values[i]; - } - else if(mode == INSERT) { - self[lrindex] = values[i]; - } - } - else{ - /* external contribution - first check if there is enough memory*/ - space = vdata->space; - if(vdata->n == space){ - /* reallocate memory for holding new entry */ - space += 10; - PARMS_RESIZE(vdata->im, space); - PARMS_RESIZE(vdata->values, space); - vdata->space = space; - } - - pos = 0; - for(k = 0; kn; k++) /* check if row has been added already */ - { - if(vdata->im[k] == im[i]) - break; - } - if(k < vdata->n) // already added row - { - pos = k; - vdata->values[pos] += values[i]; - } - else // new row contribution - { - pos = vdata->n; - vdata->im[pos] = im[i]; - vdata->values[pos] = values[i]; - vdata->n++; - } - } - } - } - - return 0; -} - -/** - * Completes setting up values for the distributed vector - * - * @param self A vector object. - * @param map A pARMS map object - * - * @return 0 on success. - */ -int parms_VecAssembleElementVector(FLOAT *self, parms_Map map) -{ - FLOAT *gvec; - int i, k, gnodv,lsize, mypid, npro, lrindex, index; - int *info, *disp, *pos, *gextvars; - MPI_Comm comm; - ext_vec_data vdata; - - lsize = map->lsize; - mypid = map->pid; - npro = map->npro; - comm = map->comm; - - if(map->isdatalloc){ - vdata = (ext_vec_data)map->data; -/* Allocate some memory */ - PARMS_NEWARRAY(info, npro); - PARMS_NEWARRAY(disp, npro+1); - -/* collect contributions from other processors */ - MPI_Allgather(&vdata->n, 1, MPI_INT, info, 1, MPI_INT, comm); - disp[0] = 0; - for (i = 0; i < npro; i++) { - disp[i+1] = disp[i] + info[i]; - } - - gnodv = disp[npro]; - PARMS_NEWARRAY(gextvars, gnodv); - PARMS_NEWARRAY0(gvec, gnodv); - - MPI_Allgatherv(vdata->im, vdata->n, MPI_INT, gextvars, - info, disp, MPI_INT, comm); -#if defined(DBL_CMPLX) - MPI_Allgatherv(vdata->values, vdata->n, MPI_CMPLX, gvec, - info, disp, MPI_CMPLX, comm); -#else - MPI_Allgatherv(vdata->values, vdata->n, MPI_DOUBLE, gvec, - info, disp, MPI_DOUBLE, comm); -#endif - for(i=0; istart; - pos = parms_MapGlobalToLocal(map,index); - if ((pos != NULL) && ((lrindex = *pos) < lsize)) { - self[lrindex] += gvec[k]; - } - } - } - } -/* Free memory */ - PARMS_FREE(info); - PARMS_FREE(disp); - PARMS_FREE(gvec); - PARMS_FREE(gextvars); - PARMS_FREE(vdata->im); - PARMS_FREE(vdata->values); - PARMS_FREE(vdata); - map->isdatalloc = false; - } - - return 0; -} - -/** - * Gather distributed vector to a global array. - * - * @param self The distributed vector. - * @param ga A global vector. - * - * @return 0 on success. - */ -int parms_VecGather(FLOAT *self, FLOAT *ga, parms_Map map) -{ - BOOL isserial; - int i, j, incx, lsize, index, npro, pid; - int *num_recv, *ind_array, *ind_snd, maxnum; - FLOAT *wk; - - isserial = map->isserial; - pid = map->pid; - npro = map->npro; - - lsize = parms_MapGetLocalSize(map); - if (isserial) { - for (i = 0; i < lsize; i++) ga[i] = self[i]; - } - else { - PARMS_NEWARRAY0(num_recv, npro); - PARMS_NEWARRAY(ind_snd, lsize); - MPI_Allgather(&lsize, 1, MPI_INT, num_recv, 1, MPI_INT, map->comm); - maxnum = 0; - for (i = 0; i < npro; i++) { - if (maxnum < num_recv[i]) { - maxnum = num_recv[i]; - } - } - PARMS_NEWARRAY(wk, maxnum); - PARMS_NEWARRAY(ind_array, maxnum); - - for (i = 0; i < lsize; i++) { - index = map->lvars[i]; - ga[index] = self[i]; - ind_snd[i] = map->lvars[i]; - } - -#if defined(DBL_CMPLX) - for (i = 0; i < npro; i++) { - if (pid == i) { - MPI_Bcast(ind_snd, lsize, MPI_INT, i, map->comm); - MPI_Bcast(self, lsize, MPI_CMPLX, i, map->comm); - } - else { - MPI_Bcast(ind_array, num_recv[i], MPI_INT, i, map->comm); - MPI_Bcast(wk, num_recv[i], MPI_CMPLX, i, map->comm); - for (j = 0; j < num_recv[i]; j++) { - index = ind_array[j]; - ga[index] = wk[j]; - } - } - } -#else - for (i = 0; i < npro; i++) { - if (pid == i) { - MPI_Bcast(ind_snd, lsize, MPI_INT, i, map->comm); - MPI_Bcast(self, lsize, MPI_DOUBLE, i, map->comm); - } - else { - MPI_Bcast(ind_array, num_recv[i], MPI_INT, i, map->comm); - MPI_Bcast(wk, num_recv[i], MPI_DOUBLE, i, map->comm); - for (j = 0; j < num_recv[i]; j++) { - index = ind_array[j]; - ga[index] = wk[j]; - } - } - } -#endif - PARMS_FREE(ind_snd); - PARMS_FREE(ind_array); - PARMS_FREE(wk); - } - - return 0; -} - diff --git a/lib/parms/src/parms_viewer.c b/lib/parms/src/parms_viewer.c deleted file mode 100755 index 4cb62e0c3..000000000 --- a/lib/parms/src/parms_viewer.c +++ /dev/null @@ -1,148 +0,0 @@ -/*-------------------------------------------------------------------- - parms_ViewerCreate : create a parms_Viewer object. - parms_ViewerFree : free the memory for a parms_Viewer object. - parms_ViewerGetFP : retrieve the file pointer. - parms_ViewerGetFname : get the file name. - parms_ViewerStoreFP : restore the file pointer. - - A code fragment for using viewer functions: - - parms_Viewer v; - parms_Map map; - - // create a parms_Map object. - parms_MapCreateFromGlobal(&map, ...); - // create a parms_Viewer object. - parms_ViewerCreate(&v, "foo"); - // dump map to a file via v. i.e. foo0.dat on PE0, f001.dat on PE1 - parms_MapView(map, v); - ... - // free the memory for the parms_Viewer object v. - pams_ViewerFree(&v); - - $Id: parms_viewer.c,v 1.1.1.1 2006-11-27 22:28:01 zzli Exp $ - ------------------------------------------------------------------*/ -#include "./include/parms_viewer_impl.h" - -/** - * Create a parms_Viewer object. - * - * If PARMS_COUT and PARMS_CERR are input as fname, they stand for - * standard output and standard error, respectively. Otherwise, each - * PE create a file "fnameID.dat". ID stands for ID of PE. - * - * @param self A pointer to the parms_Viewer object. - * @param fname A file name to store data. - * - * @return 0 on success. - */ -int parms_ViewerCreate(parms_Viewer *self, char *filename) -{ - parms_Viewer new_viewer; - int pid, length; - - PARMS_NEW0((new_viewer)); - new_viewer->ref = 1; - - length = strlen(filename) + 6; - MPI_Comm_rank(MPI_COMM_WORLD, &pid); - length += pid / 10 + 1; - PARMS_NEWARRAY(new_viewer->fname, length); - new_viewer->isstd = false; - if (!strcmp(filename, PARMS_COUT)) { - new_viewer->isstd = true; - new_viewer->fp = stdout; - } - else if (!strcmp(filename, PARMS_CERR)) { - new_viewer->isstd = true; - new_viewer->fp = stderr; - } - else { - strcpy(new_viewer->fname, filename); - sprintf(new_viewer->fname, "%s_%d.dat", new_viewer->fname, pid); - } - - *self = new_viewer; - if (filename == NULL) { - return -1; - } - else { - return 0; - } -} - -/** - * Free the memory of the parms_Viewer object. - * - * @param self A pointer to the parms_Viewer object. - * - * @return 0 on success. - */ -int parms_ViewerFree(parms_Viewer *self) -{ - assert(self && *self); - (*self)->ref--; - if ((*self)->ref == 0 ) { - if ((*self)->fname != NULL) { - if (strcmp((*self)->fname, PARMS_COUT) && strcmp((*self)->fname, - PARMS_CERR)) { - PARMS_FREE((*self)->fname); - } - } - PARMS_FREE(*self); - } - return 0; -} - -/** - * Get a pointer to file pointer. - * - * @param self A parms_Viewer object. - * @param fp A pointer to the file pointer. - * - * @return 0 on success. - */ -int parms_ViewerGetFP(parms_Viewer self, FILE **fp) -{ - if (!self->isstd) { - self->fp = fopen(self->fname, "a+"); - if (self->fp == NULL) { - fprintf(stderr, "cannot open file %s\n", self->fname); - } - } - - *fp = self->fp; - return 0; -} - -/** - * Store fp to the parms_Viewer object. - * - * @param self A parms_Viewer object. - * @param fp A file pointer. - * - * @return 0 on success. - */ -int parms_ViewerStoreFP(parms_Viewer self, FILE *fp) -{ - if (!self->isstd) { - fclose(fp); - } - return 0; -} - -/** - * Retrieve the file name. - * - * @param self A parms_Viewer object. - * @param fname The file name retrieved. - * - * @return 0 on success. - */ -int parms_ViewerGetFname(parms_Viewer self, char **fname) -{ - *fname = self->fname; - return 0; -} - - diff --git a/lib/parms/src/pbicgstab.c b/lib/parms/src/pbicgstab.c deleted file mode 100755 index 7f181c490..000000000 --- a/lib/parms/src/pbicgstab.c +++ /dev/null @@ -1,326 +0,0 @@ -/*-------------------------------------------------------------------- - pbicgstab_create : create the PBICGS solver. - pbicgstab_free : free the memory for the PBICGS solver. - pbicgstab_view : dump the PBICGS solver. - parms_pbicgstab : the PBICGS solve function. - - Pipelined BiCGstab, overlapping global sums with computation. See: - Siegfried Cools, Wim Vanroose - The communication-hiding pipelined BiCGStab method for the parallel solution of large unsymmetric linear systems - Parallel Computing 65, pp. 1-20, July 2017 - ------------------------------------------------------------------*/ - -#if defined(__ICC) -#include -#else -#if defined(C99) -#include -#else -#include -#endif -#endif -#include "parms_vec.h" -#include "parms_mat.h" -#include "parms_pc.h" -#include "./include/parms_solver_impl.h" - -#define DBL_EPSILON 2.2204460492503131e-16 // double epsilon - -typedef struct pbicgs_data { - int neigs; -} *pbicgstab_data; - -#define TINY 1.0e-20 -int parms_pbicgstab(parms_Solver self, double *rhs, double *xin) -{ - - int i, its; - int maxits, nloc, size, one = 1; - double tmp[5]; - double *b, *r0, *r, *rt, *w, *wt, *t, *pt, *s, *st, *z, *zt; - double *q, *qt, *v, *x, *y; - double tol, alpha, beta, omega, myres, rho, rho_alt; - MPI_Request req, req1, req2; - - parms_Mat A; - parms_PC pc; - parms_Map is; - parms_Viewer viewer; - - int rank; - MPI_Comm comm; - - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - - A = self->A; - pc = self->pc; - is = A->is; - - maxits = self->maxits; - tol = self->tol*self->tol; - nloc = parms_MapGetLocalSize(is); - comm = is->comm; - -/*----- allocate memory for working local arrays -------*/ -/*----- initialize to zero if not initialized before the iteration starts ---*/ - size = nloc; - - PARMS_NEWARRAY(b, size); - PARMS_NEWARRAY(r0, size); - PARMS_NEWARRAY(r, size); for (i=0; iisperm) { - for (i = 0; i < nloc; i++) { - x[is->perm[i]] = xin[i]; - b[is->perm[i]] = rhs[i]; - } - is->isvecperm = true; - } - - - /* compute residual vector r0 = b - Ax */ - /* b = y (permuted), t=Ax temporary vector */ - parms_MatVec(A, x, t); - for (i=0; iisserial == false) - MPI_Iallreduce(MPI_IN_PLACE, tmp, 2, MPI_DOUBLE, MPI_SUM, comm, &req); - - parms_PCApply(pc, w, wt); // wt = M w - parms_MatVec(A, wt, t); // t = Awt - - omega = 0.; - beta = 0.; - - if(is->isserial == false) MPI_Wait(&req, MPI_STATUS_IGNORE); - - rho_alt = tmp[0]; - alpha = tmp[0] / tmp[1]; // alpha = (r0,r0) / (r0,w) - - - for (its = 0; its < maxits; its++){ - - for (i=0; iisserial == false) - MPI_Iallreduce(MPI_IN_PLACE, tmp, 2, MPI_DOUBLE, MPI_SUM, comm, &req1); - - parms_PCApply(pc, z, zt); - parms_MatVec(A, zt, v); - - if(is->isserial == false) MPI_Wait(&req1, MPI_STATUS_IGNORE); - - omega = tmp[0]/tmp[1]; - - for (i=0; iisserial == false) - MPI_Iallreduce(MPI_IN_PLACE, tmp, 5, MPI_DOUBLE, MPI_SUM, comm, &req2); - - parms_PCApply(pc, w, wt); - parms_MatVec(A, wt, t); - - if(is->isserial == false) MPI_Wait(&req2, MPI_STATUS_IGNORE); - - // Convergence check: - if ( tmp[4] < tol) break; - - rho = tmp[0]; - beta = alpha*rho / (omega*rho_alt); - rho_alt = rho; - alpha = rho / (tmp[1] + beta*(tmp[2] - omega*tmp[3])); - - //pbicgstab_getresidualnorm2(self, b, x, &myres); - - //if(rank == 0) printf("nparms check at %d, myres=%e, (r,r)=%e\n",its,myres, tmp[4]); - - //if (myres < tol) break; - } - - - its++; - if(rank == 0){ - if(its == maxits) { - printf("ERROR: no convergence\n"); - } else { - printf("Parms iterations, residuum %d, %e\n",its,sqrt(tmp[4])); - } - } - /* reset isvecperm and do inverse permutation*/ - if (is->isperm) { - for (i = 0; i < nloc; i++) { - xin[is->iperm[i]] = x[i]; - } - is->isvecperm = false; - } - - - free(b); - free(r0); - free(r); - free(rt); - free(w); - free(wt); - free(t); - free(pt); - free(s); - free(st); - free(z); - free(zt); - free(q); - free(qt); - free(y); - free(v); - free(x); - - self->its = its; - return 0; -} - -static int pbicgstab_getresidual(parms_Solver self, double *y, double *x, double *res) -{ - - parms_Mat A; - - A = self->A; - - parms_MatMVPY(A, -1.0, x, 1.0, y, res); - - return 0; -} - -static int pbicgstab_getresidualnorm2(parms_Solver self, double *y, double *x, double *rnorm) -{ - - int nloc; - double *res; - parms_Mat A; - parms_Map is; - - A = self->A; - is = A->is; - nloc = parms_MapGetLocalSize(is); - PARMS_NEWARRAY(res, nloc); - - parms_MatMVPY(A, -1.0, x, 1.0, y, res); - - parms_VecGetNorm2(res, rnorm, is); - - PARMS_FREE(res); - - return 0; -} - - -static int pbicgstab_free(parms_Solver *self) -{ - /*dummy*/ - return 0; -} - -static int pbicgstab_view(parms_Solver self, parms_Viewer v) -{ - FILE *fp; - char *name, *iluname; - int dim; - parms_ViewerGetFP(v, &fp); - - fprintf(fp,"\n=============================\n"); - fprintf(fp," Solver Parameters \n"); - fprintf(fp,"=============================\n"); - - fprintf(fp, "Solver type = Pipelined BiConjugate Gradient Stabilized Method (PBICGS) \n"); - - fprintf(fp, "maxits = %d \n", self->maxits); - fprintf(fp, "Relative tolerance = %-8.2e \n", self->tol); - - parms_PCGetName(self->pc, &name); - parms_PCILUGetName(self->pc, &iluname); - fprintf(fp, "Global Preconditioner: %s\n", name); - fprintf(fp, "Local Preconditioner: %s\n", iluname); - - parms_ViewerGetFP(v, &fp); - - return 0; -} - -static int pbicgstab_setksize(parms_Solver self, int restart) -{ - /*dummy*/ - return 0; -} - -static int pbicgstab_setneig(parms_Solver self, int neigs) -{ - /*dummy*/ - return 0; -} - -static struct parms_Solver_ops parms_pbicgstab_sol = { - parms_pbicgstab, - pbicgstab_getresidual, - pbicgstab_getresidualnorm2, - pbicgstab_setksize, - pbicgstab_setneig, - pbicgstab_free, - pbicgstab_view -}; - - -/** Create the PBICGS solver. - * - * \param self A parms_Solver object. - * \return 0 on success. - */ -int pbicgstab_create(parms_Solver self) -{ - PARMS_MEMCPY(self->ops, &parms_pbicgstab_sol, 1); - return 0; -} diff --git a/lib/parms/src/pbicgstab_ras.c b/lib/parms/src/pbicgstab_ras.c deleted file mode 100644 index b1044add6..000000000 --- a/lib/parms/src/pbicgstab_ras.c +++ /dev/null @@ -1,381 +0,0 @@ -/*-------------------------------------------------------------------- - pbicgstabras_create : create the PBICGS_RAS solver. - pbicgstabras_free : free the memory for the PBICGS_RAS solver. - pbicgstabras_view : dump the PBICGS_RAS solver. - parms_pbicgstabras : the PBICGS_RAS solve function. - - Pipelined BiCGstab, overlapping global sums with computation. See: - Siegfried Cools, Wim Vanroose - The communication-hiding pipelined BiCGStab method for the parallel solution of large unsymmetric linear systems - Parallel Computing 65, pp. 1-20, July 2017 - ------------------------------------------------------------------*/ - -#if defined(__ICC) -#include -#else -#if defined(C99) -#include -#else -#include -#endif -#endif -#include "parms_vec.h" -#include "parms_mat.h" -#include "parms_pc.h" -#include "./include/parms_solver_impl.h" - -#include "./include/parms_comm_impl.h" -#include "./include/parms_pc_impl.h" -#include "./include/parms_opt_impl.h" - -typedef struct ras_data { - parms_Operator op; - parms_Comm handler; - parms_Map is; - BOOL issetup; - FLOAT *rbuf; - int nloc; - int n; - int nodv; - int nsend; -} *ras_data; - - -#define DBL_EPSILON 2.2204460492503131e-16 // double epsilon - -typedef struct pbicgs_data { - int neigs; -} *pbicgstabras_data; - -#define TINY 1.0e-20 -int parms_pbicgstabras(parms_Solver self, double *rhs, double *xin) -{ - - int i, its; - int maxits, nloc, n_ext, one = 1; - double tmp[5]; - double *b, *r0, *r, *rt, *t, *pt, *s, *st; - double *q, *qt, *v, *x, *y; - double *z_ext, *zt_ext, *w_ext, *wt_ext; - double tol, alpha, beta, omega, myres, rho, rho_alt; - MPI_Request req, req1, req2; - int nodv, nsend; - - parms_Mat A; - parms_PC pc; - parms_Map is; - parms_Viewer viewer; - parms_Comm pc_handler; - ras_data pc_data; - - int rank; - MPI_Comm comm; - - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - - A = self->A; - pc = self->pc; - is = A->is; - - maxits = self->maxits; - tol = self->tol*self->tol; - nloc = parms_MapGetLocalSize(is); - comm = is->comm; - - // For inlined RAS preconditioner - pc_data = (ras_data)pc->data; - n_ext = pc_data->n; // local size with halo for Schwarz - pc_handler = pc_data->handler; - nsend = pc_data->nsend; - nodv = pc_data->nodv; - -/*----- allocate memory for working local arrays -------*/ -/*----- initialize to zero if not initialized before the iteration starts ---*/ - - PARMS_NEWARRAY(b, nloc); - PARMS_NEWARRAY(r0, nloc); - PARMS_NEWARRAY(r, nloc); for (i=0; iisperm) { - for (i = 0; i < nloc; i++) { - x[is->perm[i]] = xin[i]; - b[is->perm[i]] = rhs[i]; - } - is->isvecperm = true; - } - - - /* compute residual vector r0 = b - Ax */ - /* b = y (permuted), t=Ax temporary vector */ - parms_MatVec(A, x, t); - for (i=0; iisserial == false) - MPI_Iallreduce(MPI_IN_PLACE, tmp, 2, MPI_DOUBLE, MPI_SUM, comm, &req); - - parms_PCApply(pc, w_ext, wt_ext); // wt = M w - parms_MatVec(A, wt_ext, t); // t = Awt - - omega = 0.; - beta = 0.; - - if(is->isserial == false) MPI_Wait(&req, MPI_STATUS_IGNORE); - - rho_alt = tmp[0]; - alpha = tmp[0] / tmp[1]; // alpha = (r0,r0) / (r0,w) - - - for (its = 0; its < maxits; its++){ - - for (i=0; in - pc_data->nloc) - PARMS_MEMCPY(&z_ext[nloc], pc_data->rbuf, pc_data->n - pc_data->nloc); - - tmp[0] = 0.; tmp[1] = 0.; - for (i=0; iisserial == false) - MPI_Iallreduce(MPI_IN_PLACE, tmp, 2, MPI_DOUBLE, MPI_SUM, comm, &req1); - - /* === Preconditioner RAS parms_PCApply(pc, z, zt); === */ - - /* solve the extended linear system */ - parms_OperatorApply(pc_data->op, z_ext, zt_ext); - - /* No halo exchange of the solution, that's the "Restricted" in RAS */ - /* === End Preconditioner === */ - - parms_MatVec(A, zt_ext, v); - - if(is->isserial == false) MPI_Wait(&req1, MPI_STATUS_IGNORE); - - omega = tmp[0]/tmp[1]; - - for (i=0; in - pc_data->nloc) - PARMS_MEMCPY(&w_ext[nloc], pc_data->rbuf, pc_data->n - pc_data->nloc); - - tmp[0] = 0.; tmp[1] = 0.; tmp[2] = 0.; tmp[3] = 0.; tmp[4] = 0.; - for (i=0; iisserial == false) - MPI_Iallreduce(MPI_IN_PLACE, tmp, 5, MPI_DOUBLE, MPI_SUM, comm, &req2); - - /* === Preconditioner RAS: parms_PCApply(pc, w, wt); === */ - - - parms_OperatorApply(pc_data->op, w_ext, wt_ext); - - /* === End Preconditioner === */ - - parms_MatVec(A, wt_ext, t); - - if(is->isserial == false) MPI_Wait(&req2, MPI_STATUS_IGNORE); - - // Convergence check: - if ( tmp[4] < tol) break; - - rho = tmp[0]; - beta = alpha*rho / (omega*rho_alt); - rho_alt = rho; - alpha = rho / (tmp[1] + beta*(tmp[2] - omega*tmp[3])); - - //pbicgstabras_getresidualnorm2(self, b, x, &myres); - //if(rank == 0) printf("nparms check at %d, myres=%e, (r,r)=%e\n",its,myres, tmp[4]); - - } - - - its++; - if(rank == 0){ - if(its == maxits) { - printf("ERROR: no convergence\n"); - } else { - printf("Parms iterations, residuum %d, %e\n",its,sqrt(tmp[4])); - } - } - /* reset isvecperm and do inverse permutation*/ - if (is->isperm) { - for (i = 0; i < nloc; i++) { - xin[is->iperm[i]] = x[i]; - } - is->isvecperm = false; - } - - - free(b); - free(r0); - free(r); - free(rt); - // free(wt); - free(t); - free(pt); - free(s); - free(st); - // free(zt); - free(q); - free(qt); - free(y); - free(v); - free(x); - free(z_ext); - free(zt_ext); - free(w_ext); - free(wt_ext); - - self->its = its; - return 0; -} - -static int pbicgstabras_getresidual(parms_Solver self, double *y, double *x, double *res) -{ - - parms_Mat A; - - A = self->A; - - parms_MatMVPY(A, -1.0, x, 1.0, y, res); - - return 0; -} - -static int pbicgstabras_getresidualnorm2(parms_Solver self, double *y, double *x, double *rnorm) -{ - - int nloc; - double *res; - parms_Mat A; - parms_Map is; - - A = self->A; - is = A->is; - nloc = parms_MapGetLocalSize(is); - PARMS_NEWARRAY(res, nloc); - - parms_MatMVPY(A, -1.0, x, 1.0, y, res); - - parms_VecGetNorm2(res, rnorm, is); - - PARMS_FREE(res); - - return 0; -} - - -static int pbicgstabras_free(parms_Solver *self) -{ - /*dummy*/ - return 0; -} - -static int pbicgstabras_view(parms_Solver self, parms_Viewer v) -{ - FILE *fp; - char *name, *iluname; - int dim; - parms_ViewerGetFP(v, &fp); - fprintf(fp,"\n=============================\n"); - fprintf(fp," Solver Parameters \n"); - fprintf(fp,"=============================\n"); - - fprintf(fp, "Solver type = Pipelined BiConjugate Gradient Stabilized Method (PBICGS_RAS) \n"); - fprintf(fp, " with hard coded global preconditioner RAS \n"); - - fprintf(fp, "maxits = %d \n", self->maxits); - fprintf(fp, "Relative tolerance = %-8.2e \n", self->tol); - - parms_PCILUGetName(self->pc, &iluname); - - fprintf(fp, "Local Preconditioner: %s\n", iluname); - - parms_ViewerGetFP(v, &fp); - - return 0; -} - -static int pbicgstabras_setksize(parms_Solver self, int restart) -{ - /*dummy*/ - return 0; -} - -static int pbicgstabras_setneig(parms_Solver self, int neigs) -{ - /*dummy*/ - return 0; -} - -static struct parms_Solver_ops parms_pbicgstabras_sol = { - parms_pbicgstabras, - pbicgstabras_getresidual, - pbicgstabras_getresidualnorm2, - pbicgstabras_setksize, - pbicgstabras_setneig, - pbicgstabras_free, - pbicgstabras_view -}; - - -/** Create the PBICGS_RAS solver. - * - * \param self A parms_Solver object. - * \return 0 on success. - */ -int pbicgstabras_create(parms_Solver self) -{ - PARMS_MEMCPY(self->ops, &parms_pbicgstabras_sol, 1); - return 0; -} diff --git a/mesh_part/CMakeLists.txt b/mesh_part/CMakeLists.txt index 24df289bd..d0c78e881 100644 --- a/mesh_part/CMakeLists.txt +++ b/mesh_part/CMakeLists.txt @@ -12,7 +12,7 @@ add_subdirectory(${src_home}/../lib/metis-5.1.0 ${PROJECT_BINARY_DIR include_directories(${src_home}/../lib/metis-5.1.0/include) add_library(${PROJECT_NAME}_C ${sources_C}) -target_compile_definitions(${PROJECT_NAME}_C PRIVATE PARMS USE_MPI REAL=double DBL HAS_BLAS FORTRAN_UNDERSCORE VOID_POINTER_SIZE_8 SGI LINUX UNDER_ MPI2) +target_compile_definitions(${PROJECT_NAME}_C PRIVATE USE_MPI REAL=double DBL HAS_BLAS FORTRAN_UNDERSCORE VOID_POINTER_SIZE_8 SGI LINUX UNDER_ MPI2) target_link_libraries(${PROJECT_NAME}_C metis) add_executable(${PROJECT_NAME} ${sources_Fortran}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 509e899a4..fbf37a83f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -134,8 +134,6 @@ if(ENABLE_MULTIO) list(APPEND sources_Fortran ${src_home}/ifs_interface/iom.F90) endif() -file(GLOB sources_C ${src_home}/*.c) - # generate a custom file from fesom_version_info.F90 which includes the current git SHA set(FESOM_ORIGINAL_VERSION_FILE ${src_home}/fesom_version_info.F90) set(FESOM_GENERATED_VERSION_FILE ${CMAKE_CURRENT_BINARY_DIR}/fesom_version_info-generated.F90) @@ -152,7 +150,6 @@ add_custom_target(fesom_version_info-generated.F90 ALL # list(REMOVE_ITEM sources_Fortran ${src_home}/cpl_driver.F90) #endif() list(REMOVE_ITEM sources_Fortran ${src_home}/fvom_init.F90 ${src_home}/oce_local.F90 ${src_home}/gen_comm.F90) -list(REMOVE_ITEM sources_C ${src_home}/fort_part.c) list(REMOVE_ITEM sources_Fortran ${src_home}/fesom_main.F90) find_package( NETCDF REQUIRED COMPONENTS C Fortran ) @@ -161,26 +158,17 @@ find_package( MPI REQUIRED COMPONENTS C Fortran ) # depends on the metis library #add_subdirectory(../lib/metis-5.1.0 ${PROJECT_BINARY_DIR}/metis) #include_directories(../lib/metis-5.1.0/include) -# depends on the parms library -add_subdirectory(../lib/parms ${PROJECT_BINARY_DIR}/parms) add_subdirectory(async_threads_cpp) -add_library(${PROJECT_NAME}_C OBJECT ${sources_C}) -target_compile_definitions(${PROJECT_NAME}_C PRIVATE PARMS USE_MPI REAL=double DBL HAS_BLAS FORTRAN_UNDERSCORE VOID_POINTER_SIZE_8 SGI LINUX UNDER_ MPI2) - -target_link_libraries(${PROJECT_NAME}_C PRIVATE parms) #metis -target_link_libraries(${PROJECT_NAME}_C PRIVATE MPI::MPI_C) - # fesom library -add_library(${PROJECT_NAME} ${sources_Fortran} $ $) +add_library(${PROJECT_NAME} ${sources_Fortran} $) add_dependencies(${PROJECT_NAME} fesom_version_info-generated.F90) -target_compile_definitions(${PROJECT_NAME} PRIVATE PARMS -DMETIS_VERSION=5 -DPART_WEIGHTED -DMETISRANDOMSEED=35243) +target_compile_definitions(${PROJECT_NAME} PRIVATE -DMETIS_VERSION=5 -DPART_WEIGHTED -DMETISRANDOMSEED=35243) target_include_directories(${PROJECT_NAME} PUBLIC $) target_include_directories(${PROJECT_NAME} PUBLIC $) -target_link_libraries(${PROJECT_NAME} PRIVATE parms) #metis target_link_libraries(${PROJECT_NAME} PRIVATE MPI::MPI_Fortran) set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE Fortran) @@ -286,7 +274,6 @@ target_include_directories(${PROJECT_NAME} PRIVATE ${NETCDF_Fortran_INCLUDE_DIRE target_include_directories(${PROJECT_NAME} PRIVATE ${MCT_Fortran_INCLUDE_DIRECTORIES} ${MPEU_Fortran_INCLUDE_DIRECTORIES}) target_include_directories(${PROJECT_NAME} PRIVATE ${SCRIP_Fortran_INCLUDE_DIRECTORIES}) target_include_directories(${PROJECT_NAME} PRIVATE ${YAC_Fortran_INCLUDE_DIRECTORIES} ${YAXT_Fortran_INCLUDE_DIRECTORIES}) -target_link_libraries(${PROJECT_NAME} PRIVATE ${PROJECT_NAME}_C) target_link_libraries(${PROJECT_NAME} PRIVATE ${NETCDF_Fortran_LIBRARIES} ${NETCDF_C_LIBRARIES} ${OASIS_Fortran_LIBRARIES}) target_link_libraries(${PROJECT_NAME} PRIVATE ${MCT_Fortran_LIBRARIES} ${MPEU_Fortran_LIBRARIES} ${SCRIP_Fortran_LIBRARIES}) @@ -316,4 +303,4 @@ target_link_libraries(${PROJECT_NAME}.x PUBLIC ${PROJECT_NAME}) ### Export and installation -fesom_export(TARGETS ${PROJECT_NAME} parms fesom.x) +fesom_export(TARGETS ${PROJECT_NAME} fesom.x) diff --git a/src/MOD_DYN.F90 b/src/MOD_DYN.F90 index 76b62d06e..b77437bbf 100644 --- a/src/MOD_DYN.F90 +++ b/src/MOD_DYN.F90 @@ -17,14 +17,7 @@ MODULE MOD_DYN integer :: fillin = 3 integer :: lutype = 2 real(kind=WP) :: droptol = 1.e-8 -!!! PARMS Solver - real(kind=WP) :: soltol = 1e-10 ! default for PARMS - logical :: use_parms = .TRUE. -!!! -!!! Sergey's Solver -! real(kind=WP) :: soltol = 1e-5 ! default for PARMS -! logical :: use_parms = .FALSE. -!!! + real(kind=WP) :: soltol = 1e-5 real(kind=WP), allocatable :: rr(:), zz(:), pp(:), App(:) contains procedure WRITE_T_SOLVERINFO diff --git a/src/info_module.F90 b/src/info_module.F90 index 57e3eebb7..ad6b86707 100644 --- a/src/info_module.F90 +++ b/src/info_module.F90 @@ -62,11 +62,6 @@ subroutine print_definitions() #else print '(g0)', 'OMP_MAX_THREADS is OFF' #endif -#ifdef PARMS - print '(g0)', 'PARMS is ON' -#else - print '(g0)', 'PARMS is OFF' -#endif #ifdef PETSC print '(g0)', 'PETSC is ON' #else diff --git a/src/oce_ale.F90 b/src/oce_ale.F90 index 4b3bb379b..dbdb8259e 100644 --- a/src/oce_ale.F90 +++ b/src/oce_ale.F90 @@ -2622,43 +2622,19 @@ subroutine solve_ssh_ale(dynamics, partit, mesh) USE MOD_DYN use g_comm_auto use g_config, only: which_ale - use iso_c_binding, only: C_INT, C_DOUBLE use ssh_solve_preconditioner_interface use ssh_solve_cg_interface implicit none -#include "fparms.h" type(t_dyn) , intent(inout), target :: dynamics type(t_partit), intent(inout), target :: partit type(t_mesh) , intent(inout), target :: mesh !___________________________________________________________________________ logical, save :: lfirst=.true. - integer(kind=C_INT) :: n3, reuse, new_values integer :: n - !___________________________________________________________________________ - ! interface for solver - interface - subroutine psolver_init(ident, SOL, PCGLOB, PCLOC, lutype, & - fillin, droptol, maxiter, restart, soltol, & - part, rowptr, colind, values, reuse, MPI_COMM) bind(C) - use iso_c_binding, only: C_INT, C_DOUBLE - integer(kind=C_INT) :: ident, SOL, PCGLOB, PCLOC, lutype, & - fillin, maxiter, restart, & - part(*), rowptr(*), colind(*), reuse, MPI_COMM - real(kind=C_DOUBLE) :: droptol, soltol, values(*) - end subroutine psolver_init - end interface - interface - subroutine psolve(ident, ssh_rhs, values, d_eta, newvalues) bind(C) - use iso_c_binding, only: C_INT, C_DOUBLE - integer(kind=C_INT) :: ident, newvalues - real(kind=C_DOUBLE) :: values(*), ssh_rhs(*), d_eta(*) - end subroutine psolve - end interface - !___________________________________________________________________________ ! pointer on necessary derived types - real(kind=C_DOUBLE), pointer :: droptol, soltol - integer(kind=C_INT), pointer :: maxiter, restart, lutype, fillin, ident + real(kind=WP), pointer :: droptol, soltol + integer, pointer :: maxiter, restart, lutype, fillin, ident #include "associate_part_def.h" #include "associate_mesh_def.h" #include "associate_part_ass.h" @@ -2671,57 +2647,10 @@ end subroutine psolve droptol => dynamics%solverinfo%droptol soltol => dynamics%solverinfo%soltol - if (.not. dynamics%solverinfo%use_parms) then - if (lfirst) call ssh_solve_preconditioner(dynamics%solverinfo, partit, mesh) - call ssh_solve_cg(dynamics%d_eta, dynamics%ssh_rhs, dynamics%solverinfo, partit, mesh) - call exchange_nod(dynamics%d_eta, partit) !is this required after calling psolve ? - lfirst=.false. - return - end if - - !___________________________________________________________________________ - if (trim(which_ale)=='linfs') then - reuse=0 - new_values=0 - else - reuse=1 ! For varying coefficients, set reuse=1 - new_values=1 !PS 1 ! and new_values=1, as soon as the coefficients have changed - end if - - ! reuse=0: matrix remains static - ! reuse=1: keeps a copy of the matrix structure to apply scaling of the matrix fast - - ! new_values=0: matrix coefficients unchanged (compared to the last call of psolve) - ! new_values=1: replaces the matrix values (keeps the structure and the preconditioner) - ! new_values=2: replaces the matrix values and recomputes the preconditioner (keeps the structure) - - ! new_values>0 requires reuse=1 in psolver_init! - - ! - !___________________________________________________________________________ - if (lfirst) then - ! Set SOLCG for CG solver (symmetric, positiv definit matrices only, no precond available!!) - ! SOLBICGS for BiCGstab solver (arbitrary matrices) - ! SOLBICGS_RAS for BiCGstab solver (arbitrary matrices) with integrated RAS - the global - ! preconditioner setting is ignored! It saves a 4 vector copies per iteration - ! compared to SOLBICGS + PCRAS. - ! SOLPBICGS for pipelined BiCGstab solver (arbitrary matrices) - ! Should scale better than SOLBICGS, but be careful, it is still experimental. - ! SOLPBICGS_RAS is SOLPBICGS with integrated RAS (global preconditioner setting is ignored!) - ! for even better scalability, well, in the end, it does not matter much. - call psolver_init(ident, SOLBICGS_RAS, PCRAS, PCILUK, lutype, & - fillin, droptol, maxiter, restart, soltol, & - part-1, ssh_stiff%rowptr(:)-ssh_stiff%rowptr(1), & - ssh_stiff%colind-1, ssh_stiff%values, reuse, MPI_COMM_FESOM) - lfirst=.false. - end if - ! - !___________________________________________________________________________ - call psolve(ident, dynamics%ssh_rhs, ssh_stiff%values, dynamics%d_eta, new_values) - - ! - !___________________________________________________________________________ + if (lfirst) call ssh_solve_preconditioner(dynamics%solverinfo, partit, mesh) + call ssh_solve_cg(dynamics%d_eta, dynamics%ssh_rhs, dynamics%solverinfo, partit, mesh) call exchange_nod(dynamics%d_eta, partit) !is this required after calling psolve ? + lfirst=.false. end subroutine solve_ssh_ale ! diff --git a/src/psolve.c b/src/psolve.c deleted file mode 100644 index a8a7b8ac3..000000000 --- a/src/psolve.c +++ /dev/null @@ -1,263 +0,0 @@ -#ifdef PARMS - -#include -#include -#include -#include -#include -#include "psolve.h" - -#define NSOL 10 - -psolver solvers[NSOL]; -int solv_id[12] = {0}; -int nsolver = 0; - -void psolver_init(int *id, SOLVERTYPE *stype, PCTYPE *pctype, PCILUTYPE *pcilutype, - int *ilulevel, int *fillin, double *droptol, int *maxits, int *restart, double *soltol, - int *part, int *rptr, int *cols, double *vals, int *reuse, MPI_Fint *fcomm) -// int *part, int *rptr, int *cols, double *vals, int *reuse, MPI_Comm *comm) -{ - - parms_Viewer v; - int i, j, k, nloc, pid, nproc; - int *ncnts, *idxn, *rp=NULL, *r=NULL, *c=NULL, nmb; - double tmp, *scale, *values=NULL; - MPI_Comm comm; - - parms_Map map; - parms_Mat A; - parms_PC pc; - parms_Solver ksp; - - psolver solver; - comm=MPI_Comm_f2c(*fcomm); - MPI_Comm_rank(comm,&pid); - MPI_Comm_size(comm,&nproc); - solver = malloc(sizeof(*solver)); - nmb = part[nproc]-part[0]; - - nloc = 0; - if(nproc > 1) { - nloc = part[pid+1]-part[pid]; - parms_MapCreateFromPetsc(&map, nloc, nmb, comm); - idxn = malloc(nloc*sizeof(int)); - parms_MapGetGlobalIndices(map, idxn); - } - else { - parms_MapCreateFromLocal(&map,nmb,0); - nloc = nmb; - idxn = malloc(nloc*sizeof(int)); - for(i = 0; i < nloc; i++) - idxn[i] = i; - } - solver->reuse = *reuse; - - scale = malloc(nloc*sizeof(double)); - values = malloc(rptr[nloc]*sizeof(double)); - for(i = 0; i < nloc; i++){ - tmp = 0.; - for(j = rptr[i]; j < rptr[i+1]; j++) - tmp += fabs(vals[j]); - scale[i] = 1./tmp; - for(j = rptr[i]; j < rptr[i+1]; j++) - values[j] = vals[j]*scale[i]; - } - solver->scale = scale; - - // create Mat - parms_MatCreate(&A,map); - parms_MatSetValues(A, nloc, idxn, rptr, cols, values, INSERT); - parms_MatSetup(A); - - // create PC/Solver & set parameters - parms_PCCreate(&pc,A); - set_pc_params(pc, *pctype, *pcilutype, *ilulevel, *fillin, *droptol); - parms_PCSetup(pc); - parms_SolverCreate(&ksp,A,pc); - set_solver_params(ksp, *stype, *maxits, *restart, *soltol); - - solver->ksp = ksp; - solver->map = map; - - if(solver->reuse){ - r = malloc((nloc)*sizeof(int)); - for(i = 0; i < nloc; i++) - r[i] = idxn[i]; - solver->rows = r; - - rp = malloc((nloc+1)*sizeof(int)); - for(i = 0; i < nloc+1; i++) - rp[i] = rptr[i]; - solver->rptr = rp; - - c = malloc(rptr[nloc]*sizeof(int)); - for(i = 0; i < rptr[nloc]; i++) - c[i] = cols[i]; - solver->cols = c; - solver->vals = values; - } - else{ - if(!r) - free(r); - if(!rp) - free(rp); - if(!c) - free(c); - if(!values) - free(values); - } - - - solv_id[*id] = nsolver; - solvers[nsolver++] = solver; - -} - -void psolver_final() -{ - - int pid, i; - parms_Solver ksp; - parms_PC pc; - parms_Mat A; - parms_Map map; - psolver solver; - - for(i = 0; i < nsolver; i++){ - solver = solvers[i]; - - ksp = solver->ksp; - parms_SolverGetPC(ksp, &pc); - parms_SolverGetMat(ksp,&A); - map = solver->map; - - parms_SolverFree(&ksp); - parms_PCFree(&pc); - parms_MatFree(&A); - parms_MapFree(&map); - - if(solver->reuse){ - free(solver->rptr); - free(solver->rows); - free(solver->cols); - free(solver->vals); - } - free(solver->scale); - - free(solver); - } -} - -void psolve(int *id, double *rhs, double *vals, double *sol, int *new) -{ - - parms_Viewer v; - psolver solver; - parms_Map map; - parms_Mat A; - parms_PC pc; - parms_Solver ksp; - - double resnorm; - double *x,*y, *scale, *values, tmp; - double t0,t1,t2,toh; - int *rptr, *cols; - int its,err,i,j,k, cnt, nloc, pid, sid; - - sid = solv_id[*id]; - - solver = solvers[sid]; - ksp = solver->ksp; - map = solver->map; - nloc = parms_MapGetLocalSize(map); - - scale = solver->scale; - - if(*new) { - if(solver->reuse){ - parms_SolverGetPC(ksp, &pc); - parms_SolverGetMat(ksp, &A); - - rptr = solver->rptr; - cols = solver->cols; - - values = solver->vals; - for(i = 0; i < nloc; i++){ - tmp = 0.; - for(j = rptr[i]; j < rptr[i+1]; j++) - tmp += fabs(vals[j]); - scale[i] = 1./tmp; - for(j = rptr[i]; j < rptr[i+1]; j++) - values[j] = vals[j]*scale[i]; - } - - // create Mat & set values - parms_MatReset(A,SAME_NONZERO_STRUCTURE); - parms_MatSetValues(A, nloc, solver->rows, rptr, cols, values, INSERT); - parms_MatSetup(A); - - if (*new==2) parms_PCSetup(pc); - } - else - printf("ERROR: matrix data is static\n"); - } - - x = sol; - y = malloc(nloc*sizeof(double)); - for(i = 0; i < nloc; i++) - y[i] = rhs[i]*scale[i]; - - // solve system of equations - parms_SolverApply(ksp,y,x); - -/* - // get trueresidual and number of iterations - parms_SolverGetResidualNorm2(ksp,y,x,&resnorm); - its = parms_SolverGetIts(ksp); - printf("%e %d\n", resnorm, its); -*/ - free(y); -} - -int set_pc_params(parms_PC pc, PCTYPE pctype, PCILUTYPE pcilutype, - int ilulevel, int fillin, double droptol){ - - int i, lfil[7]; - double dtol[7]; - - for(i = 0; i < 7; i++){ - lfil[i] = fillin; - dtol[i] = droptol; - } - - parms_PCSetType(pc, pctype); - parms_PCSetILUType(pc, pcilutype); - parms_PCSetNlevels(pc, ilulevel); - parms_PCSetFill(pc, lfil); - parms_PCSetTol(pc, dtol); - - return 0; -} - - -int set_solver_params(parms_Solver solver, SOLVERTYPE solvertype, - int maxits, int restart, double soltol){ - char buf[100]; - - parms_SolverSetType(solver, solvertype); - - sprintf(buf, "%d", maxits); - parms_SolverSetParam(solver, MAXITS, buf); - - sprintf(buf, "%d", restart); - parms_SolverSetParam(solver, KSIZE, buf); - - sprintf(buf, "%g", soltol); - parms_SolverSetParam(solver, DTOL, buf); - - return 0; -} - - -#endif diff --git a/src/psolve.h b/src/psolve.h deleted file mode 100644 index 60bdda648..000000000 --- a/src/psolve.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef __PSOLVE__ -#define __PSOLVE__ - -#include "parms.h" -#include "parms_sys.h" - -typedef struct psolver{ - - int reuse; - - int *rows; - int *rptr; - int *cols; - double *vals; - double *scale; - parms_Solver ksp; - parms_Map map; - -} *psolver; - -int set_pc_params(parms_PC pc, PCTYPE pctype, PCILUTYPE pcilutype, - int ilulevel, int fillin, double droptol); - -int set_solver_params(parms_Solver solver, SOLVERTYPE solvertype, - int maxits, int restart, double soltol); - -#endif From 26ad76291d4ddd76110cae796b76a7b6135bfe22 Mon Sep 17 00:00:00 2001 From: Jan Streffing Date: Fri, 26 Jul 2024 08:13:06 +0200 Subject: [PATCH 2/4] update ci test truth after removal of parms solver --- setups/test_pi/setup.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/setups/test_pi/setup.yml b/setups/test_pi/setup.yml index aa74a7996..a2b9bd4f0 100644 --- a/setups/test_pi/setup.yml +++ b/setups/test_pi/setup.yml @@ -57,12 +57,12 @@ namelist.io: prec: 8 fcheck: - a_ice: 0.2692498167543513 - salt: 23.944089812055452 - sst: 8.526792796340805 - temp: 1.7018189804276316 - u: -0.0014310701355284717 - v: 0.00014314237674481877 + a_ice: 0.2692498167586123 + salt: 23.944089812053637 + sst: 8.526792796322084 + temp: 1.7018189804265962 + u: -0.001431070160064853 + v: 0.00014314235109323336 From c3b0bb093f14c1cc97009efac72b71b79205507a Mon Sep 17 00:00:00 2001 From: Jan Streffing Date: Fri, 26 Jul 2024 08:20:21 +0200 Subject: [PATCH 3/4] update channel setup ci test truth after removal of parms solver --- setups/test_souf/setup.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setups/test_souf/setup.yml b/setups/test_souf/setup.yml index bc82f05de..83905007f 100644 --- a/setups/test_souf/setup.yml +++ b/setups/test_souf/setup.yml @@ -86,5 +86,5 @@ fcheck: temp: 14.329708416524904 sst: 18.939699613817496 u: 0.0274316731683607 - v: -0.0008870790518593145 + v: -0.0008870790413357777 From cdc330938945efa9611f5cd98682e596c0e2d0d2 Mon Sep 17 00:00:00 2001 From: Jan Streffing Date: Fri, 26 Jul 2024 08:30:57 +0200 Subject: [PATCH 4/4] update channel setup ci test truth after removal of parms solver --- setups/test_souf/setup.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setups/test_souf/setup.yml b/setups/test_souf/setup.yml index 83905007f..2e55fa562 100644 --- a/setups/test_souf/setup.yml +++ b/setups/test_souf/setup.yml @@ -83,8 +83,8 @@ namelist.io: fcheck: salt: 35.0 - temp: 14.329708416524904 - sst: 18.939699613817496 - u: 0.0274316731683607 + temp: 14.329708416524543 + sst: 18.939699613817673 + u: 0.02743167316313434 v: -0.0008870790413357777