diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8083995f..b5ad6f13 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -15,8 +15,8 @@ push_github_sourceforge: - git remote update - git checkout master - git pull origin master - - git push sourceforge master - - git push github master + - git push sourceforge master --tags + - git push github master --tags only: - master @@ -55,6 +55,8 @@ build_win: cmake %SRC_DIR% -G Ninja -DCMAKE_INSTALL_PREFIX=%INSTALL_DIR% + -DCHECK_RANGE=ON + -DUSE_CGNS=ON -DUSE_OCC=ON -DOCC_LIBRARY=C:/install_opencascade_7.4.0_static/win64/vc14/lib/TKernel.lib -DOCC_INCLUDE_DIR=C:/install_opencascade_7.4.0_static/inc @@ -68,6 +70,7 @@ test_win: <<: *win stage: test script: + - pip install pytest-check - cd tests\pytest - cd %NETGEN_BUILD_DIR%\netgen - ctest -C Release -V --output-on-failure @@ -237,11 +240,13 @@ build_mac: cmake $SRC_DIR -DCMAKE_INSTALL_PREFIX=$CMAKE_INSTALL_PREFIX -DCMAKE_BUILD_TYPE=Release + -DCHECK_RANGE=ON -DUSE_NATIVE_ARCH=OFF -DUSE_CCACHE=ON -DENABLE_UNIT_TESTS=ON -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DCMAKE_OSX_SYSROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk + -DUSE_CGNS=ON -DUSE_OCC=ON -DOCC_LIBRARY=/usr/local/opt/opencascade-7.4.0/lib/libTKernel.a -DOCC_INCLUDE_DIR=/usr/local/opt/opencascade-7.4.0/include/opencascade diff --git a/CLA.pdf b/CLA.pdf new file mode 100644 index 00000000..389f1dcd Binary files /dev/null and b/CLA.pdf differ diff --git a/CMakeLists.txt b/CMakeLists.txt index 009660aa..8e5ade9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,9 +14,12 @@ option( USE_NATIVE_ARCH "build for native cpu architecture" ON) option( USE_GUI "don't build netgen with GUI" ON ) option( USE_PYTHON "build with python interface" ON ) option( USE_MPI "enable mpi parallelization" OFF ) +option( USE_MPI4PY "enable mpi4py interface" ON ) option( USE_OCC "(not supported) compile with OpenCascade geometry kernel" OFF) option( USE_JPEG "enable snapshots using library libjpeg" OFF ) option( USE_MPEG "enable video recording with FFmpeg, uses libavcodec" OFF ) +option( USE_CGNS "enable CGNS file read/write support" OFF ) +option( USE_NUMA "compile with NUMA-aware code") option( INTEL_MIC "cross compile for intel xeon phi") option( INSTALL_PROFILES "install environment variable settings to /etc/profile.d" OFF ) option( USE_CCACHE "use ccache") @@ -30,11 +33,12 @@ option( BUILD_STUB_FILES "Build stub files for better autocompletion" ON) option( BUILD_FOR_CONDA "Link python libraries only to executables" OFF) option( USE_SUPERBUILD "use ccache" ON) +option( TRACE_MEMORY "Enable memory tracing" OFF) + +set(NG_COMPILE_FLAGS "" CACHE STRING "Additional compile flags") set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_modules") -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - if(APPLE) set(INSTALL_DIR_DEFAULT /Applications/Netgen.app) else(APPLE) @@ -50,14 +54,14 @@ if(INSTALL_DIR) set(INSTALL_DIR_DEFAULT ${INSTALL_DIR}) endif(INSTALL_DIR) -if(UNIX) +if(UNIX AND USE_SUPERBUILD) message("Checking for write permissions in install directory...") execute_process(COMMAND mkdir -p ${CMAKE_INSTALL_PREFIX}) execute_process(COMMAND test -w ${CMAKE_INSTALL_PREFIX} RESULT_VARIABLE res) if(res) message(WARNING "No write access at install directory, please set correct permissions") endif() -endif(UNIX) +endif(UNIX AND USE_SUPERBUILD) if (USE_SUPERBUILD) project (SUPERBUILD) @@ -76,12 +80,10 @@ else() endif() endif() -set(NETGEN_VERSION_MAJOR 6) -set(NETGEN_VERSION_MINOR 2) -string(TIMESTAMP NETGEN_VERSION_PATCH "%y%U%w" ) -set(NETGEN_VERSION "${NETGEN_VERSION_MAJOR}.${NETGEN_VERSION_MINOR}-dev") -set(PACKAGE_VERSION "${NETGEN_VERSION_MAJOR}.${NETGEN_VERSION_MINOR}-${NETGEN_VERSION_PATCH}") -set(CPACK_PACKAGE_VERSION "${PACKAGE_VERSION}") +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +include (${CMAKE_CURRENT_LIST_DIR}/cmake/generate_version_file.cmake) +set(CPACK_PACKAGE_VERSION "${NETGEN_VERSION}") ####################################################################### @@ -193,7 +195,6 @@ check_include_files (dlfcn.h HAVE_DLFCN_H) if(HAVE_DLFCN_H) add_definitions(-DHAVE_DLFCN_H) endif() -add_definitions(-DNETGEN_VERSION="${NETGEN_VERSION}") include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}) @@ -300,6 +301,14 @@ if (USE_MPI) target_include_directories(netgen_metis INTERFACE ${METIS_INCLUDE_DIR}) target_link_libraries(netgen_metis INTERFACE ${METIS_LIBRARY} ) target_compile_definitions(netgen_metis INTERFACE METIS ) + + if(USE_MPI4PY AND USE_PYTHON) + execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import mpi4py;print(mpi4py.get_include())" OUTPUT_VARIABLE mpi4py_path OUTPUT_STRIP_TRAILING_WHITESPACE) + find_path(MPI4PY_INCLUDE_DIR mpi4py.h HINTS ${mpi4py_path}/mpi4py NO_DEFAULT_PATH REQUIRED) + target_include_directories(netgen_metis INTERFACE ${MPI4PY_INCLUDE_DIR}) + target_compile_definitions(netgen_metis INTERFACE NG_MPI4PY ) + message(STATUS "Found mpi4py: ${MPI4PY_INCLUDE_DIR}") + endif(USE_MPI4PY AND USE_PYTHON) endif (USE_MPI) install(TARGETS netgen_mpi netgen_metis ${NG_INSTALL_DIR}) @@ -324,6 +333,12 @@ if (USE_MPEG) include_directories(${FFMPEG_INCLUDE_DIR}) endif (USE_MPEG) +####################################################################### +add_custom_target(ng_generate_version_file + ${CMAKE_COMMAND} + -DBDIR=${CMAKE_CURRENT_BINARY_DIR} + -P ${CMAKE_CURRENT_LIST_DIR}/cmake/generate_version_file.cmake + ) ####################################################################### if(INSTALL_PROFILES) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/netgen.sh "#!/bin/sh\n") @@ -388,12 +403,27 @@ if(ENABLE_CPP_CORE_GUIDELINES_CHECK) endif() endif(ENABLE_CPP_CORE_GUIDELINES_CHECK) +add_library(netgen_cgns INTERFACE) +if(USE_CGNS) + find_library( CGNS_LIBRARY NAMES cgns cgnsdll ) + find_path( CGNS_INCLUDE_DIR cgnslib.h ) + target_compile_definitions(netgen_cgns INTERFACE NG_CGNS) + target_include_directories(netgen_cgns INTERFACE ${CGNS_INCLUDE_DIR}) + target_link_libraries(netgen_cgns INTERFACE ${CGNS_LIBRARY}) + if(NOT WIN32 AND NOT APPLE) # hdf5 is statically linked into cgns in Windows amd MacOS binaries + find_library(HDF5_LIBRARY NAMES hdf5 hdf5_serial) + target_link_libraries(netgen_cgns INTERFACE ${HDF5_LIBRARY}) + endif(NOT WIN32 AND NOT APPLE) +endif(USE_CGNS) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/netgen_version.hpp ${CMAKE_CURRENT_BINARY_DIR}/netgen_config.hpp DESTINATION ${NG_INSTALL_DIR_INCLUDE}/include COMPONENT netgen_devel) + +add_subdirectory(windows) add_subdirectory(libsrc) add_subdirectory(ng) add_subdirectory(tutorials) add_subdirectory(py_tutorials) add_subdirectory(doc) -add_subdirectory(windows) add_subdirectory(nglib) if (USE_PYTHON) add_subdirectory(python) @@ -444,6 +474,8 @@ if(USE_NATIVE_ARCH) else() message(STATUS "Build for generic CPU") endif() + elseif(APPLE AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") + # no flag necessary/available on Apple M1 else() target_compile_options(ngcore PUBLIC "-march=native") endif(WIN32) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..15bb16b2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,18 @@ + +# How to Contribute + +## Reporting issues + +If you have a problem using Netgen/NGSolve consider asking a question in our [forum](https://ngsolve.org/forum). + +If you found a bug create an issue in the [Github Issue Tracker](https://github.com/NGSolve/netgen/issues). Please be as specific as possible, issues with a reproducible minimal failing example will get more attention than unspecific one liners :) + +## Contributing patches + +We love and want to encourage community engagement and will review and accept patches and contributions to this project. There are just a few steps to follow: + +On your first contribution, to clear any legal questions, we ask you to sign our [Contributor License Agreement](CLA.pdf). Generally you have to sign this only once for Netgen or NGSolve. Please send the signed agreement to . + +Place a pull request on GitHub. From there we will pull it into our internal testing environment and, if approved, merge it into the main codebase. + +If you have any questions feel free to ask on the [forum](https://ngsolve.org/forum). diff --git a/cmake/NetgenConfig.cmake.in b/cmake/NetgenConfig.cmake.in index e8262666..58b54055 100644 --- a/cmake/NetgenConfig.cmake.in +++ b/cmake/NetgenConfig.cmake.in @@ -1,4 +1,8 @@ set(NETGEN_VERSION "@NETGEN_VERSION@") +set(NETGEN_VERSION_MAJOR "@NETGEN_VERSION_MAJOR@") +set(NETGEN_VERSION_MINOR "@NETGEN_VERSION_MINOR@") +set(NETGEN_VERSION_PATCH "@NETGEN_VERSION_PATCH@") +set(NETGEN_VERSION_TWEAK "@NETGEN_VERSION_TWEAK@") get_filename_component(NETGEN_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) @@ -27,6 +31,7 @@ set(NETGEN_METIS_LIBRARY "@METIS_LIBRARY@") set(NETGEN_MKL_LIBRARIES "@MKL_LIBRARIES@") set(NETGEN_MPI_CXX_INCLUDE_PATH "@MPI_CXX_INCLUDE_PATH@") set(NETGEN_MPI_CXX_LIBRARIES "@MPI_CXX_LIBRARIES@") +set(NETGEN_NUMA_LIBRARY "@NUMA_LIBRARY@") set(NETGEN_OCC_INCLUDE_DIR "@OCC_INCLUDE_DIR@") set(NETGEN_OCC_LIBRARIES_BIN "@OCC_LIBRARIES_BIN@") set(NETGEN_OCC_LIBRARIES "@OCC_LIBRARIES@") @@ -51,10 +56,12 @@ set(NETGEN_USE_MPI @USE_MPI@) set(NETGEN_USE_OCC @USE_OCC@) set(NETGEN_USE_JPEG @USE_JPEG@) set(NETGEN_USE_MPEG @USE_MPEG@) +set(NETGEN_USE_CGNS @USE_CGNS@) set(NETGEN_INTEL_MIC @INTEL_MIC@) set(NETGEN_INSTALL_PROFILES @INSTALL_PROFILES@) set(NETGEN_USE_CCACHE @USE_CCACHE@) set(NETGEN_USE_NATIVE_ARCH @USE_NATIVE_ARCH@) +set(NETGEN_USE_NUMA @USE_NUMA@) set(NETGEN_PYTHON_RPATH "@NETGEN_PYTHON_RPATH@") set(NETGEN_RPATH_TOKEN "@NG_RPATH_TOKEN@") diff --git a/cmake/SuperBuild.cmake b/cmake/SuperBuild.cmake index a04e4164..6e9600df 100644 --- a/cmake/SuperBuild.cmake +++ b/cmake/SuperBuild.cmake @@ -15,12 +15,12 @@ macro(set_vars VAR_OUT) endforeach() endmacro() ####################################################################### -if(WIN32) - set (DEPS_DOWNLOAD_URL "https://github.com/NGSolve/ngsolve_dependencies/releases/download/v1.0.0" CACHE STRING INTERNAL) - set (OCC_DOWNLOAD_URL_WIN "${DEPS_DOWNLOAD_URL}/occ_win64.zip" CACHE STRING INTERNAL) - set (TCLTK_DOWNLOAD_URL_WIN "${DEPS_DOWNLOAD_URL}/tcltk_win64.zip" CACHE STRING INTERNAL) - set (ZLIB_DOWNLOAD_URL_WIN "${DEPS_DOWNLOAD_URL}/zlib_win64.zip" CACHE STRING INTERNAL) -endif(WIN32) +set (DEPS_DOWNLOAD_URL "https://github.com/NGSolve/ngsolve_dependencies/releases/download/v1.0.0" CACHE STRING INTERNAL) +set (OCC_DOWNLOAD_URL_WIN "${DEPS_DOWNLOAD_URL}/occ_win64.zip" CACHE STRING INTERNAL) +set (TCLTK_DOWNLOAD_URL_WIN "${DEPS_DOWNLOAD_URL}/tcltk_win64.zip" CACHE STRING INTERNAL) +set (ZLIB_DOWNLOAD_URL_WIN "${DEPS_DOWNLOAD_URL}/zlib_win64.zip" CACHE STRING INTERNAL) +set (CGNS_DOWNLOAD_URL_WIN "${DEPS_DOWNLOAD_URL}/cgns_win64.zip" CACHE STRING INTERNAL) +set (CGNS_DOWNLOAD_URL_MAC "${DEPS_DOWNLOAD_URL}/cgns_mac.zip" CACHE STRING INTERNAL) if(UNIX) message("Checking for write permissions in install directory...") @@ -86,6 +86,10 @@ if(USE_GUI) include(cmake/external_projects/tcltk.cmake) endif(USE_GUI) +if(USE_CGNS) + include(cmake/external_projects/cgns.cmake) +endif(USE_CGNS) + ####################################################################### if(USE_MPI) if(UNIX) @@ -128,6 +132,7 @@ set_vars( NETGEN_CMAKE_ARGS USE_OCC USE_MPEG USE_JPEG + USE_CGNS USE_INTERNAL_TCL INSTALL_PROFILES INTEL_MIC @@ -138,8 +143,10 @@ set_vars( NETGEN_CMAKE_ARGS USE_SPDLOG DEBUG_LOG CHECK_RANGE + TRACE_MEMORY BUILD_STUB_FILES BUILD_FOR_CONDA + NG_COMPILE_FLAGS ) # propagate all variables set on the command line using cmake -DFOO=BAR diff --git a/cmake/cmake_modules/FindOpenCasCade.cmake b/cmake/cmake_modules/FindOpenCasCade.cmake index 053cd680..870e8ed5 100644 --- a/cmake/cmake_modules/FindOpenCasCade.cmake +++ b/cmake/cmake_modules/FindOpenCasCade.cmake @@ -25,9 +25,9 @@ else(WIN32) ) endif(WIN32) -if(OCC_LIBRARY) +if(OCC_LIBRARY AND NOT OCC_LIBRARY_DIR) get_filename_component(OCC_LIBRARY_DIR ${OCC_LIBRARY} PATH) -endif(OCC_LIBRARY) +endif(OCC_LIBRARY AND NOT OCC_LIBRARY_DIR) if(OCC_INCLUDE_DIR) file(STRINGS ${OCC_INCLUDE_DIR}/Standard_Version.hxx OCC_MAJOR @@ -89,7 +89,7 @@ if(OCC_VERSION_STRING VERSION_GREATER_EQUAL "7.3.0") endif() foreach( libname ${OCC_LIBRARY_NAMES} ) - find_library( ${libname} ${libname} ${OCC_LIBRARY_DIR} ) + find_library( ${libname} ${libname} ${OCC_LIBRARY_DIR} NO_DEFAULT_PATH) set(OCC_LIBRARIES ${OCC_LIBRARIES} ${${libname}}) endforeach() diff --git a/cmake/external_projects/cgns.cmake b/cmake/external_projects/cgns.cmake new file mode 100644 index 00000000..f7e74987 --- /dev/null +++ b/cmake/external_projects/cgns.cmake @@ -0,0 +1,30 @@ +if(WIN32) + + ExternalProject_Add(project_win_cgns + URL ${CGNS_DOWNLOAD_URL_WIN} + UPDATE_COMMAND "" # Disable update + BUILD_IN_SOURCE 1 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory . ${CMAKE_INSTALL_PREFIX} + LOG_DOWNLOAD 1 + ) + + list(APPEND NETGEN_DEPENDENCIES project_win_cgns) +endif(WIN32) + +if(APPLE) + ExternalProject_Add(project_mac_cgns + URL ${CGNS_DOWNLOAD_URL_MAC} + UPDATE_COMMAND "" # Disable update + BUILD_IN_SOURCE 1 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory . ${CMAKE_INSTALL_PREFIX} + LOG_DOWNLOAD 1 + ) + + list(APPEND NETGEN_DEPENDENCIES project_mac_cgns) + list(APPEND NETGEN_CMAKE_ARGS "-DCGNS_INCLUDE_DIR=${CMAKE_INSTALL_PREFIX}/Contents/Resources/include") + list(APPEND NETGEN_CMAKE_ARGS "-DCGNS_LIBRARY=${CMAKE_INSTALL_PREFIX}/Contents/MacOS/libcgns.dylib") +endif(APPLE) diff --git a/cmake/generate_version_file.cmake b/cmake/generate_version_file.cmake new file mode 100644 index 00000000..92390efc --- /dev/null +++ b/cmake/generate_version_file.cmake @@ -0,0 +1,92 @@ +if(NOT BDIR) + set(BDIR ${CMAKE_CURRENT_BINARY_DIR}) +endif() + +find_package(Git REQUIRED) +execute_process(COMMAND git describe --tags --match "v[0-9]*" --long --dirty WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} OUTPUT_VARIABLE git_version_string RESULT_VARIABLE status ERROR_QUIET) + +if(status AND NOT status EQUAL 0) + if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/../version.txt) + # for source package files (generated for ubuntu builds on launchpad) read the version from version.txt + if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/../version.txt) + file(READ ${CMAKE_CURRENT_LIST_DIR}/../version.txt git_version_string ) + else() + get_filename_component(git_version_string ${CMAKE_CURRENT_LIST_DIR}/.. NAME) + string(REGEX REPLACE "^netgen(.*)" "\\1" git_version_string "${git_version_string}") + endif() + else() + MESSAGE(WARNING "Could not determine git-version from source code - assuming 6.2.0.0") + set(git_version_string "v6.2.0.0") + endif() +endif() + +string(REGEX REPLACE "^v([0-9]+)\\..*" "\\1" NETGEN_VERSION_MAJOR "${git_version_string}") +string(REGEX REPLACE "^v[0-9]+\\.([0-9]+).*" "\\1" NETGEN_VERSION_MINOR "${git_version_string}") +string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" NETGEN_VERSION_PATCH "${git_version_string}") +string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.[0-9]+\\-([0-9]+).*" "\\1" NETGEN_VERSION_TWEAK "${git_version_string}") +string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.[0-9]+\\-[0-9]+\\-([0-9a-z]+).*" "\\1" NETGEN_VERSION_HASH "${git_version_string}") + +set(NETGEN_VERSION_SHORT ${NETGEN_VERSION_MAJOR}.${NETGEN_VERSION_MINOR}.${NETGEN_VERSION_PATCH}) +set(NETGEN_VERSION_LONG ${NETGEN_VERSION_SHORT}-${NETGEN_VERSION_TWEAK}-${NETGEN_VERSION_HASH}) + +if(NETGEN_VERSION_TWEAK) + # no release version - nightly build + set(NETGEN_VERSION ${NETGEN_VERSION_LONG}) +else() + # TWEAK is 0 -> current version has a tag assigned + set(NETGEN_VERSION ${NETGEN_VERSION_SHORT}) +endif() + +set(NETGEN_VERSION_LONG ${NETGEN_VERSION_SHORT}-${NETGEN_VERSION_TWEAK}-${NETGEN_VERSION_HASH}) + +set(version_file ${BDIR}/netgen_version.hpp) +set(new_version_file_string "\ +#ifndef NETGEN_VERSION_HPP_INCLUDED +#define NETGEN_VERSION_HPP_INCLUDED +#define NETGEN_VERSION \"${NETGEN_VERSION}\" +#define NETGEN_VERSION_MAJOR ${NETGEN_VERSION_MAJOR} +#define NETGEN_VERSION_MINOR ${NETGEN_VERSION_MINOR} +#define NETGEN_VERSION_PATCH ${NETGEN_VERSION_PATCH} +#define NETGEN_VERSION_TWEAK ${NETGEN_VERSION_TWEAK} +#define NETGEN_VERSION_HASH \"${NETGEN_VERSION_HASH}\" +#endif // NETGEN_VERSION_HPP_INCLUDED +") +if(EXISTS ${version_file}) + file(READ ${version_file} old_version_file_string ) + if(${old_version_file_string} STREQUAL ${new_version_file_string}) + else() + file(WRITE ${BDIR}/netgen_version.hpp ${new_version_file_string}) + endif() +else() + file(WRITE ${BDIR}/netgen_version.hpp ${new_version_file_string}) +endif() + +file(GENERATE OUTPUT netgen_config.hpp CONTENT +"\ +#ifndef NETGEN_CONFIG_HPP_INCLUDED___ +#define NETGEN_CONFIG_HPP_INCLUDED___ + +#define NETGEN_USE_NATIVE_ARCH $ +#define NETGEN_USE_GUI $ +#define NETGEN_USE_PYTHON $ +#define NETGEN_USE_MPI $ +#define NETGEN_USE_MPI4PY $ +#define NETGEN_USE_OCC $ +#define NETGEN_USE_JPEG $ +#define NETGEN_USE_MPEG $ +#define NETGEN_USE_CGNS $ +#define NETGEN_USE_NUMA $ +#define NETGEN_INTEL_MIC $ +#define NETGEN_INSTALL_PROFILES $ +#define NETGEN_USE_CCACHE $ +#define NETGEN_USE_INTERNAL_TCL $ +#define NETGEN_ENABLE_UNIT_TESTS $ +#define NETGEN_ENABLE_CPP_CORE_GUIDELINES_CHECK $ +#define NETGEN_USE_SPDLOG $ +#define NETGEN_DEBUG_LOG $ +#define NETGEN_USE_CHECK_RANGE $ +#define NETGEN_BUILD_STUB_FILES $ +#define NETGEN_BUILD_FOR_CONDA $ + +#endif // NETGEN_CONFIG_HPP_INCLUDED___ +") diff --git a/external_dependencies/pybind11 b/external_dependencies/pybind11 index deb3cb23..c16da993 160000 --- a/external_dependencies/pybind11 +++ b/external_dependencies/pybind11 @@ -1 +1 @@ -Subproject commit deb3cb238a9f299d7c57f810165e90a1b14b3e6f +Subproject commit c16da993094988141101ac4d96a9b2c92f9ac714 diff --git a/libsrc/core/CMakeLists.txt b/libsrc/core/CMakeLists.txt index cb5eb36c..3d6a438d 100644 --- a/libsrc/core/CMakeLists.txt +++ b/libsrc/core/CMakeLists.txt @@ -11,8 +11,11 @@ add_library(ngcore SHARED table.cpp taskmanager.cpp utils.cpp + version.cpp ) +target_compile_options(ngcore PUBLIC "${NG_COMPILE_FLAGS}") + # Pybind11 2.3 Issue https://github.com/pybind/pybind11/issues/1604 if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") target_compile_options(ngcore PUBLIC -fsized-deallocation -faligned-allocation) @@ -39,6 +42,11 @@ if(CHECK_RANGE OR CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL target_compile_definitions(ngcore PUBLIC NETGEN_ENABLE_CHECK_RANGE) endif(CHECK_RANGE OR CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "DEBUG") +if(TRACE_MEMORY OR CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "DEBUG") + target_compile_definitions(ngcore PUBLIC NETGEN_TRACE_MEMORY) +endif(TRACE_MEMORY OR CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "DEBUG") + + if(USE_SPDLOG) include_directories(${SPDLOG_INCLUDE_DIR}) install(DIRECTORY ${SPDLOG_INCLUDE_DIR} @@ -51,20 +59,29 @@ if(USE_SPDLOG) endif(DEBUG_LOG) endif(USE_SPDLOG) +if(USE_NUMA) + find_library(NUMA_LIBRARY libnuma.so) + target_compile_definitions(ngcore PUBLIC USE_NUMA) + target_link_libraries(ngcore PRIVATE ${NUMA_LIBRARY}) +endif(USE_NUMA) + install(TARGETS ngcore DESTINATION ${NG_INSTALL_DIR} COMPONENT netgen) -target_link_libraries(ngcore PUBLIC netgen_mpi PRIVATE netgen_python ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(ngcore PUBLIC netgen_mpi PRIVATE "$" ${CMAKE_THREAD_LIBS_INIT}) install(FILES ngcore.hpp archive.hpp type_traits.hpp version.hpp ngcore_api.hpp logging.hpp exception.hpp symboltable.hpp paje_trace.hpp utils.hpp profiler.hpp mpi_wrapper.hpp array.hpp taskmanager.hpp concurrentqueue.h localheap.hpp python_ngcore.hpp flags.hpp - xbool.hpp signal.hpp bitarray.hpp table.hpp hashtable.hpp + xbool.hpp signal.hpp bitarray.hpp table.hpp hashtable.hpp ranges.hpp + simd.hpp simd_avx.hpp simd_avx512.hpp simd_generic.hpp simd_sse.hpp simd_arm64.hpp DESTINATION ${NG_INSTALL_DIR_INCLUDE}/core COMPONENT netgen_devel) if(ENABLE_CPP_CORE_GUIDELINES_CHECK) set_target_properties(ngcore PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}") endif(ENABLE_CPP_CORE_GUIDELINES_CHECK) +add_dependencies(ngcore ng_generate_version_file) + if(USE_PYTHON) pybind11_add_module(pyngcore SHARED python_ngcore_export.cpp) target_link_libraries(pyngcore PUBLIC ngcore netgen_python) diff --git a/libsrc/core/archive.cpp b/libsrc/core/archive.cpp index 84e6d875..448b8f47 100644 --- a/libsrc/core/archive.cpp +++ b/libsrc/core/archive.cpp @@ -1,5 +1,6 @@ #include "archive.hpp" +#include "version.hpp" #ifndef WIN32 #include @@ -7,18 +8,6 @@ namespace ngcore { - // clang-tidy should ignore this static object - static std::map library_versions; // NOLINT - std::map& Archive :: GetLibraryVersions() - { - return library_versions; - } - const VersionInfo& GetLibraryVersion(const std::string& library) - { return library_versions[library]; } - - void SetLibraryVersion(const std::string& library, const VersionInfo& version) - { library_versions[library] = version; } - // clang-tidy should ignore this static object static std::unique_ptr> type_register; // NOLINT const detail::ClassArchiveInfo& Archive :: GetArchiveRegister(const std::string& classname) diff --git a/libsrc/core/archive.hpp b/libsrc/core/archive.hpp index 22019302..decf8718 100644 --- a/libsrc/core/archive.hpp +++ b/libsrc/core/archive.hpp @@ -30,9 +30,6 @@ namespace pybind11 namespace ngcore { - // Libraries using this archive can store their version here to implement backwards compatibility - NGCORE_API const VersionInfo& GetLibraryVersion(const std::string& library); - NGCORE_API void SetLibraryVersion(const std::string& library, const VersionInfo& version); class NGCORE_API Archive; @@ -156,6 +153,7 @@ namespace ngcore virtual void NeedsVersion(const std::string& /*unused*/, const std::string& /*unused*/) {} // Pure virtual functions that have to be implemented by In-/OutArchive + virtual Archive & operator & (float & d) = 0; virtual Archive & operator & (double & d) = 0; virtual Archive & operator & (int & i) = 0; virtual Archive & operator & (long & i) = 0; @@ -570,9 +568,10 @@ namespace ngcore virtual void FlushBuffer() {} - protected: - static std::map& GetLibraryVersions(); - + bool parallel = false; + bool IsParallel() const { return parallel; } + void SetParallel (bool _parallel) { parallel = _parallel; } + private: template friend class RegisterClassForArchive; @@ -680,6 +679,8 @@ namespace ngcore BinaryOutArchive& operator=(BinaryOutArchive&&) = delete; using Archive::operator&; + Archive & operator & (float & f) override + { return Write(f); } Archive & operator & (double & d) override { return Write(d); } Archive & operator & (int & i) override @@ -687,7 +688,11 @@ namespace ngcore Archive & operator & (short & i) override { return Write(i); } Archive & operator & (long & i) override - { return Write(i); } + { + // for platform independence + int64_t tmp = i; + return Write(tmp); + } Archive & operator & (size_t & i) override { return Write(i); } Archive & operator & (unsigned char & i) override @@ -725,14 +730,13 @@ namespace ngcore template Archive & Write (T x) { + static_assert(sizeof(T) < BUFFERSIZE, "Cannot write large types with this function!"); if (unlikely(ptr > BUFFERSIZE-sizeof(T))) { stream->write(&buffer[0], ptr); - *reinterpret_cast(&buffer[0]) = x; // NOLINT - ptr = sizeof(T); - return *this; + ptr = 0; } - *reinterpret_cast(&buffer[ptr]) = x; // NOLINT + memcpy(&buffer[ptr], &x, sizeof(T)); ptr += sizeof(T); return *this; } @@ -751,6 +755,8 @@ namespace ngcore : BinaryInArchive(std::make_shared(filename)) { ; } using Archive::operator&; + Archive & operator & (float & f) override + { Read(f); return *this; } Archive & operator & (double & d) override { Read(d); return *this; } Archive & operator & (int & i) override @@ -758,7 +764,12 @@ namespace ngcore Archive & operator & (short & i) override { Read(i); return *this; } Archive & operator & (long & i) override - { Read(i); return *this; } + { + int64_t tmp; + Read(tmp); + i = tmp; + return *this; + } Archive & operator & (size_t & i) override { Read(i); return *this; } Archive & operator & (unsigned char & i) override @@ -815,6 +826,8 @@ namespace ngcore TextOutArchive(std::make_shared(filename)) { } using Archive::operator&; + Archive & operator & (float & f) override + { *stream << f << '\n'; return *this; } Archive & operator & (double & d) override { *stream << d << '\n'; return *this; } Archive & operator & (int & i) override @@ -866,6 +879,8 @@ namespace ngcore : TextInArchive(std::make_shared(filename)) {} using Archive::operator&; + Archive & operator & (float & f) override + { *stream >> f; return *this; } Archive & operator & (double & d) override { *stream >> d; return *this; } Archive & operator & (int & i) override @@ -912,6 +927,55 @@ namespace ngcore } }; + // HashArchive ================================================================= + // This class enables to easily create hashes for archivable objects by xoring + // threw its data + + class NGCORE_API HashArchive : public Archive + { + size_t hash_value = 0; + char* h; + int offset = 0; + public: + HashArchive() : Archive(true) + { h = (char*)&hash_value; } + + using Archive::operator&; + Archive & operator & (float & f) override { return ApplyHash(f); } + Archive & operator & (double & d) override { return ApplyHash(d); } + Archive & operator & (int & i) override { return ApplyHash(i); } + Archive & operator & (short & i) override { return ApplyHash(i); } + Archive & operator & (long & i) override { return ApplyHash(i); } + Archive & operator & (size_t & i) override { return ApplyHash(i); } + Archive & operator & (unsigned char & i) override { return ApplyHash(i); } + Archive & operator & (bool & b) override { return ApplyHash(b); } + Archive & operator & (std::string & str) override + { for(auto c : str) ApplyHash(c); return *this; } + Archive & operator & (char *& str) override + { char* s = str; while(*s != '\0') ApplyHash(*(s++)); return *this; } + + // HashArchive can be used in const context + template + Archive & operator& (const T& val) const + { return (*this) & const_cast(val); } + + size_t GetHash() const { return hash_value; } + + private: + template + Archive& ApplyHash(T val) + { + size_t n = sizeof(T); + char* pval = (char*)&val; + for(size_t i = 0; i < n; i++) + { + h[offset++] ^= pval[i]; + offset %= 8; + } + return *this; + } + }; + } // namespace ngcore #endif // NETGEN_CORE_ARCHIVE_HPP diff --git a/libsrc/core/array.hpp b/libsrc/core/array.hpp index 67b46a72..6d5fdb51 100644 --- a/libsrc/core/array.hpp +++ b/libsrc/core/array.hpp @@ -11,6 +11,7 @@ #include "archive.hpp" #include "exception.hpp" #include "localheap.hpp" +#include "profiler.hpp" #include "utils.hpp" namespace ngcore @@ -39,7 +40,7 @@ namespace ngcore }; template - ostream & operator<< (ostream & ost, Tuple tup) + ostream & operator<< (ostream & ost, Tuple /* tup */) { return ost; } @@ -211,6 +212,20 @@ namespace ngcore template constexpr T IndexBASE () { return T(0); } + + class IndexFromEnd + { + ptrdiff_t i; + public: + constexpr IndexFromEnd (ptrdiff_t ai) : i(ai) { } + IndexFromEnd operator+ (ptrdiff_t inc) const { return i+inc; } + IndexFromEnd operator- (ptrdiff_t dec) const { return i-dec; } + // operator ptrdiff_t () const { return i; } + ptrdiff_t Value() const { return i; } + }; + + constexpr IndexFromEnd END(0); + template class FlatArray; @@ -268,7 +283,7 @@ namespace ngcore NETGEN_INLINE T & First() { return first; } NETGEN_INLINE T & Next() { return next; } NETGEN_INLINE auto Size() const { return next-first; } - NETGEN_INLINE T operator[] (T i) const { return first+i; } + NETGEN_INLINE T operator[] (size_t i) const { return first+i; } NETGEN_INLINE bool Contains (T i) const { return ((i >= first) && (i < next)); } NETGEN_INLINE T_Range Modify(int inc_beg, int inc_end) const { return T_Range(first+inc_beg, next+inc_end); } @@ -559,6 +574,12 @@ namespace ngcore return FlatArray (end-start, data+start); } + /// takes range starting from position start of end-start elements + NETGEN_INLINE FlatArray Range (size_t start, IndexFromEnd indend) const + { + return this->Range(start, size_t(Size()+indend.Value())); + } + /// takes range starting from position start of end-start elements NETGEN_INLINE FlatArray Range (T_Range range) const { @@ -599,9 +620,25 @@ namespace ngcore template FlatArray View (FlatArray fa) { return fa; } + template + auto Max (FlatArray array, T max = std::numeric_limits::min()) -> T + { + for (auto & v : array) + if (v > max) max = v; + return max; + } + + template + auto Min (FlatArray array, T min = std::numeric_limits::max()) -> T + { + for (auto & v : array) + if (v < min) min = v; + return min; + } + /// print array - template - inline ostream & operator<< (ostream & s, const FlatArray & a) + template + inline ostream & operator<< (ostream & s, const FlatArray & a) { for (auto i : a.Range()) s << i << ": " << a[i] << "\n"; @@ -638,6 +675,7 @@ namespace ngcore /// that's the data we have to delete, nullptr for not owning the memory T * mem_to_delete; + using FlatArray::size; using FlatArray::data; using FlatArray::BASE; @@ -682,6 +720,8 @@ namespace ngcore NETGEN_INLINE Array (Array && a2) { + mt.Swap(sizeof(T) * allocsize, a2.mt, sizeof(T) * a2.allocsize); + size = a2.size; data = a2.data; allocsize = a2.allocsize; @@ -696,10 +736,15 @@ namespace ngcore NETGEN_INLINE explicit Array (const Array & a2) : FlatArray (a2.Size(), a2.Size() ? new T[a2.Size()] : nullptr) { - allocsize = size; - mem_to_delete = data; - for (size_t i = 0; i < size; i++) - data[i] = a2.data[i]; + if constexpr (std::is_copy_assignable::value) + { + allocsize = size; + mem_to_delete = data; + for (size_t i = 0; i < size; i++) + data[i] = a2.data[i]; + } + else + throw Exception(std::string("cannot copy-construct Array of type ") + typeid(T).name()); } @@ -747,6 +792,8 @@ namespace ngcore /// if responsible, deletes memory NETGEN_INLINE ~Array() { + if(mem_to_delete) + mt.Free(sizeof(T)*allocsize); delete [] mem_to_delete; } @@ -801,6 +848,8 @@ namespace ngcore /// assigns memory from local heap NETGEN_INLINE const Array & Assign (size_t asize, LocalHeap & lh) { + if(mem_to_delete) + mt.Free(sizeof(T)*allocsize); delete [] mem_to_delete; size = allocsize = asize; data = lh.Alloc (asize); @@ -858,7 +907,7 @@ namespace ngcore size++; } - NETGEN_INLINE Array & operator += (const T & el) + NETGEN_INLINE Array & operator += (const T & el) { Append (el); return *this; @@ -908,6 +957,8 @@ namespace ngcore /// Deallocate memory NETGEN_INLINE void DeleteAll () { + if(mem_to_delete) + mt.Free(sizeof(T)*allocsize); delete [] mem_to_delete; mem_to_delete = NULL; data = 0; @@ -924,16 +975,24 @@ namespace ngcore /// array copy NETGEN_INLINE Array & operator= (const Array & a2) { - SetSize0 (); - SetSize (a2.Size()); - for (size_t i = 0; i < size; i++) - data[i] = a2.data[i]; - return *this; + if constexpr (std::is_copy_assignable::value) + { + SetSize0 (); + SetSize (a2.Size()); + for (size_t i = 0; i < size; i++) + data[i] = a2.data[i]; + return *this; + } + else + throw Exception(std::string("cannot copy Array of type ") + typeid(T).name()); } + /// steal array NETGEN_INLINE Array & operator= (Array && a2) { + mt.Swap(sizeof(T)*allocsize, a2.mt, sizeof(T)*a2.allocsize); + ngcore::Swap (size, a2.size); ngcore::Swap (data, a2.data); ngcore::Swap (allocsize, a2.allocsize); @@ -1007,16 +1066,26 @@ namespace ngcore NETGEN_INLINE void Swap (Array & b) { + mt.Swap(sizeof(T) * allocsize, b.mt, sizeof(T) * b.allocsize); + ngcore::Swap (size, b.size); ngcore::Swap (data, b.data); ngcore::Swap (allocsize, b.allocsize); ngcore::Swap (mem_to_delete, b.mem_to_delete); } + NETGEN_INLINE void StartMemoryTracing () const + { + mt.Alloc(sizeof(T) * allocsize); + } + + const MemoryTracer& GetMemoryTracer() const { return mt; } + private: /// resize array, at least to size minsize. copy contents NETGEN_INLINE void ReSize (size_t minsize); + MemoryTracer mt; }; @@ -1029,6 +1098,7 @@ namespace ngcore T * hdata = data; data = new T[nsize]; + mt.Alloc(sizeof(T) * nsize); if (hdata) { @@ -1041,6 +1111,8 @@ namespace ngcore else for (size_t i = 0; i < mins; i++) data[i] = std::move(hdata[i]); #endif + if(mem_to_delete) + mt.Free(sizeof(T) * allocsize); delete [] mem_to_delete; } @@ -1124,6 +1196,14 @@ namespace ngcore data[cnt++] = val; } + template + ArrayMem (const BaseArrayObject & a2) + : ArrayMem (a2.Size()) + { + for (size_t i : ngcore::Range(size)) + data[i] = a2[i]; + } + ArrayMem & operator= (const T & val) { @@ -1179,7 +1259,7 @@ namespace ngcore template - size_t ArraySize (Tuple tup) + size_t ArraySize (Tuple /* tup */) { return 0;} template @@ -1192,7 +1272,7 @@ namespace ngcore template - void StoreToArray (FlatArray a, Tuple tup) { ; } + void StoreToArray (FlatArray /* a */, Tuple /* tup */) { ; } template void StoreToArray (FlatArray a, Tuple tup) @@ -1284,7 +1364,7 @@ namespace ngcore /// bubble sort array template - inline void BubbleSort (FlatArray data, FlatArray slave) + inline void BubbleSort (FlatArray data, FlatArray index) { for (size_t i = 0; i < data.Size(); i++) for (size_t j = i+1; j < data.Size(); j++) @@ -1294,9 +1374,9 @@ namespace ngcore data[i] = data[j]; data[j] = hv; - S hvs = slave[i]; - slave[i] = slave[j]; - slave[j] = hvs; + S hvs = index[i]; + index[i] = index[j]; + index[j] = hvs; } } diff --git a/libsrc/core/bitarray.cpp b/libsrc/core/bitarray.cpp index daef541e..1ddd3435 100644 --- a/libsrc/core/bitarray.cpp +++ b/libsrc/core/bitarray.cpp @@ -36,10 +36,15 @@ namespace ngcore void BitArray :: SetSize (size_t asize) { if (size == asize) return; - if (owns_data) delete [] data; + if (owns_data) + { + delete [] data; + mt.Free(Addr(size)+1); + } size = asize; data = new unsigned char [Addr (size)+1]; + mt.Alloc(Addr(size)+1); } BitArray & BitArray :: Set () throw() @@ -83,6 +88,18 @@ namespace ngcore return *this; } + bool BitArray :: operator==(const BitArray& other) const + { + if(size != other.Size()) + return false; + for(auto i : Range(size/CHAR_BIT)) + if(data[i] != other.data[i]) + return false; + for(auto i : Range(size%CHAR_BIT)) + if(Test(i + CHAR_BIT * (size/CHAR_BIT)) != other.Test(i + CHAR_BIT * (size/CHAR_BIT))) + return false; + return true; + } BitArray & BitArray :: operator= (const BitArray & ba2) { @@ -115,29 +132,52 @@ namespace ngcore return cnt; } - Archive & operator & (Archive & archive, BitArray & ba) + void BitArray :: DoArchive(Archive& archive) { - if (archive.Output()) + if(archive.GetVersion("netgen") >= "v6.2.2007-62") { - archive << ba.Size(); - for (size_t i = 0; i < ba.Size(); i++) - archive << ba[i]; + archive.NeedsVersion("netgen", "v6.2.2007-62"); + auto size = Size(); + archive & size; + if(archive.Input()) + SetSize(size); + if(archive.GetVersion("netgen") < "v6.2.2009-20") + archive.Do(data, size/CHAR_BIT+1); + else + { + archive.NeedsVersion("netgen", "v6.2.2009-20"); + archive.Do(data, size/CHAR_BIT); + for(size_t i = 0; i < size%CHAR_BIT; i++) + { + size_t index = CHAR_BIT * (size/CHAR_BIT) + i; + bool b = Test(index); + archive & b; + b ? SetBit(index) : Clear(index); + } + } } else { - int size; - archive & size; - ba.SetSize (size); - ba.Clear(); - for (size_t i = 0; i < size; i++) + if (archive.Output()) { - bool b; - archive & b; - if (b) ba.SetBit(i); + throw Exception("should not get here"); + archive << Size(); + for (size_t i = 0; i < Size(); i++) + archive << (*this)[i]; + } + else + { + size_t size; + archive & size; + SetSize (size); + Clear(); + for (size_t i = 0; i < size; i++) + { + bool b; + archive & b; + if (b) SetBit(i); + } } } - return archive; } - - -} +} // namespace ngcore diff --git a/libsrc/core/bitarray.hpp b/libsrc/core/bitarray.hpp index 9c0823cf..cd8979de 100644 --- a/libsrc/core/bitarray.hpp +++ b/libsrc/core/bitarray.hpp @@ -131,6 +131,7 @@ public: return Test(i); } + NGCORE_API bool operator==(const BitArray& other) const; /// invert all bits NGCORE_API BitArray & Invert (); @@ -145,6 +146,18 @@ public: NGCORE_API BitArray & operator= (const BitArray & ba2); NGCORE_API size_t NumSet () const; + + NGCORE_API void DoArchive(Archive& archive); + + NGCORE_API auto * Data() const { return data; } + + const MemoryTracer& GetMemoryTracer() const { return mt; } + void StartMemoryTracing() const + { + if(owns_data) + mt.Alloc(Addr(size)+1); + } + private: /// unsigned char Mask (size_t i) const @@ -154,6 +167,7 @@ private: size_t Addr (size_t i) const { return (i / CHAR_BIT); } + MemoryTracer mt; }; @@ -190,11 +204,8 @@ private: return res; } - NGCORE_API std::ostream & operator<<(std::ostream & s, const BitArray & ba); - NGCORE_API Archive & operator & (Archive & archive, BitArray & ba); - } // namespace ngcore #endif // NETGEN_CORE_BITARRAY diff --git a/libsrc/core/exception.cpp b/libsrc/core/exception.cpp index df750050..b89d721e 100644 --- a/libsrc/core/exception.cpp +++ b/libsrc/core/exception.cpp @@ -217,9 +217,12 @@ static void ngcore_signal_handler(int sig) // register signal handler when library is loaded static bool dummy = []() { - signal(SIGABRT, ngcore_signal_handler); - signal(SIGILL, ngcore_signal_handler); - signal(SIGSEGV, ngcore_signal_handler); + if(getenv("NG_BACKTRACE")) + { + signal(SIGABRT, ngcore_signal_handler); + signal(SIGILL, ngcore_signal_handler); + signal(SIGSEGV, ngcore_signal_handler); + } return true; }(); diff --git a/libsrc/core/flags.cpp b/libsrc/core/flags.cpp index cdf5a7e7..e3d845cf 100644 --- a/libsrc/core/flags.cpp +++ b/libsrc/core/flags.cpp @@ -51,6 +51,10 @@ namespace ngcore auto lflags = flags.GetFlagsFlag (i, name); SetFlag (name, lflags); } + for(auto i : Range(flags.anyflags.Size())) + { + SetFlag(flags.anyflags.GetName(i), flags.anyflags[i]); + } } Flags :: Flags (Flags && flags) @@ -178,7 +182,11 @@ namespace ngcore return *this; } - + Flags & Flags :: SetFlag (const string & name, const std::any & val) + { + anyflags.Set(name, val); + return *this; + } string Flags :: GetStringFlag (const string & name, const char * def) const { @@ -279,6 +287,14 @@ namespace ngcore } } + const std::any& Flags:: GetAnyFlag(const std::string& name) const + { + if(anyflags.Used(name)) + return anyflags[name]; + static std::any empty; + return empty; + } + bool Flags :: StringFlagDefined (const string & name) const { return strflags.Used (name); @@ -304,6 +320,11 @@ namespace ngcore return numlistflags.Used (name); } + bool Flags :: AnyFlagDefined (const string& name) const + { + return anyflags.Used(name); + } + void Flags :: SaveFlags (ostream & str) const { for (int i = 0; i < strflags.Size(); i++) diff --git a/libsrc/core/flags.hpp b/libsrc/core/flags.hpp index ea4a093c..cd4b8272 100644 --- a/libsrc/core/flags.hpp +++ b/libsrc/core/flags.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "array.hpp" #include "symboltable.hpp" @@ -38,6 +39,8 @@ namespace ngcore SymbolTable>> numlistflags; /// flags list flags SymbolTable flaglistflags; + /// any object can be stored as a flag + SymbolTable anyflags; public: /// no flags Flags (); @@ -94,6 +97,8 @@ namespace ngcore Flags & SetFlag (const std::string & name, const Array & val); /// Sets double array flag Flags & SetFlag (const std::string & name, const Array & val); + /// Sets any flag + Flags & SetFlag(const std::string& name, const std::any& val); Flags SetFlag (const char * name, bool b = true) &&; @@ -135,6 +140,7 @@ namespace ngcore const Array & GetNumListFlag (const std::string & name) const; /// Returns flag list flag, empty flag if not exist const Flags & GetFlagsFlag (const std::string & name) const; + const std::any& GetAnyFlag (const std::string& name) const; /// Test, if string flag is defined @@ -147,6 +153,7 @@ namespace ngcore bool StringListFlagDefined (const std::string & name) const; /// Test, if num list flag is defined bool NumListFlagDefined (const std::string & name) const; + bool AnyFlagDefined (const std::string& name) const; /// number of string flags int GetNStringFlags () const { return strflags.Size(); } diff --git a/libsrc/core/hashtable.hpp b/libsrc/core/hashtable.hpp index 72cb73db..29f35858 100644 --- a/libsrc/core/hashtable.hpp +++ b/libsrc/core/hashtable.hpp @@ -174,6 +174,13 @@ namespace ngcore { return MakeTupleFromInt()(*this); } + + bool Contains (T val) + { + for (int j = 0; j < N; j++) + if (i[j] == val) return true; + return false; + } }; /// sort 2 integers @@ -727,6 +734,11 @@ namespace ngcore acont = cont[pos]; } + T GetData (size_t pos) const + { + return cont[pos]; + } + std::pair GetBoth (size_t pos) const { return std::pair (hash[pos], cont[pos]); diff --git a/libsrc/core/localheap.hpp b/libsrc/core/localheap.hpp index 75de3c87..bc295e79 100644 --- a/libsrc/core/localheap.hpp +++ b/libsrc/core/localheap.hpp @@ -198,9 +198,9 @@ public: return reinterpret_cast (oldp); } - virtual void Delete(void* p) {} + virtual void Delete(void* /* p */) {} - virtual void ArrayDelete(void* p) {} + virtual void ArrayDelete(void* /* p */) {} private: /// #ifndef __CUDA_ARCH__ @@ -211,7 +211,7 @@ public: public: /// free memory (dummy function) - NETGEN_INLINE void Free (void * data) throw () + NETGEN_INLINE void Free (void * /* data */) throw () { ; } diff --git a/libsrc/core/mpi_wrapper.hpp b/libsrc/core/mpi_wrapper.hpp index c1fc47dd..2e046d18 100644 --- a/libsrc/core/mpi_wrapper.hpp +++ b/libsrc/core/mpi_wrapper.hpp @@ -8,6 +8,7 @@ #include "array.hpp" #include "exception.hpp" +#include "profiler.hpp" namespace ngcore { @@ -25,6 +26,9 @@ namespace ngcore template <> struct MPI_typetrait { static MPI_Datatype MPIType () { return MPI_CHAR; } }; + template <> struct MPI_typetrait { + static MPI_Datatype MPIType () { return MPI_CHAR; } }; + template <> struct MPI_typetrait { static MPI_Datatype MPIType () { return MPI_CHAR; } }; @@ -43,6 +47,10 @@ namespace ngcore return MPI_typetrait::MPIType(); } + template + inline MPI_Datatype GetMPIType (T &) { + return GetMPIType(); + } class NgMPI_Comm { @@ -59,6 +67,17 @@ namespace ngcore NgMPI_Comm (MPI_Comm _comm, bool owns = false) : comm(_comm), valid_comm(true) { + int flag; + MPI_Initialized (&flag); + if (!flag) + { + valid_comm = false; + refcount = nullptr; + rank = 0; + size = 1; + return; + } + if (!owns) refcount = nullptr; else @@ -89,6 +108,11 @@ namespace ngcore MPI_Comm_free(&comm); } + bool ValidCommunicator() const + { + return valid_comm; + } + NgMPI_Comm & operator= (const NgMPI_Comm & c) { if (refcount) @@ -117,6 +141,7 @@ namespace ngcore int Rank() const { return rank; } int Size() const { return size; } void Barrier() const { + static Timer t("MPI - Barrier"); RegionTimer reg(t); if (size > 1) MPI_Barrier (comm); } @@ -127,9 +152,13 @@ namespace ngcore void Send (T & val, int dest, int tag) const { MPI_Send (&val, 1, GetMPIType(), dest, tag, comm); } + + void Send (const std::string & s, int dest, int tag) const { + MPI_Send( const_cast (&s[0]), s.length(), MPI_CHAR, dest, tag, comm); + } - template())> - void Send(FlatArray s, int dest, int tag) const { + template())> + void Send(FlatArray s, int dest, int tag) const { MPI_Send (s.Data(), s.Size(), GetMPIType(), dest, tag, comm); } @@ -138,13 +167,24 @@ namespace ngcore MPI_Recv (&val, 1, GetMPIType(), src, tag, comm, MPI_STATUS_IGNORE); } - template ())> - void Recv (FlatArray s, int src, int tag) const { + void Recv (std::string & s, int src, int tag) const { + MPI_Status status; + int len; + MPI_Probe (src, tag, comm, &status); + MPI_Get_count (&status, MPI_CHAR, &len); + // s.assign (len, ' '); + s.resize (len); + MPI_Recv( &s[0], len, MPI_CHAR, src, tag, comm, MPI_STATUS_IGNORE); + } + + + template ())> + void Recv (FlatArray s, int src, int tag) const { MPI_Recv (s.Data(), s.Size(), GetMPIType (), src, tag, comm, MPI_STATUS_IGNORE); } - template ())> - void Recv (Array & s, int src, int tag) const + template ())> + void Recv (Array & s, int src, int tag) const { MPI_Status status; int len; @@ -166,7 +206,7 @@ namespace ngcore } template())> - MPI_Request ISend (const FlatArray & s, int dest, int tag) const + MPI_Request ISend (FlatArray s, int dest, int tag) const { MPI_Request request; MPI_Isend (s.Data(), s.Size(), GetMPIType(), dest, tag, comm, &request); @@ -182,7 +222,7 @@ namespace ngcore } template())> - MPI_Request IRecv (const FlatArray & s, int src, int tag) const + MPI_Request IRecv (FlatArray s, int src, int tag) const { MPI_Request request; MPI_Irecv (s.Data(), s.Size(), GetMPIType(), src, tag, comm, &request); @@ -195,6 +235,7 @@ namespace ngcore template ())> T Reduce (T d, const MPI_Op & op, int root = 0) const { + static Timer t("MPI - Reduce"); RegionTimer reg(t); if (size == 1) return d; T global_d; @@ -205,6 +246,7 @@ namespace ngcore template ())> T AllReduce (T d, const MPI_Op & op) const { + static Timer t("MPI - AllReduce"); RegionTimer reg(t); if (size == 1) return d; T global_d; @@ -227,6 +269,44 @@ namespace ngcore MPI_Bcast (&s[0], len, MPI_CHAR, root, comm); } + template + void AllToAll (FlatArray send, FlatArray recv) const + { + MPI_Alltoall (send.Data(), 1, GetMPIType(), + recv.Data(), 1, GetMPIType(), comm); + } + + + template + void ScatterRoot (FlatArray send) const + { + if (size == 1) return; + MPI_Scatter (send.Data(), 1, GetMPIType(), + MPI_IN_PLACE, -1, GetMPIType(), 0, comm); + } + + template + void Scatter (T & recv) const + { + if (size == 1) return; + MPI_Scatter (NULL, 0, GetMPIType(), + &recv, 1, GetMPIType(), 0, comm); + } + + template + void AllGather (T val, FlatArray recv) const + { + if (size == 1) + { + recv[0] = val; + return; + } + MPI_Allgather (&val, 1, GetMPIType(), + recv.Data(), 1, GetMPIType(), + comm); + } + + NgMPI_Comm SubCommunicator (FlatArray procs) const { MPI_Comm subcomm; @@ -241,6 +321,7 @@ namespace ngcore NETGEN_INLINE void MyMPI_WaitAll (FlatArray requests) { + static Timer t("MPI - WaitAll"); RegionTimer reg(t); if (!requests.Size()) return; MPI_Waitall (requests.Size(), requests.Data(), MPI_STATUSES_IGNORE); } @@ -263,9 +344,10 @@ namespace ngcore static MPI_Comm MPI_COMM_WORLD = 12345, MPI_COMM_NULL = 10000; typedef int MPI_Op; + typedef int MPI_Datatype; typedef int MPI_Request; - enum { MPI_SUM = 0, MPI_MIN = 1, MPI_MAX = 2 }; + enum { MPI_SUM = 0, MPI_MIN = 1, MPI_MAX = 2, MPI_LOR = 4711 }; class NgMPI_Comm { @@ -276,6 +358,7 @@ namespace ngcore size_t Rank() const { return 0; } size_t Size() const { return 1; } + bool ValidCommunicator() const { return false; } void Barrier() const { ; } operator MPI_Comm() const { return MPI_Comm(); } @@ -298,13 +381,13 @@ namespace ngcore MPI_Request ISend (T & val, int dest, int tag) const { return 0; } template - MPI_Request ISend (const FlatArray & s, int dest, int tag) const { return 0; } + MPI_Request ISend (FlatArray s, int dest, int tag) const { return 0; } template MPI_Request IRecv (T & val, int dest, int tag) const { return 0; } template - MPI_Request IRecv (const FlatArray & s, int src, int tag) const { return 0; } + MPI_Request IRecv (FlatArray s, int src, int tag) const { return 0; } template T Reduce (T d, const MPI_Op & op, int root = 0) const { return d; } diff --git a/libsrc/core/ngcore.hpp b/libsrc/core/ngcore.hpp index 91d65bde..37ce696a 100644 --- a/libsrc/core/ngcore.hpp +++ b/libsrc/core/ngcore.hpp @@ -6,14 +6,15 @@ #include "bitarray.hpp" #include "exception.hpp" #include "flags.hpp" +#include "table.hpp" #include "hashtable.hpp" #include "localheap.hpp" #include "logging.hpp" #include "mpi_wrapper.hpp" #include "profiler.hpp" #include "signal.hpp" +#include "simd.hpp" #include "symboltable.hpp" -#include "table.hpp" #include "taskmanager.hpp" #include "version.hpp" #include "xbool.hpp" diff --git a/libsrc/core/ngcore_api.hpp b/libsrc/core/ngcore_api.hpp index b6412157..9c977c1c 100644 --- a/libsrc/core/ngcore_api.hpp +++ b/libsrc/core/ngcore_api.hpp @@ -67,6 +67,18 @@ #endif #endif +#if defined(__amd64__) || defined(_M_AMD64) +#define NETGEN_ARCH_AMD64 +#endif + +#if defined(__aarch64__) || defined(_M_ARM64) +#define NETGEN_ARCH_ARM64 +#endif + +#if defined(__arm__) || defined(_M_ARM) +#define NETGEN_ARCH_ARM +#endif + #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED #if __MAC_OS_X_VERSION_MIN_REQUIRED < 101400 // The c++ standard library on MacOS 10.13 and earlier has no aligned new operator, diff --git a/libsrc/core/paje_trace.cpp b/libsrc/core/paje_trace.cpp index 5c52a378..56fb0c12 100644 --- a/libsrc/core/paje_trace.cpp +++ b/libsrc/core/paje_trace.cpp @@ -8,28 +8,46 @@ #include "archive.hpp" // for Demangle #include "paje_trace.hpp" #include "profiler.hpp" +#include "mpi_wrapper.hpp" extern const char *header; +constexpr int MPI_PAJE_WRITER = 1; + namespace ngcore { + static std::string GetTimerName( int id ) + { +#ifndef PARALLEL + return NgProfiler::GetName(id); +#else // PARALLEL + if(id PajeTrace::memory_events; + // Produce no traces by default size_t PajeTrace::max_tracefile_size = 0; // If true, produce variable counting active threads // increases trace by a factor of two - bool PajeTrace::trace_thread_counter = true; + bool PajeTrace::trace_thread_counter = false; bool PajeTrace::trace_threads = true; + bool PajeTrace::mem_tracing_enabled = true; PajeTrace :: PajeTrace(int anthreads, std::string aname) { - start_time = GetTimeCounter(); nthreads = anthreads; tracefile_name = std::move(aname); int bytes_per_event=33; - max_num_events_per_thread = std::min( static_cast(std::numeric_limits::max()), max_tracefile_size/bytes_per_event/(2*nthreads+1)*10/7); + max_num_events_per_thread = std::min( static_cast(std::numeric_limits::max()), max_tracefile_size/bytes_per_event/(nthreads+1+trace_thread_counter*nthreads)*10/7); if(max_num_events_per_thread>0) { logger->info( "Tracefile size = {}MB", max_tracefile_size/1024/1024); @@ -47,14 +65,61 @@ namespace ngcore jobs.reserve(reserve_size); timer_events.reserve(reserve_size); + memory_events.reserve(1024*1024); + // sync start time when running in parallel +#ifdef PARALLEL + NgMPI_Comm comm(MPI_COMM_WORLD); + for(auto i : Range(5)) + comm.Barrier(); +#endif // PARALLEL + + start_time = GetTimeCounter(); tracing_enabled = true; + mem_tracing_enabled = true; + n_memory_events_at_start = memory_events.size(); } PajeTrace :: ~PajeTrace() { - if(!tracefile_name.empty()) + for(auto & ltask : tasks) + for(auto & task : ltask) + { + task.start_time -= start_time; + task.stop_time -= start_time; + } + for(auto & job : jobs) + { + job.start_time -= start_time; + job.stop_time -= start_time; + } + for(auto & event : timer_events) + event.time -= start_time; + + for(auto & llink : links) + for(auto & link : llink) + link.time -= start_time; + + for(auto i : IntRange(n_memory_events_at_start, memory_events.size())) + memory_events[i].time -= start_time; + + NgMPI_Comm comm(MPI_COMM_WORLD); + + if(comm.Size()==1) + { Write(tracefile_name); + } + else + { + // make sure the timer id is unique across all ranks + for(auto & event : timer_events) + event.timer_id += NgProfiler::SIZE*comm.Rank(); + + if(comm.Rank() == MPI_PAJE_WRITER) + Write(tracefile_name); + else + SendData(); + } } @@ -90,7 +155,6 @@ namespace ngcore int alias_counter; FILE * ctrace_stream; - TTimePoint start_time; std::shared_ptr logger = GetLogger("PajeTrace"); @@ -98,7 +162,7 @@ namespace ngcore // return time in milliseconds as double // return std::chrono::duration(t-start_time).count()*1000.0; // return std::chrono::duration(t-start_time).count() / 2.7e3; - return 1000.0*static_cast(t-start_time) * seconds_per_tick; + return 1000.0*static_cast(t) * seconds_per_tick; } enum PType @@ -180,10 +244,10 @@ namespace ngcore void operator=(const PajeFile &) = delete; void operator=(PajeFile &&) = delete; - PajeFile( const std::string & filename, TTimePoint astart_time ) + PajeFile( const std::string & filename) { - start_time = astart_time; - ctrace_stream = fopen (filename.c_str(),"w"); // NOLINT + std::string fname = filename + ".trace"; + ctrace_stream = fopen (fname.c_str(),"w"); // NOLINT fprintf(ctrace_stream, "%s", header ); // NOLINT alias_counter = 0; } @@ -365,39 +429,85 @@ namespace ngcore logger->warn("Tracing stopped during computation due to tracefile size limit of {} megabytes.", max_tracefile_size/1024/1024); } - PajeFile paje(filename, start_time); + PajeFile paje(filename); const int container_type_task_manager = paje.DefineContainerType( 0, "Task Manager" ); const int container_type_node = paje.DefineContainerType( container_type_task_manager, "Node"); const int container_type_thread = paje.DefineContainerType( container_type_task_manager, "Thread"); const int container_type_timer = container_type_thread; //paje.DefineContainerType( container_type_task_manager, "Timers"); const int container_type_jobs = paje.DefineContainerType( container_type_task_manager, "Jobs"); + const int container_type_memory = paje.DefineContainerType( container_type_task_manager, "Memory usage"); const int state_type_job = paje.DefineStateType( container_type_jobs, "Job" ); const int state_type_task = paje.DefineStateType( container_type_thread, "Task" ); const int state_type_timer = paje.DefineStateType( container_type_timer, "Timer state" ); - const int variable_type_active_threads = paje.DefineVariableType( container_type_jobs, "Active threads" ); + int variable_type_active_threads = 0; + if(trace_thread_counter) + variable_type_active_threads = paje.DefineVariableType( container_type_jobs, "Active threads" ); const int container_task_manager = paje.CreateContainer( container_type_task_manager, 0, "The task manager" ); const int container_jobs = paje.CreateContainer( container_type_jobs, container_task_manager, "Jobs" ); - paje.SetVariable( start_time, variable_type_active_threads, container_jobs, 0.0 ); - const int num_nodes = 1; //task_manager ? task_manager->GetNumNodes() : 1; + int variable_type_memory = 0; + const int container_memory = paje.CreateContainer( container_type_memory, container_task_manager, "Memory" ); + if(mem_tracing_enabled) + { + variable_type_memory = paje.DefineVariableType( container_type_task_manager, "Memory [MB]" ); + } + + int num_nodes = 1; //task_manager ? task_manager->GetNumNodes() : 1; + std::vector thread_aliases; std::vector container_nodes; - container_nodes.reserve(num_nodes); - for(int i=0; i1) + { + nthreads = nranks; + thread_aliases.reserve(nthreads); + + std::array ahostname; + int len; + MPI_Get_processor_name(ahostname.data(), &len); + std::string hostname = ahostname.data(); + + std::map host_map; + + std::string name; + for(auto i : IntRange(0, nranks)) + { + if(i!=MPI_PAJE_WRITER) + comm.Recv(name, i, 0); + else + name = hostname; + if(host_map.count(name)==0) + { + host_map[name] = container_nodes.size(); + container_nodes.emplace_back( paje.CreateContainer( container_type_node, container_task_manager, name) ); + } + thread_aliases.emplace_back( paje.CreateContainer( container_type_thread, container_nodes[host_map[name]], "Rank " + ToString(i) ) ); + } + } + else +#endif // PARALLEL + { + container_nodes.reserve(num_nodes); + for(int i=0; i thread_aliases; - thread_aliases.reserve(nthreads); - if(trace_threads) - for (int i=0; i job_map; std::map job_task_map; @@ -416,20 +526,73 @@ namespace ngcore paje.PopState( j.stop_time, state_type_job, container_jobs ); } + size_t memory_at_start = 0; + + for(const auto & i : IntRange(0, n_memory_events_at_start)) + { + if(memory_events[i].is_alloc) + memory_at_start += memory_events[i].size; + else + memory_at_start -= memory_events[i].size; + } + + paje.SetVariable( 0, variable_type_memory, container_memory, 1.0*memory_at_start/(1024*1024)); + + for(const auto & i : IntRange(n_memory_events_at_start, memory_events.size())) + { + auto & m = memory_events[i]; + if(m.size==0) + continue; + double size = 1.0*m.size/(1024*1024); + if(m.is_alloc) + paje.AddVariable( m.time, variable_type_memory, container_memory, size); + else + paje.SubVariable( m.time, variable_type_memory, container_memory, size); + } + std::set timer_ids; std::map timer_aliases; + std::map timer_names; for(auto & event : timer_events) - timer_ids.insert(event.timer_id); + timer_ids.insert(event.timer_id); + // Timer names for(auto & vtasks : tasks) - for (Task & t : vtasks) - if(t.id_type==Task::ID_TIMER) - timer_ids.insert(t.id); + for (Task & t : vtasks) + if(t.id_type==Task::ID_TIMER) + timer_ids.insert(t.id); for(auto id : timer_ids) - timer_aliases[id] = paje.DefineEntityValue( state_type_timer, NgProfiler::GetName(id), -1 ); + timer_names[id] = GetTimerName(id); + +#ifdef PARALLEL + if(nranks>1) + { + for(auto src : IntRange(0, nranks)) + { + if(src==MPI_PAJE_WRITER) + continue; + + size_t n_timers; + comm.Recv (n_timers, src, 0); + + int id; + std::string name; + for(auto i : IntRange(n_timers)) + { + comm.Recv (id, src, 0); + comm.Recv (name, src, 0); + timer_ids.insert(id); + timer_names[id] = name; + } + } + } +#endif // PARALLEL + + for(auto id : timer_ids) + timer_aliases[id] = paje.DefineEntityValue( state_type_timer, timer_names[id], -1 ); int timerdepth = 0; int maxdepth = 0; @@ -494,6 +657,50 @@ namespace ngcore } } +#ifdef PARALLEL + if(nranks>1) + { + for(auto & event : timer_events) + { + if(event.is_start) + paje.PushState( event.time, state_type_timer, thread_aliases[MPI_PAJE_WRITER], timer_aliases[event.timer_id] ); + else + paje.PopState( event.time, state_type_timer, thread_aliases[MPI_PAJE_WRITER] ); + } + + // Timer events + Array timer_id; + Array time; + Array is_start; + Array thread_id; + + for(auto src : IntRange(0, nranks)) + { + if(src==MPI_PAJE_WRITER) + continue; + + comm.Recv (timer_id, src, 0); + comm.Recv (time, src, 0); + comm.Recv (is_start, src, 0); + comm.Recv (thread_id, src, 0); + + for(auto i : Range(timer_id.Size())) + { + TimerEvent event; + event.timer_id = timer_id[i]; + event.time = time[i]; + event.is_start = is_start[i]; + event.thread_id = thread_id[i]; + + if(event.is_start) + paje.PushState( event.time, state_type_timer, thread_aliases[src], timer_aliases[event.timer_id] ); + else + paje.PopState( event.time, state_type_timer, thread_aliases[src] ); + } + } + } +#endif // PARALLEL + // Merge link event int nlinks = 0; for( auto & l : links) @@ -552,25 +759,96 @@ namespace ngcore } } } - WriteSunburstHTML(); + WriteTimingChart(); +#ifdef NETGEN_TRACE_MEMORY + WriteMemoryChart(""); +#endif // NETGEN_TRACE_MEMORY paje.WriteEvents(); } + void PajeTrace::SendData( ) + { +#ifdef PARALLEL + // Hostname + NgMPI_Comm comm(MPI_COMM_WORLD); + auto rank = comm.Rank(); + auto nranks = comm.Size(); + + std::string hostname; + { + std::array ahostname; + int len; + MPI_Get_processor_name(ahostname.data(), &len); + hostname = ahostname.data(); + } + + comm.Send(hostname, MPI_PAJE_WRITER, 0); + + // Timer names + std::set timer_ids; + std::map timer_names; + + for(auto & event : timer_events) + timer_ids.insert(event.timer_id); + + for(auto id : timer_ids) + timer_names[id] = GetTimerName(id); + size_t size = timer_ids.size(); + comm.Send(size, MPI_PAJE_WRITER, 0); + for(auto id : timer_ids) + { + comm.Send(id, MPI_PAJE_WRITER, 0); + comm.Send(timer_names[id], MPI_PAJE_WRITER, 0); + } + + + // Timer events + Array timer_id; + Array time; + Array is_start; + Array thread_id; + + for(auto & event : timer_events) + { + timer_id.Append(event.timer_id); + time.Append(event.time); + is_start.Append(event.is_start); + thread_id.Append(event.thread_id); + } + + comm.Send (timer_id, MPI_PAJE_WRITER, 0); + comm.Send (time, MPI_PAJE_WRITER, 0); + comm.Send (is_start, MPI_PAJE_WRITER, 0); + comm.Send (thread_id, MPI_PAJE_WRITER, 0); +#endif // PARALLEL + } + /////////////////////////////////////////////////////////////////// // Write HTML file drawing a sunburst chart with cumulated timings struct TreeNode { int id = 0; std::map children; - double time = 0.0; + double chart_size = 0.0; // time without children (the chart lib accumulates children sizes again) + double size = 0.0; + double min_size = 1e99; + double max_size = 0.0; std::string name; + + size_t calls = 0; TTimePoint start_time = 0; }; - void PrintNode (const TreeNode &n, int &level, std::ofstream & f); - void PrintNode (const TreeNode &n, int &level, std::ofstream & f) + void PrintNode (const TreeNode &n, std::ofstream & f) { - f << "{ name: \"" + n.name + "\", size: " + ToString(n.time); + f << "{ name: \"" + n.name + "\""; + f << ", calls: " << n.calls; + f << ", size: " << n.chart_size; + f << ", value: " << n.size; + f << ", min: " << n.min_size; + f << ", max: " << n.max_size; + if(n.calls) + f << ", avg: " << n.size/n.calls; int size = n.children.size(); if(size>0) { @@ -578,7 +856,7 @@ namespace ngcore f << ", children: ["; for(auto & c : n.children) { - PrintNode(c.second, level, f); + PrintNode(c.second, f); if(++i + + + + +)CODE_"; + if(!time_or_memory) + f << "Maximum Memory Consumption\n"; + f << R"CODE_( + + +
+ + + +)CODE_" << std::endl; + + + } + +#ifdef NETGEN_TRACE_MEMORY + void PajeTrace::WriteMemoryChart( std::string fname ) + { + if(fname=="") + fname = tracefile_name + "_memory"; + size_t mem_allocated = 0; + size_t max_mem_allocated = 0; + size_t imax_mem_allocated = 0; + + const auto & names = MemoryTracer::GetNames(); + const auto & parents = MemoryTracer::GetParents(); + size_t N = names.size(); + + Array mem_allocated_id; + mem_allocated_id.SetSize(N); + mem_allocated_id = 0; + + // Find point with maximum memory allocation, check for missing allocs/frees + for(auto i : IntRange(memory_events.size())) + { + const auto & ev = memory_events[i]; + + if(ev.is_alloc) + { + mem_allocated += ev.size; + mem_allocated_id[ev.id] += ev.size; + if(mem_allocated > max_mem_allocated && i>=n_memory_events_at_start) + { + imax_mem_allocated = i; + max_mem_allocated = mem_allocated; + } + } + else + { + if(ev.size > mem_allocated) + { + std::cerr << "Error in memory tracer: have total allocated memory < 0" << std::endl; + mem_allocated = 0; + } + else + mem_allocated -= ev.size; + if(ev.size > mem_allocated_id[ev.id]) + { + std::cerr << "Error in memory tracer: have allocated memory < 0 in tracer " << names[ev.id] << std::endl; + mem_allocated_id[ev.id] = 0; + } + else + mem_allocated_id[ev.id] -= ev.size; + } + } + + // reconstruct again the memory consumption after event imax_mem_allocated + mem_allocated_id = 0; + for(auto i : IntRange(imax_mem_allocated+1)) + { + const auto & ev = memory_events[i]; + + if(ev.is_alloc) + mem_allocated_id[ev.id] += ev.size; + else + { + if(ev.size > mem_allocated_id[ev.id]) + mem_allocated_id[ev.id] = 0; + else + mem_allocated_id[ev.id] -= ev.size; + } + } + + TreeNode root; + root.name="all"; + + Array nodes; + nodes.SetSize(N); + nodes = nullptr; + nodes[0] = &root; + Array> children(N); + + Array sorting; // topological sorting (parents before children) + sorting.SetAllocSize(N); + + for(auto i : IntRange(1, N)) + children[parents[i]].Append(i); + + ArrayMem stack; + sorting.Append(0); + stack.Append(0); + + while(stack.Size()) + { + auto current = stack.Last(); + stack.DeleteLast(); + + for(const auto child : children[current]) + { + sorting.Append(child); + if(children[child].Size()) + stack.Append(child); + } + } + + for(auto i : sorting) + { + if(i==0) + continue; + + TreeNode * parent = nodes[parents[i]]; + + auto & node = parent->children[i]; + nodes[i] = &node; + node.id = i; + node.chart_size = mem_allocated_id[i]; + node.size = mem_allocated_id[i]; + node.name = names[i]; + } + + for(auto i_ : Range(sorting)) + { + // reverse topological order to accumulate total memory usage of all children + auto i = sorting[sorting.Size()-1-i_]; + if(i==0) + continue; + nodes[parents[i]]->size += nodes[i]->size; + } + + WriteSunburstHTML( root, fname, false ); + + } +#endif // NETGEN_TRACE_MEMORY + + void PajeTrace::WriteTimingChart( ) { std::vector events; TreeNode root; - root.time=0; root.name="all"; TreeNode *current = &root; @@ -629,7 +1119,10 @@ namespace ngcore std::sort (events.begin(), events.end()); - root.time = 1000.0*static_cast(stop_time-start_time) * seconds_per_tick; + root.size = 1000.0*static_cast(stop_time) * seconds_per_tick; + root.calls = 1; + root.min_size = root.size; + root.max_size = root.size; for(auto & event : events) { @@ -645,8 +1138,8 @@ namespace ngcore if(need_init) { - current->name = is_timer_event ? NgProfiler::GetName(id) : job_names[id]; - current->time = 0.0; + current->name = is_timer_event ? GetTimerName(id) : job_names[id]; + current->size = 0.0; current->id = id; } @@ -658,44 +1151,22 @@ namespace ngcore std::cout << "node stack empty!" << std::endl; break; } - double time = 1000.0*static_cast(event.time-current->start_time) * seconds_per_tick; - current->time += time; + double size = 1000.0*static_cast(event.time-current->start_time) * seconds_per_tick; + current->size += size; + current->chart_size += size; + current->min_size = std::min(current->min_size, size); + current->max_size = std::max(current->max_size, size); + current->calls++; + current = node_stack.back(); - current->time -= time; + current->chart_size -= size; node_stack.pop_back(); } } - int level = 0; - std::ofstream f(tracefile_name+".html"); - f.precision(4); - f << R"CODE_( - - - + root.chart_size = 0.0; - - - -
- - - -)CODE_" << std::endl; + ngcore::WriteSunburstHTML( root, tracefile_name, true ); } } // namespace ngcore diff --git a/libsrc/core/paje_trace.hpp b/libsrc/core/paje_trace.hpp index 1d656ca3..5444a96c 100644 --- a/libsrc/core/paje_trace.hpp +++ b/libsrc/core/paje_trace.hpp @@ -23,18 +23,28 @@ namespace ngcore NGCORE_API static size_t max_tracefile_size; NGCORE_API static bool trace_thread_counter; NGCORE_API static bool trace_threads; + NGCORE_API static bool mem_tracing_enabled; bool tracing_enabled; TTimePoint start_time; int nthreads; + size_t n_memory_events_at_start; public: - void WriteSunburstHTML(); + NGCORE_API void WriteTimingChart(); +#ifdef NETGEN_TRACE_MEMORY + NGCORE_API void WriteMemoryChart( std::string fname ); +#endif // NETGEN_TRACE_MEMORY // Approximate number of events to trace. Tracing will // be stopped if any thread reaches this number of events unsigned int max_num_events_per_thread; + static void SetTraceMemory( bool trace_memory ) + { + mem_tracing_enabled = trace_memory; + } + static void SetTraceThreads( bool atrace_threads ) { trace_threads = atrace_threads; @@ -96,10 +106,21 @@ namespace ngcore bool operator < (const ThreadLink & other) const { return time < other.time; } }; + struct MemoryEvent + { + TTimePoint time; + size_t size; + int id; + bool is_alloc; + + bool operator < (const MemoryEvent & other) const { return time < other.time; } + }; + std::vector > tasks; std::vector jobs; std::vector timer_events; std::vector > links; + NGCORE_API static std::vector memory_events; public: NGCORE_API void StopTracing(); @@ -129,6 +150,27 @@ namespace ngcore timer_events.push_back(TimerEvent{timer_id, GetTimeCounter(), false}); } + void AllocMemory(int id, size_t size) + { + if(!mem_tracing_enabled) return; + memory_events.push_back(MemoryEvent{GetTimeCounter(), size, id, true}); + } + + void FreeMemory(int id, size_t size) + { + if(!mem_tracing_enabled) return; + memory_events.push_back(MemoryEvent{GetTimeCounter(), size, id, false}); + } + + void ChangeMemory(int id, long long size) + { + if(size>0) + AllocMemory(id, size); + if(size<0) + FreeMemory(id, -size); + } + + NETGEN_INLINE int StartTask(int thread_id, int id, int id_type = Task::ID_NONE, int additional_value = -1) { if(!tracing_enabled) return -1; @@ -185,6 +227,8 @@ namespace ngcore void Write( const std::string & filename ); + void SendData(); // MPI parallel data reduction + }; } // namespace ngcore diff --git a/libsrc/core/profiler.cpp b/libsrc/core/profiler.cpp index 1190e6fe..33ef98f4 100644 --- a/libsrc/core/profiler.cpp +++ b/libsrc/core/profiler.cpp @@ -113,5 +113,9 @@ namespace ngcore NgProfiler prof; // NOLINT +#ifdef NETGEN_TRACE_MEMORY + std::vector MemoryTracer::names{"all"}; + std::vector MemoryTracer::parents{-1}; +#endif // NETGEN_TRACE_MEMORY } // namespace ngcore diff --git a/libsrc/core/profiler.hpp b/libsrc/core/profiler.hpp index 36c10d55..d49b6543 100644 --- a/libsrc/core/profiler.hpp +++ b/libsrc/core/profiler.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include "logging.hpp" @@ -299,7 +300,171 @@ namespace ngcore return tres; } + class MemoryTracer; + + namespace detail + { + //Type trait to check if a class implements a 'void SetMemoryTacing(int)' function + template + struct has_StartMemoryTracing + { + private: + template + static constexpr auto check(T2*) -> + typename std::is_same().StartMemoryTracing()),void>::type; + template + static constexpr std::false_type check(...); + using type = decltype(check(nullptr)); // NOLINT + public: + static constexpr bool value = type::value; + }; + } // namespace detail + + class MemoryTracer + { + #ifdef NETGEN_TRACE_MEMORY + NGCORE_API static std::vector names; + NGCORE_API static std::vector parents; + + static int CreateId(const std::string& name) + { + int id = names.size(); + names.push_back(name); + parents.push_back(0); + if(id==10*NgProfiler::SIZE) + std::cerr << "Allocated " << id << " MemoryTracer objects" << std::endl; + return id; + } + int id; + + public: + + MemoryTracer( std::string name ) + { + id = CreateId(name); + } + + // not tracing + MemoryTracer() : id(0) {} + + template + MemoryTracer( std::string name, TRest & ... rest ) + { + id = CreateId(name); + Track(rest...); + } + + NETGEN_INLINE void Alloc(size_t size) const + { + if(id && trace) + trace->AllocMemory(id, size); + } + + void Free(size_t size) const + { + if(id && trace) + trace->FreeMemory(id, size); + } + + void Swap(size_t mysize, MemoryTracer& other, size_t other_size) const + { + if(!trace || (id == 0 && other.id == 0)) + return; + if(id == 0) + return trace->ChangeMemory(other.id, mysize - other_size); + if(other.id == 0) + return trace->ChangeMemory(id, other_size - mysize); + + // first decrease memory, otherwise have artificial/wrong high peak memory usage + if(mysizeChangeMemory(other.id, mysize-other_size); + trace->ChangeMemory(id, other_size-mysize); + } + else + { + trace->ChangeMemory(id, other_size-mysize); + trace->ChangeMemory(other.id, mysize-other_size); + } + } + + int GetId() const { return id; } + + template + void Track( T1 & obj, const std::string& name, TRest & ... rest ) const + { + Track(obj, name); + Track(rest...); + } + + template + void Track( T & obj, const std::string& name ) const + { + obj.GetMemoryTracer().Activate(obj, name); + parents[obj.GetMemoryTracer().GetId()] = id; + } + + static std::string GetName(int id) + { + return names[id]; + } + + std::string GetName() const + { + return names[id]; + } + + template + void Activate(T& me, const std::string& name) const + { + if(!id) + { + const_cast(this)->id = CreateId(name); + if constexpr(detail::has_StartMemoryTracing::value) + me.StartMemoryTracing(); + } + else + SetName(name); + } + + void SetName(const std::string& name) const + { + names[id] = name; + } + + + static const std::vector & GetNames() { return names; } + static const std::vector & GetParents() { return parents; } +#else // NETGEN_TRACE_MEMORY + public: + MemoryTracer() {} + MemoryTracer( std::string /* name */ ) {} + template + MemoryTracer( std::string /* name */, TRest & ... ) {} + + void Alloc(size_t /* size */) const {} + void Free(size_t /* size */) const {} + void Swap(...) const {} + int GetId() const { return 0; } + + template + void Track(TRest&...) const {} + + static std::string GetName(int /* id */) { return ""; } + std::string GetName() const { return ""; } + void SetName(std::string /* name */) const {} +#endif // NETGEN_TRACE_MEMORY + }; } // namespace ngcore +// Helper macro to easily add multiple timers in a function for profiling +// Usage: NETGEN_TIMER_FROM_HERE("my_timer_name") +// Effect: define static Timer and RegionTimer with given name and line number +#define NETGEN_TOKEN_CONCAT(x, y) x ## y +#define NETGEN_TOKEN_CONCAT2(x, y) NETGEN_TOKEN_CONCAT(x, y) +#define NETGEN_TIMER_FROM_HERE(name) \ + static Timer NETGEN_TOKEN_CONCAT2(timer_, __LINE__)( string(name)+"_"+ToString(__LINE__)); \ + RegionTimer NETGEN_TOKEN_CONCAT2(rt_,__LINE__)(NETGEN_TOKEN_CONCAT2(timer_,__LINE__)); + #endif // NETGEN_CORE_PROFILER_HPP diff --git a/libsrc/core/python_ngcore.cpp b/libsrc/core/python_ngcore.cpp index 7503a9d1..cb8c94d4 100644 --- a/libsrc/core/python_ngcore.cpp +++ b/libsrc/core/python_ngcore.cpp @@ -8,6 +8,8 @@ using std::string; namespace ngcore { bool ngcore_have_numpy = false; + bool parallel_pickling = true; + void SetFlag(Flags &flags, string s, py::object value) { if (py::isinstance(value)) diff --git a/libsrc/core/python_ngcore.hpp b/libsrc/core/python_ngcore.hpp index 44bf0362..634094ad 100644 --- a/libsrc/core/python_ngcore.hpp +++ b/libsrc/core/python_ngcore.hpp @@ -5,17 +5,70 @@ #include #include #include +#include #include "array.hpp" #include "archive.hpp" #include "flags.hpp" #include "ngcore_api.hpp" +#include "profiler.hpp" namespace py = pybind11; +//////////////////////////////////////////////////////////////////////////////// +// automatic conversion of python list to Array<> +namespace pybind11 { +namespace detail { + +template struct ngcore_list_caster { + using value_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src) || isinstance(src)) + return false; + auto s = reinterpret_borrow(src); + value.SetSize(s.size()); + value.SetSize0(); + for (auto it : s) { + value_conv conv; + if (!conv.load(it, convert)) + return false; + value.Append(cast_op(std::move(conv))); + } + return true; + } + +public: + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + if (!std::is_lvalue_reference::value) + policy = return_value_policy_override::policy(policy); + list l(src.Size()); + size_t index = 0; + for (auto &&value : src) { + auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); + if (!value_) + return handle(); + PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference + } + return l.release(); + } + + PYBIND11_TYPE_CASTER(Type, _("Array[") + value_conv::name + _("]")); +}; + +template struct type_caster> + : ngcore_list_caster, Type> { }; + + +} // namespace detail +} // namespace pybind11 +//////////////////////////////////////////////////////////////////////////////// + namespace ngcore { NGCORE_API extern bool ngcore_have_numpy; - + NGCORE_API extern bool parallel_pickling; + // Python class name type traits template struct PyNameTraits { @@ -142,6 +195,10 @@ namespace ngcore return py::make_iterator (self.begin(),self.end()); }, py::keep_alive<0,1>()) // keep array alive while iterator is used + .def("__str__", [](TFlat& self) + { + return ToString(self); + }) ; if constexpr (detail::HasPyFormat::value) @@ -225,7 +282,6 @@ namespace ngcore using ARCHIVE::stream; using ARCHIVE::version_map; using ARCHIVE::logger; - using ARCHIVE::GetLibraryVersions; public: PyArchive(const pybind11::object& alst = pybind11::none()) : ARCHIVE(std::make_shared()), @@ -270,10 +326,11 @@ namespace ngcore pybind11::list WriteOut() { + auto version_runtime = GetLibraryVersions(); FlushBuffer(); lst.append(pybind11::bytes(std::static_pointer_cast(stream)->str())); stream = std::make_shared(); - *this & GetLibraryVersions(); + *this & version_runtime; FlushBuffer(); lst.append(pybind11::bytes(std::static_pointer_cast(stream)->str())); stream = std::make_shared(); @@ -291,19 +348,14 @@ namespace ngcore return pybind11::pickle([](T* self) { PyArchive ar; + ar.SetParallel(parallel_pickling); ar & self; auto output = pybind11::make_tuple(ar.WriteOut()); - GetLogger("Archive")->trace("Pickling output for object of type {} = {}", - Demangle(typeid(T).name()), - std::string(pybind11::str(output))); return output; }, [](const pybind11::tuple & state) { T* val = nullptr; - GetLogger("Archive")->trace("State for unpickling of object of type {} = {}", - Demangle(typeid(T).name()), - std::string(pybind11::str(state[0]))); PyArchive ar(state[0]); ar & val; return val; diff --git a/libsrc/core/python_ngcore_export.cpp b/libsrc/core/python_ngcore_export.cpp index d6cc9d6e..abdceb0e 100644 --- a/libsrc/core/python_ngcore_export.cpp +++ b/libsrc/core/python_ngcore_export.cpp @@ -30,6 +30,7 @@ PYBIND11_MODULE(pyngcore, m) // NOLINT if (a[i]) ba->SetBit(i); return ba; } ), py::arg("vec")) + .def(NGSPickle()) .def("__str__", &ToString) .def("__len__", &BitArray::Size) .def("__getitem__", [] (BitArray & self, int i) @@ -69,6 +70,29 @@ PYBIND11_MODULE(pyngcore, m) // NOLINT } }, py::arg("inds"), py::arg("value"), "Clear/Set bit at given positions") + .def("__setitem__", [] (BitArray & self, py::slice inds, BitArray & ba) + { + size_t start, step, stop, n; + if (!inds.compute(self.Size(), &start, &stop, &step, &n)) + throw py::error_already_set(); + + if (start == 0 && n == self.Size() && step == 1) + { + self = ba; + } + else + { + for (size_t i = 0; i < n; i++, start += step) + { + bool b = ba.Test(i); + if (b) + self.SetBit(start); + else + self.Clear(start); + } + } + }, py::arg("inds"), py::arg("ba"), "copy BitArray") + .def("__setitem__", [](BitArray & self, IntRange range, bool b) { if (b) @@ -222,4 +246,30 @@ threads : int .def("__timing__", &TaskManager::Timing) ; + py::class_(m, "PajeTrace") + .def(py::init( [] (string filename, size_t size_mb, bool threads, bool thread_counter, bool memory) + { + PajeTrace::SetMaxTracefileSize(size_mb*1014*1024); + PajeTrace::SetTraceThreads(threads); + PajeTrace::SetTraceMemory(memory); + PajeTrace::SetTraceThreadCounter(thread_counter); + trace = new PajeTrace(TaskManager::GetMaxThreads(), filename); + return trace; + }), py::arg("filename")="ng.trace", py::arg("size")=1000, + py::arg("threads")=true, py::arg("thread_counter")=false, + py::arg("memory")=true, + "size in Megabytes" + ) + .def("__enter__", [](PajeTrace & self) { }) + .def("__exit__", [](PajeTrace & self, py::args) { self.StopTracing(); }) + .def("__del__", [](PajeTrace & self) { trace = nullptr; }) + .def_static("SetTraceThreads", &PajeTrace::SetTraceThreads) + .def_static("SetTraceThreadCounter", &PajeTrace::SetTraceThreadCounter) + .def_static("SetMaxTracefileSize", &PajeTrace::SetMaxTracefileSize) +#ifdef NETGEN_TRACE_MEMORY + .def_static("WriteMemoryChart", [](string filename){ if(trace) trace->WriteMemoryChart(filename); }, py::arg("filename")="memory" ) +#endif // NETGEN_TRACE_MEMORY + ; + + } diff --git a/libsrc/core/ranges.hpp b/libsrc/core/ranges.hpp new file mode 100644 index 00000000..658ee3d0 --- /dev/null +++ b/libsrc/core/ranges.hpp @@ -0,0 +1,109 @@ +#ifndef NETGEN_CORE_RANGES_HPP +#define NETGEN_CORE_RANGES_HPP + +#include + +namespace ngcore +{ + template + class AdapterRange + { + Iterator _begin,_end; + public: + AdapterRange(Iterator abegin, Iterator aend) : _begin(abegin), _end(aend) { ; } + Iterator begin() const { return _begin; } + Iterator end() const { return _end; } + }; + + template + class FilterAdapter + { + FUNC f; + public: + FilterAdapter(FUNC af) : f(af) { ; } + FUNC GetFunction() const { return f; } + }; + + template + class FilterIterator + { + Iterator iter; + Iterator end; + FUNC f; + public: + FilterIterator(FUNC af, Iterator aiter, Iterator aend) + : f(af), iter(aiter), end(aend) + { + while(iter!=end && !f(*iter)) + ++iter; + } + inline FilterIterator& operator ++() + { + ++iter; + while(iter!=end && !f(*iter)) + ++iter; + return *this; + } + + inline bool operator !=(FilterIterator other) + { + return iter != other.iter; + } + + inline bool operator ==(FilterIterator other) + { + return iter == other.iter; + } + + inline decltype(auto) operator *() const + { + return *iter; + } + }; + + template + FilterAdapter filter(FUNC f) { return {f}; } + + template + auto operator |(Range&& range, FilterAdapter adapter) + -> AdapterRange> + { + return {{adapter.GetFunction(),std::begin(range),std::end(range)}, + {adapter.GetFunction(), std::end(range), std::end(range)}}; + } + + template + class TransformIterator + { + FUNC f; + Iterator iter; + public: + TransformIterator(FUNC af, Iterator aiter) : f(af), iter(aiter) { ; } + + TransformIterator& operator++() { ++iter; } + bool operator !=(TransformIterator other) { return iter != other.iter; } + decltype(auto) operator *() const { return f(*iter); } + }; + + template + class TransformAdapter + { + FUNC f; + public: + TransformAdapter(FUNC af) : f(af) { ; } + FUNC GetFunction() const { return f; } + }; + + template + TransformAdapter transform(FUNC f) { return {f}; } + + template + auto operator |(Range&& range, TransformAdapter adapter) + -> AdapterRange> + { + return {{adapter.GetFunction(), std::begin(range)}, + {adapter.GetFunction(),std::end(range)}}; + } +} // namespace ngcore + +#endif // NETGEN_CORE_RANGES_HPP diff --git a/libsrc/core/simd.hpp b/libsrc/core/simd.hpp new file mode 100644 index 00000000..3459e66d --- /dev/null +++ b/libsrc/core/simd.hpp @@ -0,0 +1,75 @@ +#ifndef NETGEN_CORE_SIMD_HPP +#define NETGEN_CORE_SIMD_HPP + +/**************************************************************************/ +/* File: simd.hpp */ +/* Author: Joachim Schoeberl, Matthias Hochsteger */ +/* Date: 25. Mar. 16 */ +/**************************************************************************/ + +#include "ngcore_api.hpp" + +#include "simd_generic.hpp" + +#ifdef NETGEN_ARCH_AMD64 +#ifndef __SSE__ +#define __SSE__ +#endif +#include "simd_sse.hpp" +#endif + +#ifdef __AVX__ +#include "simd_avx.hpp" +#endif + +#ifdef __AVX512F__ +#include "simd_avx512.hpp" +#endif + +#ifdef __aarch64__ +#include "simd_arm64.hpp" +#endif + +namespace ngcore +{ +#ifdef NETGEN_ARCH_AMD64 + NETGEN_INLINE auto HSum (SIMD v1, SIMD v2, SIMD v3, SIMD v4) + { + SIMD hsum1 = my_mm_hadd_pd (v1.Data(), v2.Data()); + SIMD hsum2 = my_mm_hadd_pd (v3.Data(), v4.Data()); + return SIMD (hsum1, hsum2); + } + + NETGEN_INLINE auto GetMaskFromBits( unsigned int i ) + { + return SIMD::GetMaskFromBits(i); + } +#endif + + + NETGEN_INLINE void SIMDTranspose (SIMD a1, SIMD a2, SIMD a3, SIMD a4, + SIMD & b1, SIMD & b2, SIMD & b3, SIMD & b4) + { + SIMD h1,h2,h3,h4; + std::tie(h1,h2) = Unpack(a1,a2); + std::tie(h3,h4) = Unpack(a3,a4); + b1 = SIMD (h1.Lo(), h3.Lo()); + b2 = SIMD (h2.Lo(), h4.Lo()); + b3 = SIMD (h1.Hi(), h3.Hi()); + b4 = SIMD (h2.Hi(), h4.Hi()); + } + + template + NETGEN_INLINE auto HSum (SIMD s1, SIMD s2) + { + return SIMD(HSum(s1), HSum(s2)); + } + + template + NETGEN_INLINE auto HSum (SIMD s1, SIMD s2, SIMD s3, SIMD s4 ) + { + return SIMD(HSum(s1), HSum(s2), HSum(s3), HSum(s4)); + } +} + +#endif // NETGEN_CORE_SIMD_HPP diff --git a/libsrc/core/simd_arm64.hpp b/libsrc/core/simd_arm64.hpp new file mode 100644 index 00000000..f2572d34 --- /dev/null +++ b/libsrc/core/simd_arm64.hpp @@ -0,0 +1,175 @@ +#include "arm_neon.h" + +namespace ngcore +{ + + template <> + class SIMD + { + int64x2_t mask; + public: + SIMD (int i) + { + mask[0] = i > 0 ? -1 : 0; + mask[1] = i > 1 ? -1 : 0; + } + + SIMD (bool i0, bool i1) { mask[0] = i0 ? -1:0; mask[1] = i1 ? -1 : 0; } + SIMD (SIMD i0, SIMD i1) { mask[0] = i0[0]; mask[1] = i1[0]; } + SIMD (float64x2_t _data) : mask{_data} { } + auto Data() const { return mask; } + static constexpr int Size() { return 2; } + // static NETGEN_INLINE SIMD GetMaskFromBits (unsigned int i); + int64_t operator[] (int i) const { return mask[i]; } + + template + int64_t Get() const { return mask[I]; } + + auto Lo() const { return mask[0]; } + auto Hi() const { return mask[1]; } + }; + + + template<> + class SIMD + { + float64x2_t data; + + public: + static constexpr int Size() { return 2; } + SIMD () {} + SIMD (const SIMD &) = default; + // SIMD (double v0, double v1) : data{v0,v1} { } + SIMD (double v0, double v1) : data{vcombine_f64(float64x1_t{v0}, float64x1_t{v1})} { } + SIMD (std::array arr) : data{arr[0], arr[1]} { } + + SIMD & operator= (const SIMD &) = default; + + SIMD (double val) : data{val,val} { } + SIMD (int val) : data{double(val),double(val)} { } + SIMD (size_t val) : data{double(val),double(val)} { } + + SIMD (double const * p) + { + data = vld1q_f64(p); + // data[0] = p[0]; + // data[1] = p[1]; + } + + SIMD (double const * p, SIMD mask) + { + data[0] = mask[0] ? p[0] : 0; + data[1] = mask[1] ? p[1] : 0; + } + SIMD (float64x2_t _data) { data = _data; } + + template>::value, int>::type = 0> + SIMD (const T & func) + { + data[0] = func(0); + data[1] = func(1); + } + + void Store (double * p) + { + vst1q_f64(p, data); + /* + p[0] = data[0]; + p[1] = data[1]; + */ + } + + void Store (double * p, SIMD mask) + { + if (mask[0]) p[0] = data[0]; + if (mask[1]) p[1] = data[1]; + } + + // NETGEN_INLINE double operator[] (int i) const { return ((double*)(&data))[i]; } + NETGEN_INLINE double operator[] (int i) const { return data[i]; } + NETGEN_INLINE double & operator[] (int i) { return ((double*)&data)[i]; } + + template + double Get() const { return data[I]; } + + NETGEN_INLINE auto Data() const { return data; } + NETGEN_INLINE auto & Data() { return data; } + + operator std::tuple () + { + auto pdata = (double*)&data; + return std::tuple(pdata[0], pdata[1]); + } + + double Lo() const { return Get<0>(); } // data[0]; } + double Hi() const { return Get<1>(); } // data[1]; } + // double Hi() const { return vget_high_f64(data)[0]; } + }; + + + + NETGEN_INLINE double HSum (SIMD sd) + { + return sd.Lo()+sd.Hi(); // sd[0]+sd[1]; + } + + NETGEN_INLINE SIMD HSum (SIMD a, SIMD b) + { + // return SIMD (a[0]+a[1], b[0]+b[1]); + return vpaddq_f64(a.Data(), b.Data()); + + } + + // a*b+c + NETGEN_INLINE SIMD FMA (SIMD a, SIMD b, SIMD c) + { + return vmlaq_f64(c.Data(), a.Data(), b.Data()); + } + NETGEN_INLINE SIMD FMA (const double & a, SIMD b, SIMD c) + { + return FMA(SIMD (a), b, c); + } + // -a*b+c + NETGEN_INLINE SIMD FNMA (SIMD a, SIMD b, SIMD c) + { + return vmlsq_f64(c.Data(), a.Data(), b.Data()); + // return c-a*b; + } + NETGEN_INLINE SIMD FNMA (const double & a, SIMD b, SIMD c) + { + return FNMA(SIMD (a), b, c); + } + + NETGEN_INLINE SIMD operator+ (SIMD a, SIMD b) + { return a.Data()+b.Data(); } + + NETGEN_INLINE SIMD operator- (SIMD a, SIMD b) + { return a.Data()-b.Data(); } + NETGEN_INLINE SIMD operator- (SIMD a) + { return -a.Data(); } + + NETGEN_INLINE SIMD operator* (SIMD a, SIMD b) + { return a.Data()*b.Data(); } + + NETGEN_INLINE SIMD operator/ (SIMD a, SIMD b) + { return a.Data()/b.Data(); } + + + + NETGEN_INLINE SIMD If (SIMD a, SIMD b, SIMD c) + { + // return { a[0] ? b[0] : c[0], a[1] ? b[1] : c[1] }; + return vbslq_f64(a.Data(), b.Data(), c.Data()); + } + NETGEN_INLINE SIMD If (SIMD a, SIMD b, SIMD c) + { + return SIMD (a[0] ? b[0] : c[0], a[1] ? b[1] : c[1]); + } + + NETGEN_INLINE SIMD operator&& (SIMD a, SIMD b) + { + return vandq_u64 (a.Data(), b.Data()); + } + +} + diff --git a/libsrc/core/simd_avx.hpp b/libsrc/core/simd_avx.hpp new file mode 100644 index 00000000..37a0bab8 --- /dev/null +++ b/libsrc/core/simd_avx.hpp @@ -0,0 +1,309 @@ +#ifndef NETGEN_CORE_SIMD_AVX_HPP +#define NETGEN_CORE_SIMD_AVX_HPP + +/**************************************************************************/ +/* File: simd_avx.hpp */ +/* Author: Joachim Schoeberl, Matthias Hochsteger */ +/* Date: 25. Mar. 16 */ +/**************************************************************************/ + +#include + +namespace ngcore +{ + +#if defined(__GNUC__) && (__GNUC__ == 7) + // GCC7 does not have intrinsic _mm256_set_m128i, see + // https://stackoverflow.com/questions/32630458/setting-m256i-to-the-value-of-two-m128i-values + NETGEN_INLINE auto _mm256_set_m128i(__m128i v0, __m128i v1) { + return _mm256_insertf128_si256(_mm256_castsi128_si256(v1), (v0), 1); + } +#endif // defined(__GNUC__) && (__GNUC__ == 7) + +#if defined(__AVX2__) + NETGEN_INLINE __m256i my_mm256_cmpgt_epi64 (__m256i a, __m256i b) + { + return _mm256_cmpgt_epi64 (a,b); + } +#else + NETGEN_INLINE __m256i my_mm256_cmpgt_epi64 (__m256i a, __m256i b) + { + __m128i rlo = _mm_cmpgt_epi64(_mm256_extractf128_si256(a, 0), + _mm256_extractf128_si256(b, 0)); + __m128i rhi = _mm_cmpgt_epi64(_mm256_extractf128_si256(a, 1), + _mm256_extractf128_si256(b, 1)); + return _mm256_insertf128_si256 (_mm256_castsi128_si256(rlo), rhi, 1); + } +#endif + + + template <> + class SIMD + { + __m256i mask; + public: + SIMD (int64_t i) + : mask(my_mm256_cmpgt_epi64(_mm256_set1_epi64x(i), + _mm256_set_epi64x(3, 2, 1, 0))) + { ; } + SIMD (__m256i _mask) : mask(_mask) { ; } + SIMD (__m256d _mask) : mask(_mm256_castpd_si256(_mask)) { ; } + __m256i Data() const { return mask; } + static constexpr int Size() { return 4; } + static SIMD GetMaskFromBits (unsigned int i); + }; + + static SIMD masks_from_4bits[16] = { + _mm256_set_epi64x (0,0,0,0), _mm256_set_epi64x (0,0,0,-1), + _mm256_set_epi64x (0,0,-1,0), _mm256_set_epi64x (0,0,-1,-1), + _mm256_set_epi64x (0,-1,0,0), _mm256_set_epi64x (0,-1,0,-1), + _mm256_set_epi64x (0,-1,-1,0), _mm256_set_epi64x (0,-1,-1,-1), + _mm256_set_epi64x (-1,0,0,0), _mm256_set_epi64x (-1,0,0,-1), + _mm256_set_epi64x (-1,0,-1,0), _mm256_set_epi64x (-1,0,-1,-1), + _mm256_set_epi64x (-1,-1,0,0), _mm256_set_epi64x (-1,-1,0,-1), + _mm256_set_epi64x (-1,-1,-1,0), _mm256_set_epi64x (-1,-1,-1,-1) + }; + + NETGEN_INLINE SIMD SIMD :: GetMaskFromBits (unsigned int i) + { + return masks_from_4bits[i & 15]; + } + + template<> + class alignas(32) SIMD + { + __m256i data; + + public: + static constexpr int Size() { return 4; } + SIMD () {} + SIMD (const SIMD &) = default; + SIMD & operator= (const SIMD &) = default; + + SIMD (int64_t val) { data = _mm256_set1_epi64x(val); } + SIMD (int64_t v0, int64_t v1, int64_t v2, int64_t v3) { data = _mm256_set_epi64x(v3,v2,v1,v0); } + SIMD (std::array a) + : data{_mm256_set_epi64x(a[3],a[2],a[1],a[0])} + {} + SIMD (SIMD v0, SIMD v1) + : data(_mm256_set_m128i(v0.Data(),v1.Data())) + {} + SIMD (__m256i _data) { data = _data; } + + NETGEN_INLINE auto operator[] (int i) const { return ((int64_t*)(&data))[i]; } + NETGEN_INLINE __m256i Data() const { return data; } + NETGEN_INLINE __m256i & Data() { return data; } + + SIMD Lo() const { return _mm256_extractf128_si256(data, 0); } + SIMD Hi() const { return _mm256_extractf128_si256(data, 1); } + static SIMD FirstInt(int n0=0) { return { n0+0, n0+1, n0+2, n0+3 }; } + }; + + + NETGEN_INLINE SIMD operator-(SIMD a) { return _mm256_sub_epi64(_mm256_setzero_si256(), a.Data()); } + +#ifdef __AVX2__ + NETGEN_INLINE SIMD operator+ (SIMD a, SIMD b) { return _mm256_add_epi64(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator- (SIMD a, SIMD b) { return _mm256_sub_epi64(a.Data(),b.Data()); } +#endif // __AVX2__ + + template<> + class alignas(32) SIMD + { + __m256d data; + + public: + static constexpr int Size() { return 4; } + SIMD () {} + SIMD (const SIMD &) = default; + SIMD & operator= (const SIMD &) = default; + + SIMD (double val) { data = _mm256_set1_pd(val); } + SIMD (int val) { data = _mm256_set1_pd(val); } + SIMD (size_t val) { data = _mm256_set1_pd(val); } + SIMD (double v0, double v1, double v2, double v3) { data = _mm256_set_pd(v3,v2,v1,v0); } + SIMD (SIMD v0, SIMD v1) : SIMD(v0[0], v0[1], v1[0], v1[1]) { ; } + SIMD (double const * p) { data = _mm256_loadu_pd(p); } + SIMD (double const * p, SIMD mask) { data = _mm256_maskload_pd(p, mask.Data()); } + SIMD (__m256d _data) { data = _data; } + SIMD (std::array a) + : data{_mm256_set_pd(a[3],a[2],a[1],a[0])} + {} + + void Store (double * p) { _mm256_storeu_pd(p, data); } + void Store (double * p, SIMD mask) { _mm256_maskstore_pd(p, mask.Data(), data); } + + template>::value, int>::type = 0> + SIMD (const T & func) + { + data = _mm256_set_pd(func(3), func(2), func(1), func(0)); + } + + NETGEN_INLINE double operator[] (int i) const { return ((double*)(&data))[i]; } + NETGEN_INLINE double & operator[] (int i) { return ((double*)(&data))[i]; } + // [[deprecated("don't write to individual elments of SIMD")]] + // NETGEN_INLINE double & operator[] (int i) { return ((double*)(&data))[i]; } + template + double Get() const { return ((double*)(&data))[I]; } + NETGEN_INLINE __m256d Data() const { return data; } + NETGEN_INLINE __m256d & Data() { return data; } + + SIMD Lo() const { return _mm256_extractf128_pd(data, 0); } + SIMD Hi() const { return _mm256_extractf128_pd(data, 1); } + + operator std::tuple () + { return std::tuple((*this)[0], (*this)[1], (*this)[2], (*this)[3]); } + }; + + NETGEN_INLINE auto Unpack (SIMD a, SIMD b) + { + return std::make_tuple(SIMD(_mm256_unpacklo_pd(a.Data(),b.Data())), + SIMD(_mm256_unpackhi_pd(a.Data(),b.Data()))); + } + + NETGEN_INLINE SIMD operator- (SIMD a) { return _mm256_xor_pd(a.Data(), _mm256_set1_pd(-0.0)); } + NETGEN_INLINE SIMD operator+ (SIMD a, SIMD b) { return _mm256_add_pd(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator- (SIMD a, SIMD b) { return _mm256_sub_pd(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator* (SIMD a, SIMD b) { return _mm256_mul_pd(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator/ (SIMD a, SIMD b) { return _mm256_div_pd(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator* (double a, SIMD b) { return _mm256_set1_pd(a)*b.Data(); } + NETGEN_INLINE SIMD operator* (SIMD b, double a) { return _mm256_set1_pd(a)*b.Data(); } + + NETGEN_INLINE SIMD sqrt (SIMD a) { return _mm256_sqrt_pd(a.Data()); } + NETGEN_INLINE SIMD floor (SIMD a) { return _mm256_floor_pd(a.Data()); } + NETGEN_INLINE SIMD ceil (SIMD a) { return _mm256_ceil_pd(a.Data()); } + NETGEN_INLINE SIMD fabs (SIMD a) { return _mm256_max_pd(a.Data(), (-a).Data()); } + + +#ifdef __FMA__ + NETGEN_INLINE SIMD FMA (SIMD a, SIMD b, SIMD c) + { + return _mm256_fmadd_pd (a.Data(), b.Data(), c.Data()); + } + NETGEN_INLINE SIMD FMA (const double & a, SIMD b, SIMD c) + { + return _mm256_fmadd_pd (_mm256_set1_pd(a), b.Data(), c.Data()); + } + NETGEN_INLINE SIMD FNMA (SIMD a, SIMD b, SIMD c) + { + return _mm256_fnmadd_pd (a.Data(), b.Data(), c.Data()); + } + NETGEN_INLINE SIMD FNMA (const double & a, SIMD b, SIMD c) + { + return _mm256_fnmadd_pd (_mm256_set1_pd(a), b.Data(), c.Data()); + } +#endif + +#if defined(__FMA__) && !defined(__AVX512F__) + // make sure to use the update-version of fma + // important in matrix kernels using 12 sum-registers, 3 a-values and updated b-value + // avx512 has enough registers, and gcc seems to use only the first 16 z-regs + NETGEN_INLINE void FMAasm (SIMD a, SIMD b, SIMD & sum) + { + asm ("vfmadd231pd %[a], %[b], %[sum]" + : [sum] "+x" (sum.Data()) + : [a] "x" (a.Data()), [b] "x" (b.Data()) + ); + } + + NETGEN_INLINE void FNMAasm (SIMD a, SIMD b, SIMD & sum) + { + asm ("vfnmadd231pd %[a], %[b], %[sum]" + : [sum] "+x" (sum.Data()) + : [a] "x" (a.Data()), [b] "x" (b.Data()) + ); + } +#endif + + + + + NETGEN_INLINE SIMD operator<= (SIMD a , SIMD b) + { return _mm256_cmp_pd (a.Data(), b.Data(), _CMP_LE_OQ); } + NETGEN_INLINE SIMD operator< (SIMD a , SIMD b) + { return _mm256_cmp_pd (a.Data(), b.Data(), _CMP_LT_OQ); } + NETGEN_INLINE SIMD operator>= (SIMD a , SIMD b) + { return _mm256_cmp_pd (a.Data(), b.Data(), _CMP_GE_OQ); } + NETGEN_INLINE SIMD operator> (SIMD a , SIMD b) + { return _mm256_cmp_pd (a.Data(), b.Data(), _CMP_GT_OQ); } + NETGEN_INLINE SIMD operator== (SIMD a , SIMD b) + { return _mm256_cmp_pd (a.Data(), b.Data(), _CMP_EQ_OQ); } + NETGEN_INLINE SIMD operator!= (SIMD a , SIMD b) + { return _mm256_cmp_pd (a.Data(), b.Data(), _CMP_NEQ_OQ); } + + NETGEN_INLINE SIMD operator<= (SIMD a , SIMD b) + { return _mm256_xor_si256(_mm256_cmpgt_epi64(a.Data(),b.Data()),_mm256_set1_epi32(-1)); } + NETGEN_INLINE SIMD operator< (SIMD a , SIMD b) + { return my_mm256_cmpgt_epi64(b.Data(),a.Data()); } + NETGEN_INLINE SIMD operator>= (SIMD a , SIMD b) + { return _mm256_xor_si256(_mm256_cmpgt_epi64(b.Data(),a.Data()),_mm256_set1_epi32(-1)); } + NETGEN_INLINE SIMD operator> (SIMD a , SIMD b) + { return my_mm256_cmpgt_epi64(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator== (SIMD a , SIMD b) + { return _mm256_cmpeq_epi64(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator!= (SIMD a , SIMD b) + { return _mm256_xor_si256(_mm256_cmpeq_epi64(a.Data(),b.Data()),_mm256_set1_epi32(-1)); } + +#ifdef __AVX2__ + NETGEN_INLINE SIMD operator&& (SIMD a, SIMD b) + { return _mm256_and_si256 (a.Data(), b.Data()); } + NETGEN_INLINE SIMD operator|| (SIMD a, SIMD b) + { return _mm256_or_si256 (a.Data(), b.Data()); } + NETGEN_INLINE SIMD operator! (SIMD a) + { return _mm256_xor_si256 (a.Data(), _mm256_cmpeq_epi64(a.Data(),a.Data())); } +#else //AVX2 is a superset of AVX. Without it, it is necessary to reinterpret the types + NETGEN_INLINE SIMD operator&& (SIMD a, SIMD b) + { return _mm256_castpd_si256(_mm256_and_pd (_mm256_castsi256_pd(a.Data()),_mm256_castsi256_pd( b.Data()))); } + NETGEN_INLINE SIMD operator|| (SIMD a, SIMD b) + { return _mm256_castpd_si256(_mm256_or_pd (_mm256_castsi256_pd(a.Data()), _mm256_castsi256_pd(b.Data()))); } + NETGEN_INLINE SIMD operator! (SIMD a) + { return _mm256_castpd_si256(_mm256_xor_pd (_mm256_castsi256_pd(a.Data()),_mm256_castsi256_pd( _mm256_cmpeq_epi64(a.Data(),a.Data())))); } +#endif + NETGEN_INLINE SIMD If (SIMD a, SIMD b, SIMD c) + { return _mm256_blendv_pd(c.Data(), b.Data(), _mm256_castsi256_pd(a.Data())); } + + NETGEN_INLINE SIMD IfPos (SIMD a, SIMD b, SIMD c) + { + auto cp = _mm256_cmp_pd (a.Data(), _mm256_setzero_pd(), _CMP_GT_OS); + return _mm256_blendv_pd(c.Data(), b.Data(), cp); + } + + NETGEN_INLINE SIMD IfZero (SIMD a, SIMD b, SIMD c) + { + auto cp = _mm256_cmp_pd (a.Data(), _mm256_setzero_pd(), _CMP_EQ_OS); + return _mm256_blendv_pd(c.Data(), b.Data(), cp); + } + + NETGEN_INLINE double HSum (SIMD sd) + { + // __m128d hv = _mm_add_pd (_mm256_extractf128_pd(sd.Data(),0), _mm256_extractf128_pd(sd.Data(),1)); + __m128d hv = (sd.Lo()+sd.Hi()).Data(); + return _mm_cvtsd_f64 (_mm_hadd_pd (hv, hv)); + } + + NETGEN_INLINE auto HSum (SIMD sd1, SIMD sd2) + { + __m256d hv = _mm256_hadd_pd(sd1.Data(), sd2.Data()); + __m128d hv2 = _mm_add_pd (_mm256_extractf128_pd(hv,0), _mm256_extractf128_pd(hv,1)); + return SIMD(_mm_cvtsd_f64 (hv2), _mm_cvtsd_f64(_mm_shuffle_pd (hv2, hv2, 3))); + } + + NETGEN_INLINE auto HSum (SIMD v1, SIMD v2, SIMD v3, SIMD v4) + { + __m256d hsum1 = _mm256_hadd_pd (v1.Data(), v2.Data()); + __m256d hsum2 = _mm256_hadd_pd (v3.Data(), v4.Data()); + SIMD hsum = _mm256_add_pd (_mm256_permute2f128_pd (hsum1, hsum2, 1+2*16), + _mm256_blend_pd (hsum1, hsum2, 12)); + return hsum; + // return make_tuple(hsum[0], hsum[1], hsum[2], hsum[3]); + } + + + NETGEN_INLINE SIMD If (SIMD a, SIMD b, SIMD c) + { return _mm256_castpd_si256(_mm256_blendv_pd(_mm256_castsi256_pd(c.Data()), _mm256_castsi256_pd(b.Data()), + _mm256_castsi256_pd(a.Data()))); } + +} + +#endif // NETGEN_CORE_SIMD_AVX_HPP + diff --git a/libsrc/core/simd_avx512.hpp b/libsrc/core/simd_avx512.hpp new file mode 100644 index 00000000..bf57f4e1 --- /dev/null +++ b/libsrc/core/simd_avx512.hpp @@ -0,0 +1,249 @@ +#ifndef NETGEN_CORE_SIMD_AVX512_HPP +#define NETGEN_CORE_SIMD_AVX512_HPP + +/**************************************************************************/ +/* File: simd_avx512.hpp */ +/* Author: Joachim Schoeberl, Matthias Hochsteger */ +/* Date: 25. Mar. 16 */ +/**************************************************************************/ + +#include + +namespace ngcore +{ + + template <> + class SIMD + { + __mmask8 mask; + public: + SIMD (size_t i) + : mask(_mm512_cmpgt_epi64_mask(_mm512_set1_epi64(i), + _mm512_set_epi64(7, 6, 5, 4, 3, 2, 1, 0))) + { ; } + SIMD (int i) + : mask(_mm512_cmpgt_epi64_mask(_mm512_set1_epi64(i), + _mm512_set_epi64(7, 6, 5, 4, 3, 2, 1, 0))) + { ; } + SIMD (int64_t i) + : mask(_mm512_cmpgt_epi64_mask(_mm512_set1_epi64(i), + _mm512_set_epi64(7, 6, 5, 4, 3, 2, 1, 0))) + { ; } + SIMD (__mmask8 _mask) : mask(_mask) { ; } + __mmask8 Data() const { return mask; } + static constexpr int Size() { return 8; } + static NETGEN_INLINE SIMD GetMaskFromBits (unsigned int i) + { + return SIMD(__mmask8(i)); + } + }; + + template<> + class alignas(64) SIMD + { + __m512i data; + + public: + static constexpr int Size() { return 8; } + SIMD () {} + SIMD (const SIMD &) = default; + SIMD & operator= (const SIMD &) = default; + + SIMD (int64_t val) { data = _mm512_set1_epi64(val); } + SIMD (int64_t v0, int64_t v1, int64_t v2, int64_t v3, int64_t v4, int64_t v5, int64_t v6, int64_t v7) { data = _mm512_set_epi64(v7,v6,v5,v4,v3,v2,v1,v0); } + SIMD (__m512i _data) { data = _data; } + + template>::value, int>::type = 0> + SIMD (const T & func) + { + data = _mm512_set_epi64(func(7), func(6), func(5), func(4), func(3), func(2), func(1), func(0)); + } + + + NETGEN_INLINE auto operator[] (int i) const { return ((int64_t*)(&data))[i]; } + NETGEN_INLINE __m512i Data() const { return data; } + NETGEN_INLINE __m512i & Data() { return data; } + static SIMD FirstInt() { return { 0, 1, 2, 3, 4, 5, 6, 7 }; } + }; + + NETGEN_INLINE SIMD operator-(SIMD a) { return _mm512_sub_epi64(_mm512_setzero_si512(), a.Data()); } + + NETGEN_INLINE SIMD operator+ (SIMD a, SIMD b) { return _mm512_add_epi64(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator- (SIMD a, SIMD b) { return _mm512_sub_epi64(a.Data(),b.Data()); } + + NETGEN_INLINE SIMD If (SIMD a, SIMD b, SIMD c) + { return _mm512_mask_blend_epi64(a.Data(), c.Data(), b.Data()); } + + + template<> + class alignas(64) SIMD + { + __m512d data; + public: + static constexpr int Size() { return 8; } + SIMD () {} + SIMD (const SIMD &) = default; + SIMD & operator= (const SIMD &) = default; + + SIMD (double val) { data = _mm512_set1_pd(val); } + SIMD (int val) { data = _mm512_set1_pd(val); } + SIMD (size_t val) { data = _mm512_set1_pd(val); } + SIMD (double const * p) { data = _mm512_loadu_pd(p); } + SIMD (double const * p, SIMD mask) + { data = _mm512_mask_loadu_pd(_mm512_setzero_pd(), mask.Data(), p); } + SIMD (__m512d _data) { data = _data; } + + template>::value, int>::type = 0> + SIMD (const T & func) + { + data = _mm512_set_pd(func(7), func(6), func(5), func(4), func(3), func(2), func(1), func(0)); + } + + void Store (double * p) { _mm512_storeu_pd(p, data); } + void Store (double * p, SIMD mask) { _mm512_mask_storeu_pd(p, mask.Data(), data); } + + template + void SIMD_function (const Function & func, std::true_type) + { + data = (__m512){ func(7), func(6), func(5), func(4), + func(3), func(2), func(1), func(0) }; + } + + // not a function + void SIMD_function (double const * p, std::false_type) + { + data = _mm512_loadu_pd(p); + } + + void SIMD_function (double val, std::false_type) + { + data = _mm512_set1_pd(val); + } + + void SIMD_function (__m512d _data, std::false_type) + { + data = _data; + } + + NETGEN_INLINE double operator[] (int i) const { return ((double*)(&data))[i]; } + NETGEN_INLINE __m512d Data() const { return data; } + NETGEN_INLINE __m512d & Data() { return data; } + + }; + + NETGEN_INLINE SIMD operator- (SIMD a) { return -a.Data(); } + NETGEN_INLINE SIMD operator+ (SIMD a, SIMD b) { return _mm512_add_pd(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator- (SIMD a, SIMD b) { return _mm512_sub_pd(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator* (SIMD a, SIMD b) { return _mm512_mul_pd(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator/ (SIMD a, SIMD b) { return _mm512_div_pd(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator* (double a, SIMD b) { return _mm512_set1_pd(a)*b.Data(); } + NETGEN_INLINE SIMD operator* (SIMD b, double a) { return _mm512_set1_pd(a)*b.Data(); } + + NETGEN_INLINE SIMD sqrt (SIMD a) { return _mm512_sqrt_pd(a.Data()); } + NETGEN_INLINE SIMD floor (SIMD a) { return _mm512_floor_pd(a.Data()); } + NETGEN_INLINE SIMD ceil (SIMD a) { return _mm512_ceil_pd(a.Data()); } + NETGEN_INLINE SIMD fabs (SIMD a) { return _mm512_max_pd(a.Data(), -a.Data()); } + + NETGEN_INLINE SIMD operator<= (SIMD a , SIMD b) + { return _mm512_cmp_pd_mask (a.Data(), b.Data(), _CMP_LE_OQ); } + NETGEN_INLINE SIMD operator< (SIMD a , SIMD b) + { return _mm512_cmp_pd_mask (a.Data(), b.Data(), _CMP_LT_OQ); } + NETGEN_INLINE SIMD operator>= (SIMD a , SIMD b) + { return _mm512_cmp_pd_mask (a.Data(), b.Data(), _CMP_GE_OQ); } + NETGEN_INLINE SIMD operator> (SIMD a , SIMD b) + { return _mm512_cmp_pd_mask (a.Data(), b.Data(), _CMP_GT_OQ); } + NETGEN_INLINE SIMD operator== (SIMD a , SIMD b) + { return _mm512_cmp_pd_mask (a.Data(), b.Data(), _CMP_EQ_OQ); } + NETGEN_INLINE SIMD operator!= (SIMD a , SIMD b) + { return _mm512_cmp_pd_mask (a.Data(), b.Data(), _CMP_NEQ_OQ); } + + NETGEN_INLINE SIMD operator<= (SIMD a , SIMD b) + { return _mm512_cmp_epi64_mask (a.Data(), b.Data(), _MM_CMPINT_LE); } + NETGEN_INLINE SIMD operator< (SIMD a , SIMD b) + { return _mm512_cmp_epi64_mask (a.Data(), b.Data(), _MM_CMPINT_LT); } + NETGEN_INLINE SIMD operator>= (SIMD a , SIMD b) + { return _mm512_cmp_epi64_mask (a.Data(), b.Data(), _MM_CMPINT_NLT); } + NETGEN_INLINE SIMD operator> (SIMD a , SIMD b) + { return _mm512_cmp_epi64_mask (a.Data(), b.Data(), _MM_CMPINT_NLE); } + NETGEN_INLINE SIMD operator== (SIMD a , SIMD b) + { return _mm512_cmp_epi64_mask (a.Data(), b.Data(), _MM_CMPINT_EQ); } + NETGEN_INLINE SIMD operator!= (SIMD a , SIMD b) + { return _mm512_cmp_epi64_mask (a.Data(), b.Data(), _MM_CMPINT_NE); } + + NETGEN_INLINE SIMD operator&& (SIMD a, SIMD b) + { return (__mmask8)(a.Data() & b.Data()); } + NETGEN_INLINE SIMD operator|| (SIMD a, SIMD b) + { return (__mmask8)(a.Data() | b.Data()); } + NETGEN_INLINE SIMD operator! (SIMD a) + { return (__mmask8)(~a.Data()); } + + NETGEN_INLINE SIMD If (SIMD a, SIMD b, SIMD c) + { return _mm512_mask_blend_pd(a.Data(), c.Data(), b.Data()); } + + NETGEN_INLINE SIMD IfPos (SIMD a, SIMD b, SIMD c) + { + auto k = _mm512_cmp_pd_mask(a.Data(),_mm512_setzero_pd(), _CMP_GT_OS); + return _mm512_mask_blend_pd(k,c.Data(),b.Data()); + } + NETGEN_INLINE SIMD IfZero (SIMD a, SIMD b, SIMD c) + { + auto k = _mm512_cmp_pd_mask(a.Data(),_mm512_setzero_pd(), _CMP_EQ_OS); + return _mm512_mask_blend_pd(k,c.Data(),b.Data()); + } + + + NETGEN_INLINE auto Unpack (SIMD a, SIMD b) + { + return std::make_tuple(SIMD(_mm512_unpacklo_pd(a.Data(),b.Data())), + SIMD(_mm512_unpackhi_pd(a.Data(),b.Data()))); + } + + + NETGEN_INLINE double HSum (SIMD sd) + { + SIMD low = _mm512_extractf64x4_pd(sd.Data(),0); + SIMD high = _mm512_extractf64x4_pd(sd.Data(),1); + return HSum(low)+HSum(high); + } + + NETGEN_INLINE auto HSum (SIMD sd1, SIMD sd2) + { + return SIMD(HSum(sd1), HSum(sd2)); + } + + NETGEN_INLINE SIMD HSum (SIMD v1, SIMD v2, SIMD v3, SIMD v4) + { + SIMD lo,hi; + std::tie(lo,hi) = Unpack(v1, v2); + SIMD sum01 = lo+hi; + std::tie(lo,hi) = Unpack(v3, v4); + SIMD sum23 = lo+hi; + // sum01 b a b a b a b a + // sum23 d c d c d c d c + // __m512 perm = _mm512_permutex2var_pd (sum01.Data(), _mm512_set_epi64(1,2,3,4,5,6,7,8), sum23.Data()); + __m256d ab = _mm512_extractf64x4_pd(sum01.Data(),0) + _mm512_extractf64x4_pd(sum01.Data(),1); + __m256d cd = _mm512_extractf64x4_pd(sum23.Data(),0) + _mm512_extractf64x4_pd(sum23.Data(),1); + return _mm256_add_pd (_mm256_permute2f128_pd (ab, cd, 1+2*16), _mm256_blend_pd (ab, cd, 12)); + } + + NETGEN_INLINE SIMD FMA (SIMD a, SIMD b, SIMD c) + { + return _mm512_fmadd_pd (a.Data(), b.Data(), c.Data()); + } + NETGEN_INLINE SIMD FMA (const double & a, SIMD b, SIMD c) + { + return _mm512_fmadd_pd (_mm512_set1_pd(a), b.Data(), c.Data()); + } + + NETGEN_INLINE SIMD FNMA (SIMD a, SIMD b, SIMD c) + { + return _mm512_fnmadd_pd (a.Data(), b.Data(), c.Data()); + } + NETGEN_INLINE SIMD FNMA (const double & a, SIMD b, SIMD c) + { + return _mm512_fnmadd_pd (_mm512_set1_pd(a), b.Data(), c.Data()); + } + +} + +#endif // NETGEN_CORE_SIMD_AVX512_HPP diff --git a/libsrc/core/simd_generic.hpp b/libsrc/core/simd_generic.hpp new file mode 100644 index 00000000..1ad4ea99 --- /dev/null +++ b/libsrc/core/simd_generic.hpp @@ -0,0 +1,707 @@ +#ifndef NETGEN_CORE_SIMD_GENERIC_HPP +#define NETGEN_CORE_SIMD_GENERIC_HPP + +/**************************************************************************/ +/* File: simd_base.hpp */ +/* Author: Joachim Schoeberl, Matthias Hochsteger */ +/* Date: 25. Mar. 16 */ +/**************************************************************************/ + +#include +#include +#include + +#include "array.hpp" + +namespace ngcore +{ + + constexpr int GetDefaultSIMDSize() { +#if defined __AVX512F__ + return 8; +#elif defined __AVX__ + return 4; +#elif defined NETGEN_ARCH_AMD64 + return 2; +#else + return 2; +#endif + } + + + template class SIMD; + + class mask64; + + //////////////////////////////////////////////////////////////////////////// + namespace detail { + template + auto array_range_impl(std::array const& arr, + size_t first, + std::index_sequence) + -> std::array { + return {arr[first + I]...}; + } + + template + auto array_range(std::array const& arr, size_t first) { + return array_range_impl(arr, first, std::make_index_sequence{}); + } + + } // namespace detail + + //////////////////////////////////////////////////////////////////////////// + // mask + + template <> + class SIMD + { + int64_t mask; + public: + SIMD (int64_t i) + : mask(i > 0 ? -1 : 0) { ; } + bool Data() const { return mask; } + static constexpr int Size() { return 1; } + auto operator[] (int /* i */) const { return mask; } + }; + + + template + class alignas(GetDefaultSIMDSize()*sizeof(int64_t)) SIMD + { + static constexpr int N1 = std::min(GetDefaultSIMDSize(), N/2); + static constexpr int N2 = N-N1; + + SIMD lo; + SIMD hi; + public: + + SIMD (int64_t i) : lo(i), hi(i-N1 ) { ; } + SIMD (SIMD lo_, SIMD hi_) : lo(lo_), hi(hi_) { ; } + SIMD Lo() const { return lo; } + SIMD Hi() const { return hi; } + static constexpr int Size() { return N; } + }; + + template + NETGEN_INLINE SIMD operator&& (SIMD a, SIMD b) + { + if constexpr(N==1) return a.Data() && b.Data(); + else return { a.Lo() && b.Lo(), a.Hi() && b.Hi() }; + } + + + //////////////////////////////////////////////////////////////////////////// + // int64 + + template<> + class SIMD + { + int64_t data; + + public: + static constexpr int Size() { return 1; } + SIMD () {} + SIMD (const SIMD &) = default; + SIMD & operator= (const SIMD &) = default; + SIMD (int val) : data{val} {} + SIMD (int64_t val) : data{val} {} + SIMD (size_t val) : data(val) {} + explicit SIMD (std::array arr) + : data{arr[0]} + {} + + int64_t operator[] (int i) const { return ((int64_t*)(&data))[i]; } + auto Data() const { return data; } + static SIMD FirstInt(int64_t n0=0) { return {n0}; } + template + int64_t Get() + { + static_assert(I==0); + return data; + } + }; + + template + class alignas(GetDefaultSIMDSize()*sizeof(int64_t)) SIMD + { + static constexpr int N1 = std::min(GetDefaultSIMDSize(), N/2); + static constexpr int N2 = N-N1; + + SIMD lo; + SIMD high; + + public: + static constexpr int Size() { return N; } + + SIMD () {} + SIMD (const SIMD &) = default; + SIMD & operator= (const SIMD &) = default; + + SIMD (int val) : lo{val}, high{val} { ; } + SIMD (int64_t val) : lo{val}, high{val} { ; } + SIMD (size_t val) : lo{val}, high{val} { ; } + SIMD (SIMD lo_, SIMD high_) : lo(lo_), high(high_) { ; } + + explicit SIMD( std::array arr ) + : lo(detail::array_range(arr, 0)), + high(detail::array_range(arr, N1)) + {} + + template + explicit SIMD(const T... vals) + : lo(detail::array_range(std::array{vals...}, 0)), + high(detail::array_range(std::array{vals...}, N1)) + { + static_assert(sizeof...(vals)==N, "wrong number of arguments"); + } + + + template>::value, int>::type = 0> + SIMD (const T & func) + { + for(auto i : IntRange(N1)) + lo[i] = func(i); + for(auto i : IntRange(N2)) + high[i] = func(N1+i); + } + + auto Lo() const { return lo; } + auto Hi() const { return high; } + + int64_t operator[] (int i) const { return ((int64_t*)(&lo))[i]; } + + /* + operator tuple () + { return tuple((*this)[0], (*this)[1], (*this)[2], (*this)[3]); } + */ + + /* + static SIMD FirstInt() { return { 0, 1, 2, 3 }; } + */ + static SIMD FirstInt(int64_t n0=0) { return {SIMD::FirstInt(n0), SIMD::FirstInt(n0+N1)}; } + template + int64_t Get() + { + static_assert(I>=0 && I(); + else return high.template Get(); + } + }; + + + //////////////////////////////////////////////////////////////////////////// + // double + + template<> + class SIMD + { + double data; + + public: + static constexpr int Size() { return 1; } + SIMD () {} + SIMD (const SIMD &) = default; + SIMD & operator= (const SIMD &) = default; + SIMD (double val) { data = val; } + SIMD (int val) { data = val; } + SIMD (size_t val) { data = val; } + SIMD (double const * p) { data = *p; } + SIMD (double const * p, SIMD mask) { data = mask.Data() ? *p : 0.0; } + explicit SIMD (std::array arr) + : data{arr[0]} + {} + + template >::value,int>::type = 0> + SIMD (const T & func) + { + data = func(0); + } + + template >::value,int>::type = 0> + SIMD & operator= (const T & func) + { + data = func(0); + return *this; + } + + void Store (double * p) { *p = data; } + void Store (double * p, SIMD mask) { if (mask.Data()) *p = data; } + + double operator[] (int i) const { return ((double*)(&data))[i]; } + double Data() const { return data; } + template + double Get() + { + static_assert(I==0); + return data; + } + }; + + + template + class alignas(GetDefaultSIMDSize()*sizeof(double)) SIMD + { + static constexpr int N1 = std::min(GetDefaultSIMDSize(), N/2); + static constexpr int N2 = N-N1; + + SIMD lo; + SIMD high; + + public: + static constexpr int Size() { return N; } + SIMD () {} + SIMD (const SIMD &) = default; + SIMD (SIMD lo_, SIMD hi_) : lo(lo_), high(hi_) { ; } + + template >::value,int>::type = 0> + SIMD (const T & func) + { + double *p = (double*)this; + for(auto i : IntRange(N)) + p[i] = func(i); + } + + template >::value,int>::type = 0> + SIMD & operator= (const T & func) + { + double *p = (double*)this; + for(auto i : IntRange(N)) + p[i] = func(i); + return *this; + } + + + SIMD & operator= (const SIMD &) = default; + + SIMD (double val) : lo{val}, high{val} { ; } + SIMD (int val) : lo{val}, high{val} { ; } + SIMD (size_t val) : lo{val}, high{val} { ; } + + SIMD (double const * p) : lo{p}, high{p+N1} { ; } + SIMD (double const * p, SIMD mask) + : lo{p, mask.Lo()}, high{p+N1, mask.Hi()} + { } + SIMD (double * p) : lo{p}, high{p+N1} { ; } + SIMD (double * p, SIMD mask) + : lo{p, mask.Lo()}, high{p+N1, mask.Hi()} + { } + + explicit SIMD( std::array arr ) + : lo(detail::array_range(arr, 0)), + high(detail::array_range(arr, N1)) + {} + + template + explicit SIMD(const T... vals) + : lo(detail::array_range(std::array{vals...}, 0)), + high(detail::array_range(std::array{vals...}, N1)) + { + static_assert(sizeof...(vals)==N, "wrong number of arguments"); + } + + void Store (double * p) { lo.Store(p); high.Store(p+N1); } + void Store (double * p, SIMD mask) + { + lo.Store(p, mask.Lo()); + high.Store(p+N1, mask.Hi()); + } + + auto Lo() const { return lo; } + auto Hi() const { return high; } + + double operator[] (int i) const { return ((double*)(&lo))[i]; } + + template> + operator std::tuple () + { + double *p = (double*)this; + return std::tuple(p[0], p[1]); + } + + template> + operator std::tuple () + { return std::tuple((*this)[0], (*this)[1], (*this)[2], (*this)[3]); } + + template + double Get() + { + static_assert(I>=0 && I(); + else return high.template Get(); + } + auto Data() const { return *this; } + }; + + + // Generic operators for any arithmetic type/simd width + template + NETGEN_INLINE SIMD operator+ (SIMD a, SIMD b) { + if constexpr(N==1) return a.Data()+b.Data(); + else return { a.Lo()+b.Lo(), a.Hi()+b.Hi() }; + } + + template + NETGEN_INLINE SIMD operator- (SIMD a, SIMD b) { + if constexpr(N==1) return a.Data()-b.Data(); + else return { a.Lo()-b.Lo(), a.Hi()-b.Hi() }; + } + template + NETGEN_INLINE SIMD operator- (SIMD a) { + if constexpr(N==1) return -a.Data(); + else return { -a.Lo(), -a.Hi() }; + } + + template + NETGEN_INLINE SIMD operator* (SIMD a, SIMD b) { + if constexpr(N==1) return a.Data()*b.Data(); + else return { a.Lo()*b.Lo(), a.Hi()*b.Hi() }; + } + + template + NETGEN_INLINE SIMD operator/ (SIMD a, SIMD b) { + if constexpr(N==1) return a.Data()/b.Data(); + else return { a.Lo()/b.Lo(), a.Hi()/b.Hi() }; + } + + template + NETGEN_INLINE SIMD operator< (SIMD a, SIMD b) + { + if constexpr(N==1) return a.Data() < b.Data(); + else return { a.Lo() + NETGEN_INLINE SIMD operator<= (SIMD a, SIMD b) + { + if constexpr(N==1) return a.Data() <= b.Data(); + else return { a.Lo()<=b.Lo(), a.Hi()<=b.Hi() }; + } + + template + NETGEN_INLINE SIMD operator> (SIMD a, SIMD b) + { + if constexpr(N==1) return a.Data() > b.Data(); + else return { a.Lo()>b.Lo(), a.Hi()>b.Hi() }; + } + + template + NETGEN_INLINE SIMD operator>= (SIMD a, SIMD b) + { + if constexpr(N==1) return a.Data() >= b.Data(); + else return { a.Lo()>=b.Lo(), a.Hi()>=b.Hi() }; + } + + template + NETGEN_INLINE SIMD operator== (SIMD a, SIMD b) + { + if constexpr(N==1) return a.Data() == b.Data(); + else return { a.Lo()==b.Lo(), a.Hi()==b.Hi() }; + } + + template + NETGEN_INLINE SIMD operator!= (SIMD a, SIMD b) + { + if constexpr(N==1) return a.Data() != b.Data(); + else return { a.Lo()!=b.Lo(), a.Hi()!=b.Hi() }; + } + + // int64_t operators with scalar operand (implement overloads to allow implicit casts for second operand) + template + NETGEN_INLINE SIMD operator+ (SIMD a, int64_t b) { return a+SIMD(b); } + template + NETGEN_INLINE SIMD operator+ (int64_t a, SIMD b) { return SIMD(a)+b; } + template + NETGEN_INLINE SIMD operator- (int64_t a, SIMD b) { return SIMD(a)-b; } + template + NETGEN_INLINE SIMD operator- (SIMD a, int64_t b) { return a-SIMD(b); } + template + NETGEN_INLINE SIMD operator* (int64_t a, SIMD b) { return SIMD(a)*b; } + template + NETGEN_INLINE SIMD operator* (SIMD b, int64_t a) { return SIMD(a)*b; } + template + NETGEN_INLINE SIMD operator/ (SIMD a, int64_t b) { return a/SIMD(b); } + template + NETGEN_INLINE SIMD operator/ (int64_t a, SIMD b) { return SIMD(a)/b; } + template + NETGEN_INLINE SIMD & operator+= (SIMD & a, SIMD b) { a=a+b; return a; } + template + NETGEN_INLINE SIMD & operator+= (SIMD & a, int64_t b) { a+=SIMD(b); return a; } + template + NETGEN_INLINE SIMD & operator-= (SIMD & a, SIMD b) { a = a-b; return a; } + template + NETGEN_INLINE SIMD & operator-= (SIMD & a, int64_t b) { a-=SIMD(b); return a; } + template + NETGEN_INLINE SIMD & operator*= (SIMD & a, SIMD b) { a=a*b; return a; } + template + NETGEN_INLINE SIMD & operator*= (SIMD & a, int64_t b) { a*=SIMD(b); return a; } + template + NETGEN_INLINE SIMD & operator/= (SIMD & a, SIMD b) { a = a/b; return a; } + + // double operators with scalar operand (implement overloads to allow implicit casts for second operand) + template + NETGEN_INLINE SIMD operator+ (SIMD a, double b) { return a+SIMD(b); } + template + NETGEN_INLINE SIMD operator+ (double a, SIMD b) { return SIMD(a)+b; } + template + NETGEN_INLINE SIMD operator- (double a, SIMD b) { return SIMD(a)-b; } + template + NETGEN_INLINE SIMD operator- (SIMD a, double b) { return a-SIMD(b); } + template + NETGEN_INLINE SIMD operator* (double a, SIMD b) { return SIMD(a)*b; } + template + NETGEN_INLINE SIMD operator* (SIMD b, double a) { return SIMD(a)*b; } + template + NETGEN_INLINE SIMD operator/ (SIMD a, double b) { return a/SIMD(b); } + template + NETGEN_INLINE SIMD operator/ (double a, SIMD b) { return SIMD(a)/b; } + template + NETGEN_INLINE SIMD & operator+= (SIMD & a, SIMD b) { a=a+b; return a; } + template + NETGEN_INLINE SIMD & operator+= (SIMD & a, double b) { a+=SIMD(b); return a; } + template + NETGEN_INLINE SIMD & operator-= (SIMD & a, SIMD b) { a = a-b; return a; } + template + NETGEN_INLINE SIMD & operator-= (SIMD & a, double b) { a-=SIMD(b); return a; } + template + NETGEN_INLINE SIMD & operator*= (SIMD & a, SIMD b) { a=a*b; return a; } + template + NETGEN_INLINE SIMD & operator*= (SIMD & a, double b) { a*=SIMD(b); return a; } + template + NETGEN_INLINE SIMD & operator/= (SIMD & a, SIMD b) { a = a/b; return a; } + + // double functions + + template + NETGEN_INLINE SIMD L2Norm2 (SIMD a) { return a*a; } + template + NETGEN_INLINE SIMD Trans (SIMD a) { return a; } + + template + NETGEN_INLINE double HSum (SIMD a) + { + if constexpr(N==1) + return a.Data(); + else + return HSum(a.Lo()) + HSum(a.Hi()); + } + + NETGEN_INLINE double IfPos (double a, double b, double c) { return a>0 ? b : c; } + NETGEN_INLINE double IfZero (double a, double b, double c) { return a==0. ? b : c; } + + template + NETGEN_INLINE SIMD IfPos (SIMD a, SIMD b, SIMD c) + { + if constexpr(N==1) return a.Data()>0.0 ? b : c; + else return { IfPos(a.Lo(), b.Lo(), c.Lo()), IfPos(a.Hi(), b.Hi(), c.Hi())}; + + } + + template + NETGEN_INLINE SIMD IfZero (SIMD a, SIMD b, SIMD c) + { + if constexpr(N==1) return a.Data()==0.0 ? b : c; + else return { IfZero(a.Lo(), b.Lo(), c.Lo()), IfZero(a.Hi(), b.Hi(), c.Hi())}; + + } + + template + NETGEN_INLINE SIMD If (SIMD a, SIMD b, SIMD c) + { + if constexpr(N==1) return a.Data() ? b : c; + else return { If(a.Lo(), b.Lo(), c.Lo()), If(a.Hi(), b.Hi(), c.Hi())}; + + } + + // a*b+c + template + NETGEN_INLINE auto FMA(T1 a, T2 b, T3 c) + { + return c+a*b; + } + + template + NETGEN_INLINE auto FNMA(T1 a, T2 b, T3 c) + { + return c-a*b; + } + + // update form of fma + template + void FMAasm (SIMD a, SIMD b, SIMD & sum) + { + sum = FMA(a,b,sum); + } + + // update form of fms + template + void FNMAasm (SIMD a, SIMD b, SIMD & sum) + { + // sum -= a*b; + sum = FNMA(a,b,sum); + } + + + template + T get(SIMD a) { return a[i]; } + + template + NETGEN_INLINE void Iterate2 (FUNC f) + { + if constexpr (NUM > 1) Iterate2 (f); + if constexpr (NUM >= 1) f(std::integral_constant()); + } + + + template + ostream & operator<< (ostream & ost, SIMD simd) + { + /* + ost << simd[0]; + for (int i = 1; i < simd.Size(); i++) + ost << " " << simd[i]; + */ + Iterate2 ([&] (auto I) { + if (I.value != 0) ost << " "; + ost << get(simd); + }); + return ost; + } + + using std::sqrt; + template + NETGEN_INLINE ngcore::SIMD sqrt (ngcore::SIMD a) { + return ngcore::SIMD([a](int i)->double { return sqrt(a[i]); } ); + } + + using std::fabs; + template + NETGEN_INLINE ngcore::SIMD fabs (ngcore::SIMD a) { + return ngcore::SIMD([a](int i)->double { return fabs(a[i]); } ); + } + + using std::floor; + template + NETGEN_INLINE ngcore::SIMD floor (ngcore::SIMD a) { + return ngcore::SIMD([a](int i)->double { return floor(a[i]); } ); + } + + using std::ceil; + template + NETGEN_INLINE ngcore::SIMD ceil (ngcore::SIMD a) { + return ngcore::SIMD([a](int i)->double { return ceil(a[i]); } ); + } + + using std::exp; + template + NETGEN_INLINE ngcore::SIMD exp (ngcore::SIMD a) { + return ngcore::SIMD([a](int i)->double { return exp(a[i]); } ); + } + + using std::log; + template + NETGEN_INLINE ngcore::SIMD log (ngcore::SIMD a) { + return ngcore::SIMD([a](int i)->double { return log(a[i]); } ); + } + + using std::pow; + template + NETGEN_INLINE ngcore::SIMD pow (ngcore::SIMD a, double x) { + return ngcore::SIMD([a,x](int i)->double { return pow(a[i],x); } ); + } + + template + NETGEN_INLINE ngcore::SIMD pow (ngcore::SIMD a, ngcore::SIMD b) { + return ngcore::SIMD([a,b](int i)->double { return pow(a[i],b[i]); } ); + } + + using std::sin; + template + NETGEN_INLINE ngcore::SIMD sin (ngcore::SIMD a) { + return ngcore::SIMD([a](int i)->double { return sin(a[i]); } ); + } + + using std::cos; + template + NETGEN_INLINE ngcore::SIMD cos (ngcore::SIMD a) { + return ngcore::SIMD([a](int i)->double { return cos(a[i]); } ); + } + + using std::tan; + template + NETGEN_INLINE ngcore::SIMD tan (ngcore::SIMD a) { + return ngcore::SIMD([a](int i)->double { return tan(a[i]); } ); + } + + using std::atan; + template + NETGEN_INLINE ngcore::SIMD atan (ngcore::SIMD a) { + return ngcore::SIMD([a](int i)->double { return atan(a[i]); } ); + } + + using std::atan2; + template + NETGEN_INLINE ngcore::SIMD atan2 (ngcore::SIMD y, ngcore::SIMD x) { + return ngcore::SIMD([y,x](int i)->double { return atan2(y[i], x[i]); } ); + } + + using std::acos; + template + NETGEN_INLINE ngcore::SIMD acos (ngcore::SIMD a) { + return ngcore::SIMD([a](int i)->double { return acos(a[i]); } ); + } + + using std::asin; + template + NETGEN_INLINE ngcore::SIMD asin (ngcore::SIMD a) { + return ngcore::SIMD([a](int i)->double { return asin(a[i]); } ); + } + + using std::sinh; + template + NETGEN_INLINE ngcore::SIMD sinh (ngcore::SIMD a) { + return ngcore::SIMD([a](int i)->double { return sinh(a[i]); } ); + } + + using std::cosh; + template + NETGEN_INLINE ngcore::SIMD cosh (ngcore::SIMD a) { + return ngcore::SIMD([a](int i)->double { return cosh(a[i]); } ); + } + + template + using MultiSIMD = SIMD; + + template + NETGEN_INLINE auto Unpack (SIMD a, SIMD b) + { + if constexpr(N==1) + { + return std::make_tuple(SIMD{a.Data()}, SIMD{b.Data()} ); + } + else if constexpr(N==2) + { + return std::make_tuple(SIMD{ a.Lo(), b.Lo() }, + SIMD{ a.Hi(), b.Hi() }); + } + else + { + auto [a1,b1] = Unpack(a.Lo(), b.Lo()); + auto [a2,b2] = Unpack(a.Hi(), b.Hi()); + return std::make_tuple(SIMD{ a1, a2 }, + SIMD{ b1, b2 }); + } + } + +} + + +namespace std +{ + // structured binding support + template + struct tuple_size> : std::integral_constant {}; + template struct tuple_element> { using type = T; }; +} + +#endif // NETGEN_CORE_SIMD_GENERIC_HPP diff --git a/libsrc/core/simd_sse.hpp b/libsrc/core/simd_sse.hpp new file mode 100644 index 00000000..b6f9c61e --- /dev/null +++ b/libsrc/core/simd_sse.hpp @@ -0,0 +1,266 @@ +#ifndef NETGEN_CORE_SIMD_SSE_HPP +#define NETGEN_CORE_SIMD_SSE_HPP + +/**************************************************************************/ +/* File: simd_sse.hpp */ +/* Author: Joachim Schoeberl, Matthias Hochsteger */ +/* Date: 25. Mar. 16 */ +/**************************************************************************/ + +#include + +namespace ngcore +{ + + template <> + class SIMD + { + __m128i mask; + public: + SIMD (int i) + : mask(_mm_cmpgt_epi32(_mm_set1_epi32(i), + _mm_set_epi32(1, 1, 0, 0))) + { ; } + SIMD (__m128i _mask) : mask(_mask) { ; } + __m128i Data() const { return mask; } + static constexpr int Size() { return 2; } + static NETGEN_INLINE SIMD GetMaskFromBits (unsigned int i); + }; + + static SIMD masks_from_2bits[4] = { + _mm_set_epi32 (0,0,0,0), _mm_set_epi32 (0,0,-1,0), + _mm_set_epi32 (-1,0,0,0), _mm_set_epi32 (-1,0,-1,0), + }; + + NETGEN_INLINE SIMD SIMD :: GetMaskFromBits (unsigned int i) + { + return masks_from_2bits[i & 3]; + } + + + template<> + class alignas(16) SIMD + { + __m128i data; + + public: + static constexpr int Size() { return 2; } + SIMD () {} + SIMD (const SIMD &) = default; + SIMD (int64_t v0, int64_t v1) { data = _mm_set_epi64x(v1,v0); } + SIMD (std::array arr) + : data{_mm_set_epi64x(arr[1],arr[0])} + {} + + SIMD & operator= (const SIMD &) = default; + + SIMD (int64_t val) { data = _mm_set1_epi64x(val); } + SIMD (__m128i _data) { data = _data; } + + template>::value, int>::type = 0> + SIMD (const T & func) + { + data = _mm_set_epi64(func(1), func(0)); + } + + NETGEN_INLINE auto operator[] (int i) const { return ((int64_t*)(&data))[i]; } + NETGEN_INLINE __m128i Data() const { return data; } + NETGEN_INLINE __m128i & Data() { return data; } + static SIMD FirstInt(int n0=0) { return { n0, n0+1 }; } + }; + + + +NETGEN_INLINE SIMD operator-(SIMD a) { return _mm_sub_epi64(_mm_setzero_si128(), a.Data()); } +NETGEN_INLINE SIMD operator+ (SIMD a, SIMD b) { return _mm_add_epi64(a.Data(),b.Data()); } +NETGEN_INLINE SIMD operator- (SIMD a, SIMD b) { return _mm_sub_epi64(a.Data(),b.Data()); } + + + template<> + class alignas(16) SIMD + { + __m128d data; + + public: + static constexpr int Size() { return 2; } + SIMD () {} + SIMD (const SIMD &) = default; + SIMD (double v0, double v1) { data = _mm_set_pd(v1,v0); } + SIMD (std::array arr) + : data{_mm_set_pd(arr[1], arr[0])} + {} + + SIMD & operator= (const SIMD &) = default; + + SIMD (double val) { data = _mm_set1_pd(val); } + SIMD (int val) { data = _mm_set1_pd(val); } + SIMD (size_t val) { data = _mm_set1_pd(val); } + + SIMD (double const * p) { data = _mm_loadu_pd(p); } + SIMD (double const * p, SIMD mask) + { +#ifdef __AVX__ + data = _mm_maskload_pd(p, mask.Data()); +#else + // this versions segfaults if p points to the last allowed element + // happened on Mac with the new SparseCholesky-factorization + // data = _mm_and_pd(_mm_castsi128_pd(mask.Data()), _mm_loadu_pd(p)); + auto pmask = (int64_t*)&mask; + data = _mm_set_pd (pmask[1] ? p[1] : 0.0, pmask[0] ? p[0] : 0.0); +#endif + } + SIMD (__m128d _data) { data = _data; } + + void Store (double * p) { _mm_storeu_pd(p, data); } + void Store (double * p, SIMD mask) + { +#ifdef __AVX__ + _mm_maskstore_pd(p, mask.Data(), data); +#else + /* + _mm_storeu_pd (p, _mm_or_pd (_mm_and_pd(_mm_castsi128_pd(mask.Data()), data), + _mm_andnot_pd(_mm_castsi128_pd(mask.Data()), _mm_loadu_pd(p)))); + */ + auto pmask = (int64_t*)&mask; + if (pmask[0]) p[0] = (*this)[0]; + if (pmask[1]) p[1] = (*this)[1]; +#endif + } + + template>::value, int>::type = 0> + SIMD (const T & func) + { + data = _mm_set_pd(func(1), func(0)); + } + + NETGEN_INLINE double operator[] (int i) const { return ((double*)(&data))[i]; } + NETGEN_INLINE __m128d Data() const { return data; } + NETGEN_INLINE __m128d & Data() { return data; } + + operator std::tuple () + { + auto pdata = (double*)&data; + return std::tuple(pdata[0], pdata[1]); + } + }; + + NETGEN_INLINE SIMD operator- (SIMD a) { return _mm_xor_pd(a.Data(), _mm_set1_pd(-0.0)); } + NETGEN_INLINE SIMD operator+ (SIMD a, SIMD b) { return _mm_add_pd(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator- (SIMD a, SIMD b) { return _mm_sub_pd(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator* (SIMD a, SIMD b) { return _mm_mul_pd(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator/ (SIMD a, SIMD b) { return _mm_div_pd(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator* (double a, SIMD b) { return _mm_set1_pd(a)*b; } + NETGEN_INLINE SIMD operator* (SIMD b, double a) { return _mm_set1_pd(a)*b; } + + template<> + NETGEN_INLINE auto Unpack (SIMD a, SIMD b) + { + return std::make_tuple(SIMD(_mm_unpacklo_pd(a.Data(),b.Data())), + SIMD(_mm_unpackhi_pd(a.Data(),b.Data()))); + } + + NETGEN_INLINE __m128d my_mm_hadd_pd(__m128d a, __m128d b) { +#if defined(__SSE3__) || defined(__AVX__) + return _mm_hadd_pd(a,b); +#else + return _mm_add_pd( _mm_unpacklo_pd(a,b), _mm_unpackhi_pd(a,b) ); +#endif + } + +#ifndef __AVX__ + NETGEN_INLINE __m128i my_mm_cmpgt_epi64(__m128i a, __m128i b) { + auto res_lo = _mm_cvtsi128_si64(a) > _mm_cvtsi128_si64(b) ? -1:0; + auto res_hi = _mm_cvtsi128_si64(_mm_srli_si128(a,8)) > _mm_cvtsi128_si64(_mm_srli_si128(b,8)) ? -1 : 0; + return _mm_set_epi64x(res_hi,res_lo); + } +#else + NETGEN_INLINE __m128i my_mm_cmpgt_epi64(__m128i a, __m128i b) { + return _mm_cmpgt_epi64(a,b); + } +#endif + + + NETGEN_INLINE SIMD sqrt (SIMD a) { return _mm_sqrt_pd(a.Data()); } + NETGEN_INLINE SIMD fabs (SIMD a) { return _mm_max_pd(a.Data(), (-a).Data()); } + using std::floor; + NETGEN_INLINE SIMD floor (SIMD a) + { return ngcore::SIMD([&](int i)->double { return floor(a[i]); } ); } + using std::ceil; + NETGEN_INLINE SIMD ceil (SIMD a) + { return ngcore::SIMD([&](int i)->double { return ceil(a[i]); } ); } + + NETGEN_INLINE SIMD operator<= (SIMD a , SIMD b) + { return _mm_castpd_si128( _mm_cmple_pd(a.Data(),b.Data())); } + NETGEN_INLINE SIMD operator< (SIMD a , SIMD b) + { return _mm_castpd_si128( _mm_cmplt_pd(a.Data(),b.Data())); } + NETGEN_INLINE SIMD operator>= (SIMD a , SIMD b) + { return _mm_castpd_si128( _mm_cmpge_pd(a.Data(),b.Data())); } + NETGEN_INLINE SIMD operator> (SIMD a , SIMD b) + { return _mm_castpd_si128( _mm_cmpgt_pd(a.Data(),b.Data())); } + NETGEN_INLINE SIMD operator== (SIMD a , SIMD b) + { return _mm_castpd_si128( _mm_cmpeq_pd(a.Data(),b.Data())); } + NETGEN_INLINE SIMD operator!= (SIMD a , SIMD b) + { return _mm_castpd_si128( _mm_cmpneq_pd(a.Data(),b.Data())); } + + NETGEN_INLINE SIMD operator<= (SIMD a , SIMD b) + { return _mm_xor_si128(_mm_cmpgt_epi64(a.Data(),b.Data()),_mm_set1_epi32(-1)); } + NETGEN_INLINE SIMD operator< (SIMD a , SIMD b) + { return my_mm_cmpgt_epi64(b.Data(),a.Data()); } + NETGEN_INLINE SIMD operator>= (SIMD a , SIMD b) + { return _mm_xor_si128(_mm_cmpgt_epi64(b.Data(),a.Data()),_mm_set1_epi32(-1)); } + NETGEN_INLINE SIMD operator> (SIMD a , SIMD b) + { return my_mm_cmpgt_epi64(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator== (SIMD a , SIMD b) + { return _mm_cmpeq_epi64(a.Data(),b.Data()); } + NETGEN_INLINE SIMD operator!= (SIMD a , SIMD b) + { return _mm_xor_si128(_mm_cmpeq_epi64(a.Data(),b.Data()),_mm_set1_epi32(-1)); } + + + + NETGEN_INLINE SIMD operator&& (SIMD a, SIMD b) + { return _mm_castpd_si128(_mm_and_pd (_mm_castsi128_pd(a.Data()),_mm_castsi128_pd( b.Data()))); } + NETGEN_INLINE SIMD operator|| (SIMD a, SIMD b) + { return _mm_castpd_si128(_mm_or_pd (_mm_castsi128_pd(a.Data()), _mm_castsi128_pd(b.Data()))); } + NETGEN_INLINE SIMD operator! (SIMD a) + { return _mm_castpd_si128(_mm_xor_pd (_mm_castsi128_pd(a.Data()),_mm_castsi128_pd( _mm_cmpeq_epi64(a.Data(),a.Data())))); } +#ifdef __SSE4_1__ + NETGEN_INLINE SIMD If (SIMD a, SIMD b, SIMD c) + { return _mm_blendv_pd(c.Data(), b.Data(), _mm_castsi128_pd(a.Data())); } +#else + NETGEN_INLINE SIMD If (SIMD a, SIMD b, SIMD c) + { + return _mm_or_pd( + _mm_andnot_pd(_mm_castsi128_pd(a.Data()),c.Data()), + _mm_and_pd(b.Data(),_mm_castsi128_pd(a.Data())) + );} +#endif // __SSE4_1__ + + NETGEN_INLINE SIMD IfPos (SIMD a, SIMD b, SIMD c) + { return ngcore::SIMD([&](int i)->double { return a[i]>0 ? b[i] : c[i]; }); } + NETGEN_INLINE SIMD IfZero (SIMD a, SIMD b, SIMD c) + { return ngcore::SIMD([&](int i)->double { return a[i]==0. ? b[i] : c[i]; }); } + + + NETGEN_INLINE double HSum (SIMD sd) + { + return _mm_cvtsd_f64 (my_mm_hadd_pd (sd.Data(), sd.Data())); + } + + NETGEN_INLINE auto HSum (SIMD sd1, SIMD sd2) + { + __m128d hv2 = my_mm_hadd_pd(sd1.Data(), sd2.Data()); + return SIMD (hv2); + // return SIMD(_mm_cvtsd_f64 (hv2), _mm_cvtsd_f64(_mm_shuffle_pd (hv2, hv2, 3))); + } + + NETGEN_INLINE SIMD If(SIMD a, SIMD b, + SIMD c) { + return _mm_or_si128( + _mm_andnot_si128(a.Data(),c.Data()), + _mm_and_si128(b.Data(),a.Data()) + ); + } + +} + +#endif // NETGEN_CORE_SIMD_SSE_HPP diff --git a/libsrc/core/table.cpp b/libsrc/core/table.cpp index 62f544d0..1fbcda75 100644 --- a/libsrc/core/table.cpp +++ b/libsrc/core/table.cpp @@ -18,6 +18,19 @@ namespace ngcore size_t size = entrysize.Size(); size_t * index = new size_t[size+1]; + if (entrysize.Size() < 100) + { + size_t mysum = 0; + for (size_t i = 0; i < entrysize.Size(); i++) + { + index[i] = mysum; + mysum += entrysize[i]; + } + index[entrysize.Size()] = mysum; + return index; + } + + Array partial_sums(TaskManager::GetNumThreads()+1); partial_sums[0] = 0; ParallelJob @@ -54,7 +67,7 @@ namespace ngcore NGCORE_API size_t * TablePrefixSum64 (FlatArray entrysize) { return TablePrefixSum2 (entrysize); } - + /* BaseDynamicTable :: BaseDynamicTable (int size) : data(size) { @@ -88,7 +101,6 @@ namespace ngcore } } - BaseDynamicTable :: ~BaseDynamicTable () { if (oneblock) @@ -112,7 +124,7 @@ namespace ngcore } } - void BaseDynamicTable :: IncSize (int i, int elsize) + void BaseDynamicTable :: IncSize (IndexType i, int elsize) { if (i < 0 || i >= data.Size()) { @@ -135,7 +147,7 @@ namespace ngcore line.size++; } - void BaseDynamicTable :: DecSize (int i) + void BaseDynamicTable :: DecSize (IndexType i) { if (i < 0 || i >= data.Size()) { @@ -153,6 +165,7 @@ namespace ngcore line.size--; } + */ void FilteredTableCreator::Add (size_t blocknr, int data) { diff --git a/libsrc/core/table.hpp b/libsrc/core/table.hpp index 537ea2a4..34f40c93 100644 --- a/libsrc/core/table.hpp +++ b/libsrc/core/table.hpp @@ -20,76 +20,76 @@ namespace ngcore template -class FlatTable -{ -protected: - static constexpr IndexType BASE = IndexBASE(); - /// number of rows - size_t size; - /// pointer to first in row - size_t * index; - /// array of data - T * data; - -public: - FlatTable() = delete; - - NETGEN_INLINE FlatTable(size_t as, size_t * aindex, T * adata) - : size(as), index(aindex), data(adata) { ; } - - /// Size of table - NETGEN_INLINE size_t Size() const { return size; } - - /// Access entry - NETGEN_INLINE const FlatArray operator[] (IndexType i) const + class FlatTable { - i = i-BASE; - return FlatArray (index[i+1]-index[i], data+index[i]); - } + protected: + static constexpr IndexType BASE = IndexBASE(); + /// number of rows + size_t size; + /// pointer to first in row + size_t * index; + /// array of data + T * data; - NETGEN_INLINE T * Data() const { return data; } - - NETGEN_INLINE FlatArray AsArray() const - { - return FlatArray (index[size]-index[0], data+index[0]); - } - - NETGEN_INLINE FlatArray IndexArray() const - { - return FlatArray (size+1, index); - } - - /// takes range starting from position start of end-start elements - NETGEN_INLINE FlatTable Range (size_t start, size_t end) const - { - return FlatTable (end-start, index+start-BASE, data); - } - - /// takes range starting from position start of end-start elements - NETGEN_INLINE FlatTable Range (T_Range range) const - { - return FlatTable (range.Size(), index+range.First()-BASE, data); - } - - NETGEN_INLINE T_Range Range () const - { - return T_Range (BASE, size+BASE); - } - - class Iterator - { - const FlatTable & tab; - size_t row; public: - Iterator (const FlatTable & _tab, size_t _row) : tab(_tab), row(_row) { ; } - Iterator & operator++ () { ++row; return *this; } - FlatArray operator* () const { return tab[row]; } - bool operator!= (const Iterator & it2) { return row != it2.row; } - }; + FlatTable() = delete; - Iterator begin() const { return Iterator(*this, BASE); } - Iterator end() const { return Iterator(*this, BASE+size); } -}; + NETGEN_INLINE FlatTable(size_t as, size_t * aindex, T * adata) + : size(as), index(aindex), data(adata) { ; } + + /// Size of table + NETGEN_INLINE size_t Size() const { return size; } + + /// Access entry + NETGEN_INLINE const FlatArray operator[] (IndexType i) const + { + i = i-BASE; + return FlatArray (index[i+1]-index[i], data+index[i]); + } + + NETGEN_INLINE T * Data() const { return data; } + + NETGEN_INLINE FlatArray AsArray() const + { + return FlatArray (index[size]-index[0], data+index[0]); + } + + NETGEN_INLINE FlatArray IndexArray() const + { + return FlatArray (size+1, index); + } + + /// takes range starting from position start of end-start elements + NETGEN_INLINE FlatTable Range (size_t start, size_t end) const + { + return FlatTable (end-start, index+start-BASE, data); + } + + /// takes range starting from position start of end-start elements + NETGEN_INLINE FlatTable Range (T_Range range) const + { + return FlatTable (range.Size(), index+range.First()-BASE, data); + } + + NETGEN_INLINE T_Range Range () const + { + return T_Range (BASE, size+BASE); + } + + class Iterator + { + const FlatTable & tab; + size_t row; + public: + Iterator (const FlatTable & _tab, size_t _row) : tab(_tab), row(_row) { ; } + Iterator & operator++ () { ++row; return *this; } + FlatArray operator* () const { return tab[row]; } + bool operator!= (const Iterator & it2) { return row != it2.row; } + }; + + Iterator begin() const { return Iterator(*this, BASE); } + Iterator end() const { return Iterator(*this, BASE+size); } + }; NGCORE_API extern size_t * TablePrefixSum32 (FlatArray entrysize); NGCORE_API extern size_t * TablePrefixSum64 (FlatArray entrysize); @@ -105,106 +105,119 @@ public: { return TablePrefixSum64 (entrysize); } -/** - A compact Table container. - A table contains size entries of variable size. - The entry sizes must be known at construction. -*/ + /** + A compact Table container. + A table contains size entries of variable size. + The entry sizes must be known at construction. + */ template class Table : public FlatTable -{ -protected: - - using FlatTable::size; - using FlatTable::index; - using FlatTable::data; - -public: - /// - NETGEN_INLINE Table () : FlatTable (0,nullptr,nullptr) { ; } - /// Construct table of uniform entrysize - NETGEN_INLINE Table (size_t asize, size_t entrysize) - : FlatTable( asize, new size_t[asize+1], new T[asize*entrysize] ) { - for (size_t i : IntRange(size+1)) - index[i] = i*entrysize; - } + protected: - /// Construct table of variable entrysize - template - NETGEN_INLINE Table (FlatArray entrysize) - : FlatTable (0, nullptr, nullptr) - { - size = entrysize.Size(); - index = TablePrefixSum (FlatArray (entrysize.Size(), entrysize.Data())); - size_t cnt = index[size]; - data = new T[cnt]; - } + using FlatTable::size; + using FlatTable::index; + using FlatTable::data; - explicit NETGEN_INLINE Table (const Table & tab2) - : FlatTable(0, nullptr, nullptr) - { - size = tab2.Size(); + public: + /// + NETGEN_INLINE Table () : FlatTable (0,nullptr,nullptr) { ; } + /// Construct table of uniform entrysize + NETGEN_INLINE Table (size_t asize, size_t entrysize) + : FlatTable( asize, new size_t[asize+1], new T[asize*entrysize] ) + { + for (size_t i : IntRange(size+1)) + index[i] = i*entrysize; + } - index = new size_t[size+1]; - for (size_t i = 0; i <= size; i++) - index[i] = tab2.index[i]; + /// Construct table of variable entrysize + template + NETGEN_INLINE Table (FlatArray entrysize) + : FlatTable (0, nullptr, nullptr) + { + size = entrysize.Size(); + index = TablePrefixSum (FlatArray (entrysize.Size(), entrysize.Data())); + size_t cnt = index[size]; + data = new T[cnt]; + } - size_t cnt = index[size]; - data = new T[cnt]; - for (size_t i = 0; i < cnt; i++) - data[i] = tab2.data[i]; - } + explicit NETGEN_INLINE Table (const Table & tab2) + : FlatTable(0, nullptr, nullptr) + { + size = tab2.Size(); - NETGEN_INLINE Table (Table && tab2) - : FlatTable(0, nullptr, nullptr) - { - Swap (size, tab2.size); - Swap (index, tab2.index); - Swap (data, tab2.data); - } + index = new size_t[size+1]; + for (size_t i = 0; i <= size; i++) + index[i] = tab2.index[i]; - NETGEN_INLINE Table & operator= (Table && tab2) - { - Swap (size, tab2.size); - Swap (index, tab2.index); - Swap (data, tab2.data); - return *this; - } + size_t cnt = index[size]; + data = new T[cnt]; + for (size_t i = 0; i < cnt; i++) + data[i] = tab2.data[i]; + } + + NETGEN_INLINE Table (Table && tab2) + : FlatTable(0, nullptr, nullptr) + { + tab2.mt.Free(tab2.GetMemUsage()); + Swap (size, tab2.size); + Swap (index, tab2.index); + Swap (data, tab2.data); + } + + NETGEN_INLINE Table & operator= (Table && tab2) + { + mt.Swap(GetMemUsage(), tab2.mt, tab2.GetMemUsage()); + Swap (size, tab2.size); + Swap (index, tab2.index); + Swap (data, tab2.data); + return *this; + } - /// Delete data - NETGEN_INLINE ~Table () - { - delete [] data; - delete [] index; - } + /// Delete data + NETGEN_INLINE ~Table () + { + mt.Free(GetMemUsage()); + delete [] data; + delete [] index; + } - /// Size of table - using FlatTable::Size; + /// Size of table + using FlatTable::Size; - /// number of elements in all rows - NETGEN_INLINE size_t NElements() const { return index[size]; } + /// number of elements in all rows + NETGEN_INLINE size_t NElements() const { return index[size]; } - using FlatTable::operator[]; -}; + using FlatTable::operator[]; + + NETGEN_INLINE void StartMemoryTracing (int /* mem_id */) + { + mt.Alloc(GetMemUsage()); + } + const MemoryTracer& GetMemoryTracer() const { return mt; } + + private: + size_t GetMemUsage() const { return size == 0 ? 0 : sizeof(T)*index[size] + sizeof(IndexType) * size+1; } + MemoryTracer mt; + }; -/// Print table + /// Print table template inline ostream & operator<< (ostream & s, const Table & table) -{ - for (auto i : table.Range()) - { - s << i << ":"; - for (auto el : table[i]) - s << " " << el; - s << "\n"; - } - s << std::flush; - return s; -} + { + for (auto i : table.Range()) + { + s << i << ":"; + for (auto el : table[i]) + s << " " << el; + s << "\n"; + } + s << std::flush; + return s; + } @@ -349,142 +362,262 @@ public: }; - /// Base class to generic DynamicTable. - class BaseDynamicTable - { - protected: - - /// - struct linestruct - { - /// - int size; - /// - int maxsize; - /// - void * col; - }; - - /// - Array data; - /// - char * oneblock; - - public: - /// - NGCORE_API BaseDynamicTable (int size); - /// - NGCORE_API BaseDynamicTable (const Array & entrysizes, int elemsize); - /// - NGCORE_API ~BaseDynamicTable (); - - /// Changes Size of table to size, deletes data - NGCORE_API void SetSize (int size); - /// - NGCORE_API void IncSize (int i, int elsize); - - NGCORE_API void DecSize (int i); - }; - - /** - A dynamic table class. + A dynamic table class. - A DynamicTable contains entries of variable size. Entry sizes can - be increased dynamically. + A DynamicTable contains entries of variable size. Entry sizes can + be increased dynamically. */ - template - class DynamicTable : public BaseDynamicTable + template + class DynamicTable { + protected: + static constexpr IndexType BASE = IndexBASE(); + + struct linestruct + { + int size; + int maxsize; + T * col; + }; + + Array data; + T * oneblock = nullptr; + public: /// Creates table of size size DynamicTable (int size = 0) - : BaseDynamicTable (size) { ; } - - /// Creates table with a priori fixed entry sizes. - DynamicTable (const Array & entrysizes) - : BaseDynamicTable (entrysizes, sizeof(T)) { ; } - - /// Inserts element acont into row i. Does not test if already used. - void Add (int i, const T & acont) + : data(size) { - if (data[i].size == data[i].maxsize) - IncSize (i, sizeof (T)); - else - data[i].size++; - static_cast (data[i].col) [data[i].size-1] = acont; + for (auto & d : data) + { + d.maxsize = 0; + d.size = 0; + d.col = nullptr; + } + oneblock = nullptr; } + /// Creates table with a priori fixed entry sizes. + DynamicTable (const Array & entrysizes) + : data(entrysizes.Size()) + { + size_t cnt = 0; + // size_t n = entrysizes.Size(); + + for (auto es : entrysizes) + cnt += es; + oneblock = new T[cnt]; + + cnt = 0; + for (auto i : data.Range()) + { + data[i].maxsize = entrysizes[i]; + data[i].size = 0; + data[i].col = &oneblock[cnt]; + cnt += entrysizes[i]; + } + } + + DynamicTable (DynamicTable && tab2) + { + Swap (data, tab2.data); + Swap (oneblock, tab2.oneblock); + } + + ~DynamicTable () + { + if (oneblock) + delete [] oneblock; + else + for (auto & d : data) + delete [] d.col; + } + + DynamicTable & operator= (DynamicTable && tab2) + { + Swap (data, tab2.data); + Swap (oneblock, tab2.oneblock); + return *this; + } + + /// Changes Size of table to size, deletes data + void SetSize (int size) + { + for (auto & d : data) + delete [] d.col; + + data.SetSize(size); + for (auto & d : data) + { + d.maxsize = 0; + d.size = 0; + d.col = nullptr; + } + } + + void ChangeSize (size_t size) + { + if (oneblock) + throw Exception ("cannot change size of oneblock dynamic table"); + + size_t oldsize = data.Size(); + if (size == oldsize) + return; + + if (size < oldsize) + for (int i = size; i < oldsize; i++) + delete [] data[i+BASE].col; + + data.SetSize(size); + + for (int i = oldsize; i < size; i++) + { + data[i+BASE].maxsize = 0; + data[i+BASE].size = 0; + data[i+BASE].col = nullptr; + } + } + + + + /// + void IncSize (IndexType i) + { + NETGEN_CHECK_RANGE(i,BASE,data.Size()+BASE); + + linestruct & line = data[i]; + + if (line.size == line.maxsize) + { + T * p; + if constexpr (std::is_default_constructible::value) + p = new T[(2*line.maxsize+5)]; + else + p = reinterpret_cast(new char[(2*line.maxsize+5)*sizeof(T)]); + for (size_t i = 0; i < line.maxsize; i++) + p[i] = std::move(line.col[i]); + // memcpy (p, line.col, line.maxsize * sizeof(T)); + delete [] line.col; + line.col = p; + line.maxsize = 2*line.maxsize+5; + } + + line.size++; + } + + void DecSize (IndexType i) + { + NETGEN_CHECK_RANGE(i,BASE,data.Size()+BASE); + linestruct & line = data[i]; + +#ifdef NETGEN_ENABLE_CHECK_RANGE + if (line.size == 0) + throw Exception ("BaseDynamicTable::Dec: EntrySize < 0"); +#endif + + line.size--; + } + + + /// Inserts element acont into row i. Does not test if already used. + void Add (IndexType i, const T & acont) + { + if (data[i].size == data[i].maxsize) + this->IncSize (i); + else + data[i].size++; + data[i].col[data[i].size-1] = acont; + } + /// Inserts element acont into row i, iff not yet exists. - void AddUnique (int i, const T & cont) + void AddUnique (IndexType i, const T & cont) { int es = EntrySize (i); - int * line = const_cast (GetLine (i)); + T * line = data[i].col; for (int j = 0; j < es; j++) if (line[j] == cont) return; Add (i, cont); } - + /// Inserts element acont into row i. Does not test if already used. - void AddEmpty (int i) + void AddEmpty (IndexType i) { - IncSize (i, sizeof (T)); + IncSize (i); } - + /** Set the nr-th element in the i-th row to acont. Does not check for overflow. */ - void Set (int i, int nr, const T & acont) - { static_cast (data[i].col)[nr] = acont; } + void Set (IndexType i, int nr, const T & acont) + { + data[i].col[nr] = acont; + } /** Returns the nr-th element in the i-th row. - Does not check for overflow. */ - const T & Get (int i, int nr) const - { return static_cast (data[i].col)[nr]; } - - + Does not check for overflow. */ + const T & Get (IndexType i, int nr) const + { + return data[i].col[nr]; + } + + /** Returns pointer to the first element in row i. */ - const T * GetLine (int i) const - { return static_cast (data[i].col); } - - + const T * GetLine (IndexType i) const + { + return data[i].col; + } + /// Returns size of the table. - int Size () const - { return data.Size(); } + size_t Size () const + { + return data.Size(); + } + + auto Range () const + { + return data.Range(); + } /// Returns size of the i-th row. - int EntrySize (int i) const - { return data[i].size; } + int EntrySize (IndexType i) const + { + return data[i].size; + } /// - void DecEntrySize (int i) - { DecSize(i); } - + void DecEntrySize (IndexType i) + { + DecSize(i); + } + /// Access entry i - FlatArray operator[] (int i) - { return FlatArray (data[i].size, static_cast (data[i].col)); } - + FlatArray operator[] (IndexType i) + { + return FlatArray (data[i].size, data[i].col); + } + /* - typedef const FlatArray ConstFlatArray; - /// Access entry i - ConstFlatArray operator[] (int i) const - { return FlatArray (data[i].size, static_cast (data[i].col)); } + typedef const FlatArray ConstFlatArray; + /// Access entry i + ConstFlatArray operator[] (int i) const + { return FlatArray (data[i].size, static_cast (data[i].col)); } */ - FlatArray operator[] (int i) const - { return FlatArray (data[i].size, static_cast (data[i].col)); } + FlatArray operator[] (IndexType i) const + { + return FlatArray (data[i].size, data[i].col); + } }; - - /// Print table template inline ostream & operator<< (ostream & s, const DynamicTable & table) { - for (int i = 0; i < table.Size(); i++) + for (auto i : Range(table)) { s << i << ":"; for (int j = 0; j < table[i].Size(); j++) diff --git a/libsrc/core/taskmanager.cpp b/libsrc/core/taskmanager.cpp index e730e55c..1d88b766 100644 --- a/libsrc/core/taskmanager.cpp +++ b/libsrc/core/taskmanager.cpp @@ -159,29 +159,18 @@ namespace ngcore active_workers = 0; static int cnt = 0; - char buf[100]; if (use_paje_trace) - { -#ifdef PARALLEL - int is_init = -1; - MPI_Initialized(&is_init); - if (is_init) - sprintf(buf, "ng%d_rank%d.trace", cnt++, NgMPI_Comm(MPI_COMM_WORLD).Rank()); - else -#endif - sprintf(buf, "ng%d.trace", cnt++); - } - else - buf[0] = 0; - //sprintf(buf, ""); - trace = new PajeTrace(num_threads, buf); + trace = new PajeTrace(num_threads, "ng" + ToString(cnt++)); } TaskManager :: ~TaskManager () { - delete trace; - trace = nullptr; + if (use_paje_trace) + { + delete trace; + trace = nullptr; + } num_threads = 1; } @@ -212,14 +201,14 @@ namespace ngcore ; } - static size_t calibrate_init_tsc = __rdtsc(); + static size_t calibrate_init_tsc = GetTimeCounter(); typedef std::chrono::system_clock TClock; static TClock::time_point calibrate_init_clock = TClock::now(); void TaskManager :: StopWorkers() { done = true; - double delta_tsc = __rdtsc()-calibrate_init_tsc; + double delta_tsc = GetTimeCounter()-calibrate_init_tsc; double delta_sec = std::chrono::duration(TClock::now()-calibrate_init_clock).count(); double frequ = (delta_sec != 0) ? delta_tsc/delta_sec : 2.7e9; @@ -348,8 +337,28 @@ namespace ngcore return; } + if (antasks == 1) + { + if (trace) + trace->StartJob(jobnr, afunc.target_type()); + jobnr++; + if (startup_function) (*startup_function)(); + TaskInfo ti; + ti.task_nr = 0; + ti.ntasks = 1; + ti.thread_nr = 0; ti.nthreads = 1; + { + RegionTracer t(ti.thread_nr, jobnr, RegionTracer::ID_JOB, ti.task_nr); + afunc(ti); + } + if (cleanup_function) (*cleanup_function)(); + if (trace) + trace->StopJob(); + return; + } - trace->StartJob(jobnr, afunc.target_type()); + if (trace) + trace->StartJob(jobnr, afunc.target_type()); func = &afunc; @@ -412,14 +421,19 @@ namespace ngcore if (workers_on_node[j]) { while (complete[j] != jobnr) + { +#ifdef NETGEN_ARCH_AMD64 _mm_pause(); +#endif // NETGEN_ARCH_AMD64 + } } func = nullptr; if (ex) throw Exception (*ex); - trace->StopJob(); + if (trace) + trace->StopJob(); } void TaskManager :: Loop(int thd) diff --git a/libsrc/core/taskmanager.hpp b/libsrc/core/taskmanager.hpp index 4ba656c3..7025ce3d 100644 --- a/libsrc/core/taskmanager.hpp +++ b/libsrc/core/taskmanager.hpp @@ -17,6 +17,12 @@ #include "paje_trace.hpp" #include "profiler.hpp" +#ifdef USE_NUMA +#include +#include +#endif + + namespace ngcore { using std::atomic; @@ -1062,11 +1068,11 @@ public: { static Timer timer("ComputeColoring - "+Demangle(typeid(Tmask).name())); RegionTimer rt(timer); static_assert(sizeof(unsigned int)==4, "Adapt type of mask array"); - auto n = colors.Size(); + size_t n = colors.Size(); Array mask(ndofs); - int colored_blocks = 0; + size_t colored_blocks = 0; // We are coloring with 32 colors at once and use each bit to mask conflicts unsigned int check = 0; diff --git a/libsrc/core/utils.cpp b/libsrc/core/utils.cpp index bf355bc1..cfa3fef8 100644 --- a/libsrc/core/utils.cpp +++ b/libsrc/core/utils.cpp @@ -13,7 +13,15 @@ namespace ngcore #ifdef WIN32 // windows does demangling in typeid(T).name() - NGCORE_API std::string Demangle(const char* typeinfo) { return typeinfo; } + NGCORE_API std::string Demangle(const char* typeinfo) { + std::string name = typeinfo; + // remove "class " and "struct " at beginning of type names to be consistent with demangled names of gcc/clang + if(name.find("class ") == 0) + name.erase(0,6); + if(name.find("struct ") == 0) + name.erase(0,7); + return name; + } #else NGCORE_API std::string Demangle(const char* typeinfo) { diff --git a/libsrc/core/utils.hpp b/libsrc/core/utils.hpp index 65db4fff..102ff319 100644 --- a/libsrc/core/utils.hpp +++ b/libsrc/core/utils.hpp @@ -8,13 +8,19 @@ #include #include +#include "ngcore_api.hpp" // for NGCORE_API and CPU arch macros + +#if defined(__APPLE__) && defined(NETGEN_ARCH_ARM64) +#include +#endif + +#ifdef NETGEN_ARCH_AMD64 #ifdef WIN32 #include // for __rdtsc() CPU time step counter #else #include // for __rdtsc() CPU time step counter #endif // WIN32 - -#include "ngcore_api.hpp" // for NGCORE_API +#endif // NETGEN_ARCH_AMD64 namespace ngcore { @@ -23,6 +29,10 @@ namespace ngcore NGCORE_API std::string Demangle(const char* typeinfo); + template + std::string GetName(const T& obj) + { return Demangle(typeid(obj).name()); } + #if defined(__GNUC__) inline bool likely (bool x) { return bool(__builtin_expect(long(x), 1L)); } inline bool unlikely (bool x) { return bool(__builtin_expect(long(x), 0L)); } @@ -48,7 +58,19 @@ namespace ngcore inline TTimePoint GetTimeCounter() noexcept { - return TTimePoint(__rdtsc()); +#if defined(__APPLE__) && defined(NETGEN_ARCH_ARM64) + return mach_absolute_time(); +#elif defined(NETGEN_ARCH_AMD64) + return __rdtsc(); +#elif defined(NETGEN_ARCH_ARM64) && defined(__GNUC__) + // __GNUC__ is also defined by CLANG. Use inline asm to read Generic Timer + unsigned long long tics; + __asm __volatile("mrs %0, CNTVCT_EL0" : "=&r" (tics)); + return tics; +#else +#warning "Unsupported CPU architecture" + return 0; +#endif } template @@ -157,7 +179,9 @@ namespace ngcore while (!m.compare_exchange_weak(should, true)) { should = false; +#ifdef NETGEN_ARCH_AMD64 _mm_pause(); +#endif // NETGEN_ARCH_AMD64 } } void unlock() diff --git a/libsrc/core/version.cpp b/libsrc/core/version.cpp new file mode 100644 index 00000000..546abbf7 --- /dev/null +++ b/libsrc/core/version.cpp @@ -0,0 +1,29 @@ +#include + +#include +#include "exception.hpp" +#include "version.hpp" + +namespace ngcore +{ + // clang-tidy should ignore this static object + static std::map library_versions; // NOLINT + + const VersionInfo& GetLibraryVersion(const std::string& library) + { return library_versions[library]; } + + const std::map& GetLibraryVersions() + { return library_versions; } + + void SetLibraryVersion(const std::string& library, const VersionInfo& version) + { + if(library_versions.count(library) && (library_versions[library] != version)) + throw Exception("Failed to set library version for " + library + " to " + version.to_string() + ": version already set to " + library_versions[library].to_string()); + library_versions[library] = version; + } + + static bool dummy = [](){ + SetLibraryVersion("netgen", NETGEN_VERSION); + return true; + }(); +} // namespace ngcore diff --git a/libsrc/core/version.hpp b/libsrc/core/version.hpp index aea50bf6..3048ce5b 100644 --- a/libsrc/core/version.hpp +++ b/libsrc/core/version.hpp @@ -80,6 +80,10 @@ namespace ngcore return mayor_ == other.mayor_ && minor_ == other.minor_ && release == other.release && patch == other.patch; } + bool operator !=(const VersionInfo& other) const + { + return !(*this==other); + } bool operator >(const VersionInfo& other) const { return other < (*this); } bool operator <=(const VersionInfo& other) const { return !((*this) > other); } bool operator >=(const VersionInfo& other) const { return !((*this) < other); } @@ -89,6 +93,10 @@ namespace ngcore { return ost << version.to_string(); } + + NGCORE_API const VersionInfo& GetLibraryVersion(const std::string& library); + NGCORE_API const std::map& GetLibraryVersions(); + NGCORE_API void SetLibraryVersion(const std::string& library, const VersionInfo& version); } // namespace ngcore #endif // NETGEN_CORE_VERSION_HPP diff --git a/libsrc/core/xbool.hpp b/libsrc/core/xbool.hpp index 31ca8730..fd0da95e 100644 --- a/libsrc/core/xbool.hpp +++ b/libsrc/core/xbool.hpp @@ -20,17 +20,27 @@ namespace ngcore public: xbool (bool b) : state(b ? 2 : 0) { ; } - xbool (TMAYBE x) : state(1) { ; } + xbool (TMAYBE /* x */) : state(1) { ; } xbool () = default; xbool (const xbool &) = default; xbool & operator= (bool b) { state = b ? 2 : 0; return *this; } - xbool & operator= (TMAYBE x) { state = 1; return *this; } + xbool & operator= (TMAYBE /* x */) { state = 1; return *this; } bool IsTrue () const { return state == 2; } bool IsMaybe () const { return state == 1; } bool IsFalse () const { return state == 0; } + bool IsMaybeTrue() const { return state >= 1; } + bool IsMaybeFalse() const { return state <= 1; } + friend ostream & operator<< (ostream & ost, xbool xb); }; + + + static char output[] = "0?1"; + inline ostream & operator<< (ostream & ost, xbool xb) + { + return ost << output[xb.state]; + } } // namespace ngcore diff --git a/libsrc/csg/CMakeLists.txt b/libsrc/csg/CMakeLists.txt index 7d196b5b..52514038 100644 --- a/libsrc/csg/CMakeLists.txt +++ b/libsrc/csg/CMakeLists.txt @@ -11,7 +11,7 @@ if(APPLE) set_target_properties( csg PROPERTIES SUFFIX ".so") endif(APPLE) -target_link_libraries(csg PUBLIC mesh PRIVATE netgen_python) +target_link_libraries(csg PUBLIC mesh PRIVATE "$") if(NOT WIN32) install( TARGETS csg ${NG_INSTALL_DIR}) endif(NOT WIN32) @@ -20,7 +20,7 @@ target_link_libraries(csg PUBLIC ngcore) if(USE_GUI) add_library(csgvis ${NG_LIB_TYPE} vscsg.cpp ) - target_link_libraries(csgvis PRIVATE netgen_python PUBLIC ngcore) + target_link_libraries(csgvis PRIVATE "$" PUBLIC ngcore) if(NOT WIN32) target_link_libraries(csgvis PUBLIC csg visual) if(APPLE) diff --git a/libsrc/csg/csgeom.cpp b/libsrc/csg/csgeom.cpp index 4874f0a9..e1ada634 100644 --- a/libsrc/csg/csgeom.cpp +++ b/libsrc/csg/csgeom.cpp @@ -130,6 +130,7 @@ namespace netgen Point<3> & newp, EdgePointGeomInfo & newgi) const { Point<3> hnewp = p1+secpoint*(p2-p1); + //(*testout) << "hnewp " << hnewp << " s1 " << surfi1 << " s2 " << surfi2 << endl; if (surfi1 != -1 && surfi2 != -1 && surfi1 != surfi2) { @@ -175,9 +176,8 @@ namespace netgen solids.DeleteAll (); - for (int i = 0; i < splinecurves2d.Size(); i++) - delete splinecurves2d[i]; splinecurves2d.DeleteAll(); + splinecurves3d.DeleteAll(); /* for (int i = 0; i < surfaces.Size(); i++) @@ -711,24 +711,24 @@ namespace netgen - void CSGeometry :: SetSplineCurve (const char * name, SplineGeometry<2> * spl) + void CSGeometry :: SetSplineCurve (const char * name, shared_ptr> spl) { splinecurves2d.Set(name,spl); } - void CSGeometry :: SetSplineCurve (const char * name, SplineGeometry<3> * spl) + void CSGeometry :: SetSplineCurve (const char * name, shared_ptr> spl) { splinecurves3d.Set(name,spl); } - const SplineGeometry<2> * CSGeometry :: GetSplineCurve2d (const string & name) const + shared_ptr> CSGeometry :: GetSplineCurve2d (const string & name) const { if (splinecurves2d.Used(name)) return splinecurves2d[name]; else return NULL; } - const SplineGeometry<3> * CSGeometry :: GetSplineCurve3d (const string & name) const + shared_ptr> CSGeometry :: GetSplineCurve3d (const string & name) const { if (splinecurves3d.Used(name)) return splinecurves3d[name]; diff --git a/libsrc/csg/csgeom.hpp b/libsrc/csg/csgeom.hpp index 35453b30..17d0a3d9 100644 --- a/libsrc/csg/csgeom.hpp +++ b/libsrc/csg/csgeom.hpp @@ -115,9 +115,9 @@ namespace netgen SymbolTable solids; /// all 2d splinecurves - SymbolTable< SplineGeometry<2>* > splinecurves2d; + SymbolTable>> splinecurves2d; /// all 3d splinecurves - SymbolTable< SplineGeometry<3>* > splinecurves3d; + SymbolTable>> splinecurves3d; /// all top level objects: solids and surfaces NgArray toplevelobjects; @@ -232,10 +232,10 @@ namespace netgen const SymbolTable & GetSolids () const { return solids; } - void SetSplineCurve (const char * name, SplineGeometry<2> * spl); - void SetSplineCurve (const char * name, SplineGeometry<3> * spl); - const SplineGeometry<2> * GetSplineCurve2d (const string & name) const; - const SplineGeometry<3> * GetSplineCurve3d (const string & name) const; + void SetSplineCurve (const char * name, shared_ptr> spl); + void SetSplineCurve (const char * name, shared_ptr> spl); + shared_ptr> GetSplineCurve2d (const string & name) const; + shared_ptr> GetSplineCurve3d (const string & name) const; void DoArchive(Archive& archive) override; diff --git a/libsrc/csg/csgparser.cpp b/libsrc/csg/csgparser.cpp index 4e3c1786..05c1bdcd 100644 --- a/libsrc/csg/csgparser.cpp +++ b/libsrc/csg/csgparser.cpp @@ -511,8 +511,8 @@ namespace netgen break; } - Primitive * nprim = new Extrusion(*(geom->GetSplineCurve3d(epath)), - *(geom->GetSplineCurve2d(profile)), + Primitive * nprim = new Extrusion(geom->GetSplineCurve3d(epath), + geom->GetSplineCurve2d(profile), z_dir); geom->AddSurfaces (nprim); return new Solid(nprim); @@ -1186,7 +1186,7 @@ namespace netgen ParseChar (scan, '='); ParseChar (scan, '('); - SplineGeometry<2> * newspline = new SplineGeometry<2>; + auto newspline = make_shared>(); // newspline->CSGLoad(scan); LoadSpline (*newspline, scan); @@ -1212,7 +1212,7 @@ namespace netgen ParseChar (scan, '='); ParseChar (scan, '('); - SplineGeometry<3> * newspline = new SplineGeometry<3>; + auto newspline = make_shared>(); // newspline->CSGLoad(scan); LoadSpline (*newspline, scan); diff --git a/libsrc/csg/edgeflw.cpp b/libsrc/csg/edgeflw.cpp index d89e2fd9..6746ecf8 100644 --- a/libsrc/csg/edgeflw.cpp +++ b/libsrc/csg/edgeflw.cpp @@ -970,7 +970,7 @@ namespace netgen for (int i = 0; i < geometry.GetNTopLevelObjects(); i++) { - Solid * locsol; + // Solid * locsol; if (geometry.GetTopLevelObject(i)->GetLayer() != layer) continue; @@ -978,7 +978,8 @@ namespace netgen const Solid * sol = geometry.GetTopLevelObject(i)->GetSolid(); const Surface * surf = geometry.GetTopLevelObject(i)->GetSurface(); - sol -> TangentialSolid (hp, locsol, locsurfind, size*ideps); + // sol -> TangentialSolid (hp, locsol, locsurfind, size*ideps); + auto locsol = sol -> TangentialSolid (hp, locsurfind, size*ideps); //*testout << "hp = " << hp << endl; //(*testout) << "locsol: " << endl; @@ -995,7 +996,8 @@ namespace netgen ReducePrimitiveIterator rpi(boxp); UnReducePrimitiveIterator urpi; - ((Solid*)locsol) -> IterateSolid (rpi); + // ((Solid*)locsol) -> IterateSolid (rpi); + locsol -> IterateSolid (rpi); locsol -> CalcSurfaceInverse (); @@ -1020,7 +1022,8 @@ namespace netgen } } - ((Solid*)locsol) -> IterateSolid (urpi); + // ((Solid*)locsol) -> IterateSolid (urpi); + locsol -> IterateSolid (urpi); if (debug) @@ -1085,23 +1088,33 @@ namespace netgen //int k; double eps = 1e-8*size; - NgArray pre_ok(2); + ArrayMem pre_ok(2); + bool flip = false; do { eps *= 0.5; - pre_ok[0] = (locsol -> VectorIn2 (hp, m, n, eps) == IS_OUTSIDE && - locsol -> VectorIn2 (hp, m, -1. * n, eps) == IS_INSIDE); - pre_ok[1] = (locsol -> VectorIn2 (hp, -1.*m, n, eps) == IS_OUTSIDE && - locsol -> VectorIn2 (hp, -1.*m, -1. * n, eps) == IS_INSIDE); + auto in00 = locsol -> VectorIn2 (hp, m, n, eps); + auto in01 = locsol -> VectorIn2 (hp, m, -1. * n, eps); + pre_ok[0] = in00 == IS_OUTSIDE && in01 == IS_INSIDE; + + if(in00 == IS_INSIDE && in01 == IS_OUTSIDE) + pre_ok[0] = flip = true; + + auto in10 = locsol -> VectorIn2 (hp, -1.*m, n, eps); + auto in11 = locsol -> VectorIn2 (hp, -1.*m, -1. * n, eps); + pre_ok[1] = (in10 == IS_OUTSIDE && in11 == IS_INSIDE); + + if(in10 == IS_INSIDE && in11 == IS_OUTSIDE) + pre_ok[1] = flip = true; if (debug) { *testout << "eps = " << eps << endl; - *testout << "in,1 = " << locsol -> VectorIn2 (hp, m, n, eps) << endl; - *testout << "in,1 = " << locsol -> VectorIn2 (hp, m, -1. * n, eps) << endl; - *testout << "in,1 = " << locsol -> VectorIn2 (hp, -1.*m, n, eps) << endl; - *testout << "in,1 = " << locsol -> VectorIn2 (hp, -1.*m, -1. * n, eps) << endl; + *testout << "in,1 = " << in00 << endl; + *testout << "in,1 = " << in01 << endl; + *testout << "in,1 = " << in10 << endl; + *testout << "in,1 = " << in11 << endl; } } while(pre_ok[0] && pre_ok[1] && eps > 1e-16*size); @@ -1150,10 +1163,10 @@ namespace netgen m2 = fac * grad; // (*testout) << "hp = " << hp << ", m = " << m << ", m2 = " << m2 << endl; - Solid * locsol2; - locsol -> TangentialSolid3 (hp, m, m2, locsol2, locsurfind2, ideps*size); + // Solid * locsol2; + auto locsol2 = locsol -> TangentialSolid3 (hp, m, m2, locsurfind2, ideps*size); if (!locsol2) ok = 0; - delete locsol2; + // delete locsol2; if (ok) @@ -1197,7 +1210,10 @@ namespace netgen if (!surf) { - if (sameasref) + bool inside = sameasref; + if(flip) + inside = !inside; + if (inside) refedges.Elem(hi).domin = i; else refedges.Elem(hi).domout = i; @@ -1246,7 +1262,7 @@ namespace netgen m *= -1; } } - delete locsol; + // delete locsol; } @@ -1255,6 +1271,9 @@ namespace netgen *testout << "Refsegments, before delete: " << endl << refedges << endl; *testout << "inv: " << endl << refedgesinv << endl; } + + if(refedges.Size() == 0) + throw Exception("No edges found, something wrong."); NgBitArray todelete(refedges.Size()); todelete.Clear(); @@ -1764,7 +1783,7 @@ namespace netgen int nsurf = geometry.GetNSurf(); int layer = 0; - Solid * tansol; + // Solid * tansol; NgArray tansurfind; double size = geometry.MaxSize(); @@ -1822,7 +1841,8 @@ namespace netgen continue; const Solid * sol = geometry.GetTopLevelObject(j)->GetSolid(); - sol -> TangentialSolid (p1, tansol, tansurfind, ideps*size); + // sol -> TangentialSolid (p1, tansol, tansurfind, ideps*size); + auto tansol = sol -> TangentialSolid (p1, tansurfind, ideps*size); layer = geometry.GetTopLevelObject(j)->GetLayer(); @@ -1852,7 +1872,7 @@ namespace netgen // seg.invs1 = surfaces[i] -> Inverse(); // seg.invs2 = ! (surfaces[i] -> Inverse()); } - delete tansol; + // delete tansol; } } diff --git a/libsrc/csg/extrusion.cpp b/libsrc/csg/extrusion.cpp index 4354bc05..82b2e651 100644 --- a/libsrc/csg/extrusion.cpp +++ b/libsrc/csg/extrusion.cpp @@ -41,6 +41,18 @@ namespace netgen loc_z_dir[i] = glob_z_direction; } } + + double cum_angle = 0.; + for(auto i : Range(path->GetSplines())) + { + const auto& sp = path->GetSpline(i); + auto t1 = sp.GetTangent(0.); + t1.Normalize(); + auto t2 = sp.GetTangent(1.); + t2.Normalize(); + cum_angle += acos(t1 * t2); + angles.Append(cum_angle); + } profile->GetCoeff(profile_spline_coeff); latest_point3d = -1.111e30; @@ -415,6 +427,14 @@ namespace netgen } + bool ExtrusionFace :: PointInFace (const Point<3> & p, const double eps) const + { + Point<3> hp = p; + Project(hp); + return Dist2(p,hp) < sqr(eps); + } + + void ExtrusionFace :: LineIntersections ( const Point<3> & p, const Vec<3> & v, const double eps, @@ -648,20 +668,35 @@ namespace netgen dez /= lenz; dez -= (dez * ez) * ez; } - - Extrusion :: Extrusion(const SplineGeometry<3> & path_in, - const SplineGeometry<2> & profile_in, + void ExtrusionFace :: DefineTangentialPlane(const Point<3>& ap1, + const Point<3>& ap2) + { + Surface::DefineTangentialPlane(ap1, ap2); + tangential_plane_seg = latest_seg; + } + + void ExtrusionFace :: ToPlane(const Point<3>& p3d, Point<2>& p2d, + double h, int& zone) const + { + Surface::ToPlane(p3d, p2d, h, zone); + double angle = angles[tangential_plane_seg] - angles[latest_seg]; + if(fabs(angle) > 3.14/2.) + zone = -1; + } + + Extrusion :: Extrusion(shared_ptr> path_in, + shared_ptr> profile_in, const Vec<3> & z_dir) : - path(&path_in), profile(&profile_in), z_direction(z_dir) + path(path_in), profile(profile_in), z_direction(z_dir) { surfaceactive.SetSize(0); surfaceids.SetSize(0); for(int j=0; jGetNSplines(); j++) { - ExtrusionFace * face = new ExtrusionFace(&((*profile).GetSpline(j)), - path, + ExtrusionFace * face = new ExtrusionFace(&(profile->GetSpline(j)), + path.get(), z_direction); faces.Append(face); surfaceactive.Append(true); @@ -737,6 +772,16 @@ namespace netgen return PointInSolid(p,eps,NULL); } + void Extrusion :: GetTangentialSurfaceIndices (const Point<3> & p, + NgArray & surfind, double eps) const + { + for (int j = 0; j < faces.Size(); j++) + if (faces[j] -> PointInFace(p, eps)) + if (!surfind.Contains (GetSurfaceId(j))) + surfind.Append (GetSurfaceId(j)); + } + + INSOLID_TYPE Extrusion :: VecInSolid (const Point<3> & p, const Vec<3> & v, double eps) const @@ -838,7 +883,7 @@ namespace netgen return retval; if(latestfacenum >= 0) - return faces[latestfacenum]->VecInFace(p,v2,0); + return faces[latestfacenum]->VecInFace(p,v2,eps); else return VecInSolid(p,v2,eps); } diff --git a/libsrc/csg/extrusion.hpp b/libsrc/csg/extrusion.hpp index 70a9e4f3..e29d5660 100644 --- a/libsrc/csg/extrusion.hpp +++ b/libsrc/csg/extrusion.hpp @@ -12,8 +12,10 @@ namespace netgen const SplineSeg<2> * profile; const SplineGeometry<3> * path; Vec<3> glob_z_direction; + Array angles; bool deletable; + int tangential_plane_seg; NgArray< const SplineSeg3<3> * > spline3_path; NgArray< const LineSeg<3> * > line_path; @@ -54,7 +56,7 @@ namespace netgen ~ExtrusionFace(); - virtual void DoArchive(Archive& ar) + void DoArchive(Archive& ar) override { Surface::DoArchive(ar); ar & profile & path & glob_z_direction & deletable & spline3_path & line_path & @@ -62,25 +64,25 @@ namespace netgen profile_spline_coeff & latest_seg & latest_t & latest_point2d & latest_point3d; } - virtual int IsIdentic (const Surface & s2, int & inv, double eps) const; + int IsIdentic (const Surface & s2, int & inv, double eps) const override; - virtual double CalcFunctionValue (const Point<3> & point) const; - virtual void CalcGradient (const Point<3> & point, Vec<3> & grad) const; - virtual void CalcHesse (const Point<3> & point, Mat<3> & hesse) const; - virtual double HesseNorm () const; + double CalcFunctionValue (const Point<3> & point) const override; + void CalcGradient (const Point<3> & point, Vec<3> & grad) const override; + void CalcHesse (const Point<3> & point, Mat<3> & hesse) const override; + double HesseNorm () const override; - virtual double MaxCurvature () const; + double MaxCurvature () const override; //virtual double MaxCurvatureLoc (const Point<3> & /* c */ , // double /* rad */) const; - virtual void Project (Point<3> & p) const; + void Project (Point<3> & p) const override; - virtual Point<3> GetSurfacePoint () const; - virtual void Print (ostream & str) const; + Point<3> GetSurfacePoint () const override; + void Print (ostream & str) const override; - virtual void GetTriangleApproximation (TriangleApproximation & tas, + void GetTriangleApproximation (TriangleApproximation & tas, const Box<3> & boundingbox, - double facets) const; + double facets) const override; const SplineGeometry<3> & GetPath(void) const {return *path;} const SplineSeg<2> & GetProfile(void) const {return *profile;} @@ -94,6 +96,9 @@ namespace netgen int & after, bool & intersecting ) const; + + bool PointInFace (const Point<3> & p, const double eps) const; + INSOLID_TYPE VecInFace ( const Point<3> & p, const Vec<3> & v, const double eps ) const; @@ -111,6 +116,11 @@ namespace netgen Vec<3> & ex, Vec<3> & ey, Vec<3> & ez, Vec<3> & dex, Vec<3> & dey, Vec<3> & dez) const; + void DefineTangentialPlane(const Point<3>& ap1, + const Point<3>& ap2) override; + void ToPlane(const Point<3>& p3d, Point<2>& p2d, + double h, int& zone) const override; + }; @@ -118,8 +128,8 @@ namespace netgen class Extrusion : public Primitive { private: - const SplineGeometry<3>* path; - const SplineGeometry<2>* profile; // closed, clockwise oriented curve + shared_ptr> path; + shared_ptr> profile; // closed, clockwise oriented curve Vec<3> z_direction; @@ -128,43 +138,46 @@ namespace netgen mutable int latestfacenum; public: - Extrusion(const SplineGeometry<3> & path_in, - const SplineGeometry<2> & profile_in, + Extrusion(shared_ptr> path_in, + shared_ptr> profile_in, const Vec<3> & z_dir); // default constructor for archive Extrusion() {} ~Extrusion(); - virtual void DoArchive(Archive& ar) + void DoArchive(Archive& ar) override { Primitive::DoArchive(ar); ar & path & profile & z_direction & faces & latestfacenum; } - virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const; - virtual INSOLID_TYPE PointInSolid (const Point<3> & p, - double eps) const; + INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const override; + INSOLID_TYPE PointInSolid (const Point<3> & p, + double eps) const override; INSOLID_TYPE PointInSolid (const Point<3> & p, double eps, NgArray * const facenums) const; - virtual INSOLID_TYPE VecInSolid (const Point<3> & p, - const Vec<3> & v, - double eps) const; + + void GetTangentialSurfaceIndices (const Point<3> & p, + NgArray & surfind, double eps) const override; + + INSOLID_TYPE VecInSolid (const Point<3> & p, + const Vec<3> & v, + double eps) const override; // checks if lim s->0 lim t->0 p + t(v1 + s v2) in solid - virtual INSOLID_TYPE VecInSolid2 (const Point<3> & p, - const Vec<3> & v1, - const Vec<3> & v2, - double eps) const; + INSOLID_TYPE VecInSolid2 (const Point<3> & p, + const Vec<3> & v1, + const Vec<3> & v2, + double eps) const override; - virtual int GetNSurfaces() const; - virtual Surface & GetSurface (int i = 0); - virtual const Surface & GetSurface (int i = 0) const; + int GetNSurfaces() const override; + Surface & GetSurface (int i = 0) override; + const Surface & GetSurface (int i = 0) const override; - virtual void Reduce (const BoxSphere<3> & box); - virtual void UnReduce (); - + void Reduce (const BoxSphere<3> & box) override; + void UnReduce () override; }; } diff --git a/libsrc/csg/identify.cpp b/libsrc/csg/identify.cpp index 935ed22f..e8dbb8fd 100644 --- a/libsrc/csg/identify.cpp +++ b/libsrc/csg/identify.cpp @@ -318,6 +318,10 @@ GetIdentifiedPoint (class Mesh & mesh, int pi) void PeriodicIdentification :: IdentifyPoints (class Mesh & mesh) { + Point3d p1, p2; + mesh.GetBox(p1, p2); + auto eps = 1e-6 * (p2-p1).Length(); + for (int i = 1; i <= mesh.GetNP(); i++) { Point<3> p = mesh.Point(i); @@ -327,7 +331,7 @@ void PeriodicIdentification :: IdentifyPoints (class Mesh & mesh) pp = trafo(pp); s2->Project (pp); for (int j = 1; j <= mesh.GetNP(); j++) - if (Dist2(mesh.Point(j), pp) < 1e-6) + if (Dist2(mesh.Point(j), pp) < eps) { mesh.GetIdentifications().Add (i, j, nr); /* diff --git a/libsrc/csg/polyhedra.cpp b/libsrc/csg/polyhedra.cpp index 5e35241b..7db4f94c 100644 --- a/libsrc/csg/polyhedra.cpp +++ b/libsrc/csg/polyhedra.cpp @@ -6,283 +6,406 @@ namespace netgen { -Polyhedra::Face::Face (int pi1, int pi2, int pi3, - const NgArray > & points, - int ainputnr) -{ - inputnr = ainputnr; + Polyhedra::Face::Face (int pi1, int pi2, int pi3, + const NgArray > & points, + int ainputnr) + { + inputnr = ainputnr; - pnums[0] = pi1; - pnums[1] = pi2; - pnums[2] = pi3; + pnums[0] = pi1; + pnums[1] = pi2; + pnums[2] = pi3; - bbox.Set (points[pi1]); - bbox.Add (points[pi2]); - bbox.Add (points[pi3]); + bbox.Set (points[pi1]); + bbox.Add (points[pi2]); + bbox.Add (points[pi3]); - v1 = points[pi2] - points[pi1]; - v2 = points[pi3] - points[pi1]; + v1 = points[pi2] - points[pi1]; + v2 = points[pi3] - points[pi1]; - n = Cross (v1, v2); + n = Cross (v1, v2); - nn = n; - nn.Normalize(); - // PseudoInverse (v1, v2, w1, w2); + nn = n; + nn.Normalize(); + // PseudoInverse (v1, v2, w1, w2); - Mat<2,3> mat; - Mat<3,2> inv; - for (int i = 0; i < 3; i++) - { - mat(0,i) = v1(i); - mat(1,i) = v2(i); - } - CalcInverse (mat, inv); - for (int i = 0; i < 3; i++) - { - w1(i) = inv(i,0); - w2(i) = inv(i,1); - } -} + Mat<2,3> mat; + Mat<3,2> inv; + for (int i = 0; i < 3; i++) + { + mat(0,i) = v1(i); + mat(1,i) = v2(i); + } + CalcInverse (mat, inv); + for (int i = 0; i < 3; i++) + { + w1(i) = inv(i,0); + w2(i) = inv(i,1); + } + } -Polyhedra :: Polyhedra () -{ - surfaceactive.SetSize(0); - surfaceids.SetSize(0); - eps_base1 = 1e-8; -} + Polyhedra :: Polyhedra () + { + surfaceactive.SetSize(0); + surfaceids.SetSize(0); + eps_base1 = 1e-8; + } -Polyhedra :: ~Polyhedra () -{ - ; -} + Polyhedra :: ~Polyhedra () + { + ; + } -Primitive * Polyhedra :: CreateDefault () -{ - return new Polyhedra(); -} + Primitive * Polyhedra :: CreateDefault () + { + return new Polyhedra(); + } -INSOLID_TYPE Polyhedra :: BoxInSolid (const BoxSphere<3> & box) const -{ - /* - for (i = 1; i <= faces.Size(); i++) - if (FaceBoxIntersection (i, box)) + INSOLID_TYPE Polyhedra :: BoxInSolid (const BoxSphere<3> & box) const + { + /* + for (i = 1; i <= faces.Size(); i++) + if (FaceBoxIntersection (i, box)) return DOES_INTERSECT; - */ - for (int i = 0; i < faces.Size(); i++) - { - if (!faces[i].bbox.Intersect (box)) - continue; - //(*testout) << "face " << i << endl; + */ + for (int i = 0; i < faces.Size(); i++) + { + if (!faces[i].bbox.Intersect (box)) + continue; + //(*testout) << "face " << i << endl; - const Point<3> & p1 = points[faces[i].pnums[0]]; - const Point<3> & p2 = points[faces[i].pnums[1]]; - const Point<3> & p3 = points[faces[i].pnums[2]]; + const Point<3> & p1 = points[faces[i].pnums[0]]; + const Point<3> & p2 = points[faces[i].pnums[1]]; + const Point<3> & p3 = points[faces[i].pnums[2]]; - if (fabs (faces[i].nn * (p1 - box.Center())) > box.Diam()/2) - continue; + if (fabs (faces[i].nn * (p1 - box.Center())) > box.Diam()/2) + continue; - //(*testout) << "still in loop" << endl; + //(*testout) << "still in loop" << endl; - double dist2 = MinDistTP2 (p1, p2, p3, box.Center()); - //(*testout) << "p1 " << p1 << " p2 " << p2 << " p3 " << p3 << endl - // << " box.Center " << box.Center() << " box.Diam() " << box.Diam() << endl - // << " dist2 " << dist2 << " sqr(box.Diam()/2) " << sqr(box.Diam()/2) << endl; - if (dist2 < sqr (box.Diam()/2)) - { - //(*testout) << "DOES_INTERSECT" << endl; - return DOES_INTERSECT; - } - }; + double dist2 = MinDistTP2 (p1, p2, p3, box.Center()); + //(*testout) << "p1 " << p1 << " p2 " << p2 << " p3 " << p3 << endl + // << " box.Center " << box.Center() << " box.Diam() " << box.Diam() << endl + // << " dist2 " << dist2 << " sqr(box.Diam()/2) " << sqr(box.Diam()/2) << endl; + if (dist2 < sqr (box.Diam()/2)) + { + //(*testout) << "DOES_INTERSECT" << endl; + return DOES_INTERSECT; + } + }; - return PointInSolid (box.Center(), 1e-3 * box.Diam()); -} + return PointInSolid (box.Center(), 1e-3 * box.Diam()); + } -INSOLID_TYPE Polyhedra :: PointInSolid (const Point<3> & p, - double eps) const -{ - //(*testout) << "PointInSolid p " << p << " eps " << eps << endl; - //(*testout) << "bbox " << poly_bbox << endl; - - if((p(0) > poly_bbox.PMax()(0) + eps) || (p(0) < poly_bbox.PMin()(0) - eps) || - (p(1) > poly_bbox.PMax()(1) + eps) || (p(1) < poly_bbox.PMin()(1) - eps) || - (p(2) > poly_bbox.PMax()(2) + eps) || (p(2) < poly_bbox.PMin()(2) - eps)) - { - //(*testout) << "returning IS_OUTSIDE" << endl; + // check how many faces a ray starting in p intersects + INSOLID_TYPE Polyhedra :: PointInSolid (const Point<3> & p, + double eps) const + { + if (!poly_bbox.IsIn (p, eps)) return IS_OUTSIDE; - } - Vec<3> n, v1, v2; + // random (?) direction: + Vec<3> n(-0.424621, 0.1543, 0.89212238); - // random (?) numbers: - n(0) = -0.424621; - n(1) = 0.15432; - n(2) = 0.89212238; + int cnt = 0; + for (auto & face : faces) + { + Vec<3> v0 = p - points[face.pnums[0]]; - int cnt = 0; + double lam3 = face.nn * v0; - for (int i = 0; i < faces.Size(); i++) - { - const Point<3> & p1 = points[faces[i].pnums[0]]; - - Vec<3> v0 = p - p1; + if (fabs(lam3) < eps) // point is in plance of face + { + double lam1 = face.w1 * v0; + double lam2 = face.w2 * v0; + if (lam1 >= -eps_base1 && lam2 >= -eps_base1 && lam1+lam2 <= 1+eps_base1) + return DOES_INTERSECT; + } + else + { + double lam3 = -(face.n * v0) / (face.n * n); - double lam3 = faces[i].nn * v0; + if (lam3 < 0) continue; // ray goes not in direction of face - if(fabs(lam3) < eps) - { - double lam1 = (faces[i].w1 * v0); - double lam2 = (faces[i].w2 * v0); - if (lam1 >= -eps_base1 && lam2 >= -eps_base1 && lam1+lam2 <= 1+eps_base1) - { - //(*testout) << "returning DOES_INTERSECT" << endl; - return DOES_INTERSECT; - } - } - else - { - lam3 = -(faces[i].n * v0) / (faces[i].n * n); - - if (lam3 < 0) continue; - - Vec<3> rs = v0 + lam3 * n; + Vec<3> rs = v0 + lam3 * n; - double lam1 = (faces[i].w1 * rs); - double lam2 = (faces[i].w2 * rs); - if (lam1 >= 0 && lam2 >= 0 && lam1+lam2 <= 1) - cnt++; - } + double lam1 = face.w1 * rs; + double lam2 = face.w2 * rs; + if (lam1 >= 0 && lam2 >= 0 && lam1+lam2 <= 1) + cnt++; + } + } - } - - //(*testout) << " cnt = " << cnt%2 << endl; - return (cnt % 2) ? IS_INSIDE : IS_OUTSIDE; -} + return (cnt % 2) ? IS_INSIDE : IS_OUTSIDE; + } -void Polyhedra :: GetTangentialSurfaceIndices (const Point<3> & p, - NgArray & surfind, double eps) const -{ - for (int i = 0; i < faces.Size(); i++) - { - const Point<3> & p1 = points[faces[i].pnums[0]]; + void Polyhedra :: GetTangentialSurfaceIndices (const Point<3> & p, + NgArray & surfind, double eps) const + { + for (int i = 0; i < faces.Size(); i++) + { + auto & face = faces[i]; + const Point<3> & p1 = points[face.pnums[0]]; - Vec<3> v0 = p - p1; - double lam3 = -(faces[i].nn * v0); // n->nn + Vec<3> v0 = p - p1; + double lam3 = -(face.nn * v0); // n->nn - if (fabs (lam3) > eps) continue; + if (fabs (lam3) > eps) continue; - double lam1 = (faces[i].w1 * v0); - double lam2 = (faces[i].w2 * v0); + double lam1 = (face.w1 * v0); + double lam2 = (face.w2 * v0); - if (lam1 >= -eps_base1 && lam2 >= -eps_base1 && lam1+lam2 <= 1+eps_base1) - if (!surfind.Contains (GetSurfaceId(i))) - surfind.Append (GetSurfaceId(i)); - } + if (lam1 >= -eps_base1 && lam2 >= -eps_base1 && lam1+lam2 <= 1+eps_base1) + if (!surfind.Contains (GetSurfaceId(i))) + surfind.Append (GetSurfaceId(i)); + } -} + } + INSOLID_TYPE Polyhedra :: VecInSolidOld (const Point<3> & p, + const Vec<3> & v, + double eps) const + { + NgArray point_on_faces; + INSOLID_TYPE res(DOES_INTERSECT); - -INSOLID_TYPE Polyhedra :: VecInSolid (const Point<3> & p, - const Vec<3> & v, - double eps) const -{ - NgArray point_on_faces; - INSOLID_TYPE res(DOES_INTERSECT); - - Vec<3> vn = v; - vn.Normalize(); - for (int i = 0; i < faces.Size(); i++) - { - const Point<3> & p1 = points[faces[i].pnums[0]]; + Vec<3> vn = v; + vn.Normalize(); + for (int i = 0; i < faces.Size(); i++) + { + const Point<3> & p1 = points[faces[i].pnums[0]]; - Vec<3> v0 = p - p1; - double lam3 = -(faces[i].nn * v0); // n->nn + Vec<3> v0 = p - p1; + double lam3 = -(faces[i].nn * v0); // n->nn - if (fabs (lam3) > eps) continue; - //(*testout) << "lam3 <= eps" << endl; + if (fabs (lam3) > eps) continue; + //(*testout) << "lam3 <= eps" << endl; - double lam1 = (faces[i].w1 * v0); - double lam2 = (faces[i].w2 * v0); + double lam1 = (faces[i].w1 * v0); + double lam2 = (faces[i].w2 * v0); - if (lam1 >= -eps_base1 && lam2 >= -eps_base1 && lam1+lam2 <= 1+eps_base1) - { - point_on_faces.Append(i); + if (lam1 >= -eps_base1 && lam2 >= -eps_base1 && lam1+lam2 <= 1+eps_base1) + { + point_on_faces.Append(i); - double scal = vn * faces[i].nn; // n->nn + double scal = vn * faces[i].nn; // n->nn - res = DOES_INTERSECT; - if (scal > eps_base1) res = IS_OUTSIDE; - if (scal < -eps_base1) res = IS_INSIDE; - } - } + res = DOES_INTERSECT; + if (scal > eps_base1) res = IS_OUTSIDE; + if (scal < -eps_base1) res = IS_INSIDE; + } + } - //(*testout) << "point_on_faces.Size() " << point_on_faces.Size() - // << " res " << res << endl; + //(*testout) << "point_on_faces.Size() " << point_on_faces.Size() + // << " res " << res << endl; + + if (point_on_faces.Size() == 0) + return PointInSolid (p, 0); + if (point_on_faces.Size() == 1) + return res; + + + + + double mindist(0); + bool first = true; + + for(int i=0; i eps && (first || dist < mindist)) + { + mindist = dist; + first = false; + } + } + } + + Point<3> p2 = p + (1e-4*mindist) * vn; + res = PointInSolid (p2, eps); + + // (*testout) << "mindist " << mindist << " res " << res << endl; - if (point_on_faces.Size() == 0) - return PointInSolid (p, 0); - if (point_on_faces.Size() == 1) return res; + } + + + + // check how many faces a ray starting in p+alpha*v intersects + INSOLID_TYPE Polyhedra :: VecInSolidNew (const Point<3> & p, + const Vec<3> & v, + double eps, bool printing) const + { + if (!poly_bbox.IsIn (p, eps)) + return IS_OUTSIDE; + + // random (?) direction: + Vec<3> n(-0.424621, 0.1543, 0.89212238); + + int cnt = 0; + for (auto & face : faces) + { + Vec<3> v0 = p - points[face.pnums[0]]; + if (printing) + { + *testout << "face: "; + for (int j = 0; j < 3; j++) + *testout << points[face.pnums[j]] << " "; + *testout << endl; + } + double lamn = face.nn * v0; + + if (fabs(lamn) < eps) // point is in plane of face + { + double lam1 = face.w1 * v0; + double lam2 = face.w2 * v0; + double lam3 = 1-lam1-lam2; + if (printing) + *testout << "lam = " << lam1 << " " << lam2 << " " << lam3 << endl; + if (lam1 >= -eps_base1 && lam2 >= -eps_base1 && lam3 >= -eps_base1) + { // point is close to trianlge, perturbe by alpha*v + double dlamn = face.nn*v; + + if (fabs(dlamn) < 1e-8) // vec also in plane + { + if (printing) + *testout << "tang in plane" << endl; + double dlam1 = face.w1 * v; + double dlam2 = face.w2 * v; + double dlam3 = -dlam1-dlam2; + if (printing) + *testout << "dlam = " << dlam1 << " " << dlam2 << " " << dlam3 << endl; + bool in1 = lam1 > eps_base1 || dlam1 > -eps_base1; + bool in2 = lam2 > eps_base1 || dlam2 > -eps_base1; + bool in3 = lam3 > eps_base1 || dlam3 > -eps_base1; + if (in1 && in2 && in3) + return DOES_INTERSECT; + } + else // vec out of plane + { + if (printing) + *testout << "out of plane"; + double dlamn = -(face.n * v) / (face.n * n); + if (printing) + *testout << "dlamn = " << dlamn << endl; + if (dlamn < 0) continue; // ray goes not in direction of face + + Vec<3> drs = v + dlamn * n; + if (printing) + { + *testout << "drs = " << drs << endl; + *testout << "face.w1 = " << face.w1 << endl; + *testout << "face.w2 = " << face.w2 << endl; + } + + double dlam1 = face.w1 * drs; + double dlam2 = face.w2 * drs; + double dlam3 = -dlam1-dlam2; + + if (printing) + *testout << "dlam = " << dlam1 << " " << dlam2 << " " << dlam3 << endl; + + bool in1 = lam1 > eps_base1 || dlam1 > -eps_base1; + bool in2 = lam2 > eps_base1 || dlam2 > -eps_base1; + bool in3 = lam3 > eps_base1 || dlam3 > -eps_base1; + + if (in1 && in2 && in3) + { + if (printing) + *testout << "hit" << endl; + cnt++; + } + } + } + } + else + { + double lamn = -(face.n * v0) / (face.n * n); + + if (lamn < 0) continue; // ray goes not in direction of face + + Vec<3> rs = v0 + lamn * n; + + double lam1 = face.w1 * rs; + double lam2 = face.w2 * rs; + double lam3 = 1-lam1-lam2; + if (lam1 >= 0 && lam2 >= 0 && lam3 >= 0) + { + if (printing) + *testout << "hit" << endl; + cnt++; + } + } + } + + return (cnt % 2) ? IS_INSIDE : IS_OUTSIDE; + } + + + INSOLID_TYPE Polyhedra :: VecInSolid (const Point<3> & p, + const Vec<3> & v, + double eps) const + { + return VecInSolidNew (p, v, eps); + /* + auto oldval = VecInSolidOld (p, v, eps); + auto newval = VecInSolidNew (p, v, eps); + if (oldval != newval) + { + *testout << "different decision: oldval = " << oldval + << " newval = " << newval << endl; + *testout << "p = " << p << ", v = " << v << endl; + VecInSolidNew (p, v, eps, true); + *testout << "Poly:" << endl; + for (auto & face : faces) + { + for (int j = 0; j < 3; j++) + *testout << points[face.pnums[j]] << " "; + *testout << endl; + } + } + return newval; + */ + } + + + + - double mindist(0); - bool first = true; + /* + INSOLID_TYPE Polyhedra :: VecInSolid2 (const Point<3> & p, + const Vec<3> & v1, + const Vec<3> & v2, + double eps) const + { + INSOLID_TYPE res; - for(int i=0; i eps && (first || dist < mindist)) - { - mindist = dist; - first = false; - } - } - } - - Point<3> p2 = p + (1e-2*mindist) * vn; - res = PointInSolid (p2, eps); + res = VecInSolid(p,v1,eps); + if(res != DOES_INTERSECT) + return res; - // (*testout) << "mindist " << mindist << " res " << res << endl; + int point_on_n_faces = 0; - return res; - - -} + Vec<3> v1n = v1; + v1n.Normalize(); + Vec<3> v2n = v2; + v2n.Normalize(); -/* -INSOLID_TYPE Polyhedra :: VecInSolid2 (const Point<3> & p, - const Vec<3> & v1, - const Vec<3> & v2, - double eps) const -{ - INSOLID_TYPE res; - - res = VecInSolid(p,v1,eps); - if(res != DOES_INTERSECT) - return res; - - int point_on_n_faces = 0; - - Vec<3> v1n = v1; - v1n.Normalize(); - Vec<3> v2n = v2; - v2n.Normalize(); - - - for (int i = 0; i < faces.Size(); i++) - { + for (int i = 0; i < faces.Size(); i++) + { const Point<3> & p1 = points[faces[i].pnums[0]]; Vec<3> v0 = p - p1; @@ -294,146 +417,303 @@ INSOLID_TYPE Polyhedra :: VecInSolid2 (const Point<3> & p, double lam2 = (faces[i].w2 * v0); if (lam1 >= -eps && lam2 >= -eps && lam1+lam2 <= 1+eps) - { - double scal1 = v1n * faces[i].n; - if (fabs (scal1) > eps) continue; + { + double scal1 = v1n * faces[i].n; + if (fabs (scal1) > eps) continue; - point_on_n_faces++; + point_on_n_faces++; - double scal2 = v2n * faces[i].n; - res = DOES_INTERSECT; - if (scal2 > eps) res = IS_OUTSIDE; - if (scal2 < -eps) res = IS_INSIDE; - } - } + double scal2 = v2n * faces[i].n; + res = DOES_INTERSECT; + if (scal2 > eps) res = IS_OUTSIDE; + if (scal2 < -eps) res = IS_INSIDE; + } + } - if (point_on_n_faces == 1) - return res; + if (point_on_n_faces == 1) + return res; - cerr << "primitive::vecinsolid2 makes nonsense for polyhedra" << endl; + cerr << "primitive::vecinsolid2 makes nonsense for polyhedra" << endl; - return Primitive :: VecInSolid2 (p, v1, v2, eps); -} -*/ + return Primitive :: VecInSolid2 (p, v1, v2, eps); + } + */ + // #define OLDVECINSOLID2 +#ifdef OLDVECINSOLID2 + INSOLID_TYPE Polyhedra :: VecInSolid2 (const Point<3> & p, + const Vec<3> & v1, + const Vec<3> & v2, + double eps) const + { + //(*testout) << "VecInSolid2 eps " << eps << endl; + INSOLID_TYPE res = VecInSolid(p,v1,eps); + //(*testout) << "VecInSolid = " < & p, - const Vec<3> & v1, - const Vec<3> & v2, - double eps) const -{ - //(*testout) << "VecInSolid2 eps " << eps << endl; - INSOLID_TYPE res = VecInSolid(p,v1,eps); - //(*testout) << "VecInSolid = " < v1n = v1; + v1n.Normalize(); + Vec<3> v2n = v2 - (v2 * v1n) * v1n; + v2n.Normalize(); - Vec<3> v1n = v1; - v1n.Normalize(); - Vec<3> v2n = v2 - (v2 * v1n) * v1n; - v2n.Normalize(); - - double cosv2, cosv2max = -99; + double cosv2, cosv2max = -99; - for (int i = 0; i < faces.Size(); i++) - { - const Point<3> & p1 = points[faces[i].pnums[0]]; + for (int i = 0; i < faces.Size(); i++) + { + const Point<3> & p1 = points[faces[i].pnums[0]]; - Vec<3> v0 = p - p1; - if (fabs (faces[i].nn * v0) > eps) continue; // n->nn - if (fabs (v1n * faces[i].nn) > eps_base1) continue; // n->nn + Vec<3> v0 = p - p1; + if (fabs (faces[i].nn * v0) > eps) continue; // n->nn + if (fabs (v1n * faces[i].nn) > eps_base1) continue; // n->nn - double lam1 = (faces[i].w1 * v0); - double lam2 = (faces[i].w2 * v0); + double lam1 = (faces[i].w1 * v0); + double lam2 = (faces[i].w2 * v0); - if (lam1 >= -eps_base1 && lam2 >= -eps_base1 && lam1+lam2 <= 1+eps_base1) - { - // v1 is in face + if (lam1 >= -eps_base1 && lam2 >= -eps_base1 && lam1+lam2 <= 1+eps_base1) + { + // v1 is in face - Point<3> fc = Center (points[faces[i].pnums[0]], - points[faces[i].pnums[1]], - points[faces[i].pnums[2]]); + Point<3> fc = Center (points[faces[i].pnums[0]], + points[faces[i].pnums[1]], + points[faces[i].pnums[2]]); - Vec<3> vpfc = fc - p; - cosv2 = (v2n * vpfc) / vpfc.Length(); - if (cosv2 > cosv2max) - { - cosv2max = cosv2; - point_on_n_faces++; + Vec<3> vpfc = fc - p; + cosv2 = (v2n * vpfc) / vpfc.Length(); + if (cosv2 > cosv2max) + { + cosv2max = cosv2; + point_on_n_faces++; - double scal2 = v2n * faces[i].nn; // n->nn - res = DOES_INTERSECT; - if (scal2 > eps_base1) res = IS_OUTSIDE; - if (scal2 < -eps_base1) res = IS_INSIDE; + double scal2 = v2n * faces[i].nn; // n->nn + res = DOES_INTERSECT; + if (scal2 > eps_base1) res = IS_OUTSIDE; + if (scal2 < -eps_base1) res = IS_INSIDE; - } - } + } + } + } + + if (point_on_n_faces >= 1) + return res; + + (*testout) << "primitive::vecinsolid2 makes nonsense for polyhedra" << endl; + cerr << "primitive::vecinsolid2 makes nonsense for polyhedra" << endl; + + return Primitive :: VecInSolid2 (p, v1, v2, eps); } - if (point_on_n_faces >= 1) + + +#else + + + // check how many faces a ray starting in p+alpha*v+alpha^2/2 v2 intersects: + // if p + alpha v is in plane, use v2 + INSOLID_TYPE Polyhedra :: VecInSolid2 (const Point<3> & p, + const Vec<3> & v, + const Vec<3> & v2, + double eps) const + { + if (!poly_bbox.IsIn (p, eps)) + return IS_OUTSIDE; + + // random (?) direction: + Vec<3> n(-0.424621, 0.1543, 0.89212238); + + int cnt = 0; + for (auto & face : faces) + { + Vec<3> v0 = p - points[face.pnums[0]]; + double lamn = face.nn * v0; + + if (fabs(lamn) < eps) // point is in plane of face + { + double lam1 = face.w1 * v0; + double lam2 = face.w2 * v0; + double lam3 = 1-lam1-lam2; + + if (lam1 >= -eps_base1 && lam2 >= -eps_base1 && lam3 >= -eps_base1) + { // point is close to trianlge, perturbe by alpha*v + double dlamn = face.nn*v; + + if (fabs(dlamn) < 1e-8) // vec also in plane + { + double dlam1 = face.w1 * v; + double dlam2 = face.w2 * v; + double dlam3 = -dlam1-dlam2; + + bool in1 = lam1 > eps_base1 || dlam1 > -eps_base1; + bool in2 = lam2 > eps_base1 || dlam2 > -eps_base1; + bool in3 = lam3 > eps_base1 || dlam3 > -eps_base1; + + // and the same thing for v2 + if (in1 && in2 && in3) + { + double ddlamn = face.nn*v2; + + if (fabs(ddlamn) < 1e-8) // vec2 also in plane + { + double ddlam1 = face.w1 * v2; + double ddlam2 = face.w2 * v2; + double ddlam3 = -ddlam1-ddlam2; + + bool ddin1 = lam1 > eps_base1 || dlam1 > eps_base1 || ddlam1 > -eps_base1; + bool ddin2 = lam2 > eps_base1 || dlam2 > eps_base1 || ddlam2 > -eps_base1; + bool ddin3 = lam3 > eps_base1 || dlam3 > eps_base1 || ddlam3 > -eps_base1; + if (ddin1 && ddin2 && ddin3) + return DOES_INTERSECT; + } + else // vec2 out of plane + { + double ddlamn = -(face.n * v2) / (face.n * n); + if (ddlamn < 0) continue; // ray goes not in direction of face + + Vec<3> drs = v; // + dlamn * n; but dlamn==0 + Vec<3> ddrs = v2 + ddlamn * n; + + double dlam1 = face.w1 * drs; + double dlam2 = face.w2 * drs; + double dlam3 = -dlam1-dlam2; + + double ddlam1 = face.w1 * ddrs; + double ddlam2 = face.w2 * ddrs; + double ddlam3 = -ddlam1-ddlam2; + + bool ddin1 = lam1 > eps_base1 || dlam1 > eps_base1 || ddlam1 > -eps_base1; + bool ddin2 = lam2 > eps_base1 || dlam2 > eps_base1 || ddlam2 > -eps_base1; + bool ddin3 = lam3 > eps_base1 || dlam3 > eps_base1 || ddlam3 > -eps_base1; + + if (ddin1 && ddin2 && ddin3) + cnt++; + } + } + } + else // vec out of plane + { + double dlamn = -(face.n * v) / (face.n * n); + if (dlamn < 0) continue; // ray goes not in direction of face + + Vec<3> drs = v + dlamn * n; + + double dlam1 = face.w1 * drs; + double dlam2 = face.w2 * drs; + double dlam3 = -dlam1-dlam2; + + bool in1 = lam1 > eps_base1 || dlam1 > -eps_base1; + bool in2 = lam2 > eps_base1 || dlam2 > -eps_base1; + bool in3 = lam3 > eps_base1 || dlam3 > -eps_base1; + + if (in1 && in2 && in3) + cnt++; + + } + } + } + else + { + double lamn = -(face.n * v0) / (face.n * n); + + if (lamn < 0) continue; // ray goes not in direction of face + + Vec<3> rs = v0 + lamn * n; + + double lam1 = face.w1 * rs; + double lam2 = face.w2 * rs; + double lam3 = 1-lam1-lam2; + if (lam1 >= 0 && lam2 >= 0 && lam3 >= 0) + cnt++; + } + } + + return (cnt % 2) ? IS_INSIDE : IS_OUTSIDE; + } +#endif + + + + + + + + INSOLID_TYPE Polyhedra :: VecInSolid3 (const Point<3> & p, + const Vec<3> & v1, + const Vec<3> & v2, + double eps) const + { + return VecInSolid2 (p, v1, v2, eps); + } + + INSOLID_TYPE Polyhedra :: VecInSolid4 (const Point<3> & p, + const Vec<3> & v, + const Vec<3> & v2, + const Vec<3> & m, + double eps) const + { + auto res = VecInSolid2 (p, v, v2, eps); + + if (res == DOES_INTERSECT) // following edge second order, let m decide + return VecInSolid2 (p, v, m, eps); + return res; - - (*testout) << "primitive::vecinsolid2 makes nonsense for polyhedra" << endl; - cerr << "primitive::vecinsolid2 makes nonsense for polyhedra" << endl; - - return Primitive :: VecInSolid2 (p, v1, v2, eps); -} + } + -void Polyhedra :: GetTangentialVecSurfaceIndices2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2, - NgArray & surfind, double eps) const -{ - Vec<3> v1n = v1; - v1n.Normalize(); - Vec<3> v2n = v2; // - (v2 * v1n) * v1n; - v2n.Normalize(); + void Polyhedra :: GetTangentialVecSurfaceIndices2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2, + NgArray & surfind, double eps) const + { + Vec<3> v1n = v1; + v1n.Normalize(); + Vec<3> v2n = v2; // - (v2 * v1n) * v1n; + v2n.Normalize(); - for (int i = 0; i < faces.Size(); i++) - { - const Point<3> & p1 = points[faces[i].pnums[0]]; + for (int i = 0; i < faces.Size(); i++) + { + const Point<3> & p1 = points[faces[i].pnums[0]]; - Vec<3> v0 = p - p1; - if (fabs (v0 * faces[i].nn) > eps) continue; // n->nn - if (fabs (v1n * faces[i].nn) > eps_base1) continue; // n->nn - if (fabs (v2n * faces[i].nn) > eps_base1) continue; // n->nn + Vec<3> v0 = p - p1; + if (fabs (v0 * faces[i].nn) > eps) continue; // n->nn + if (fabs (v1n * faces[i].nn) > eps_base1) continue; // n->nn + if (fabs (v2n * faces[i].nn) > eps_base1) continue; // n->nn - double lam01 = (faces[i].w1 * v0); - double lam02 = (faces[i].w2 * v0); - double lam03 = 1-lam01-lam02; - double lam11 = (faces[i].w1 * v1); - double lam12 = (faces[i].w2 * v1); - double lam13 = -lam11-lam12; - double lam21 = (faces[i].w1 * v2); - double lam22 = (faces[i].w2 * v2); - double lam23 = -lam21-lam22; + double lam01 = (faces[i].w1 * v0); + double lam02 = (faces[i].w2 * v0); + double lam03 = 1-lam01-lam02; + double lam11 = (faces[i].w1 * v1); + double lam12 = (faces[i].w2 * v1); + double lam13 = -lam11-lam12; + double lam21 = (faces[i].w1 * v2); + double lam22 = (faces[i].w2 * v2); + double lam23 = -lam21-lam22; - bool ok1 = lam01 > eps_base1 || - (lam01 > -eps_base1 && lam11 > eps_base1) || - (lam01 > -eps_base1 && lam11 > -eps_base1 && lam21 > eps_base1); + bool ok1 = lam01 > eps_base1 || + (lam01 > -eps_base1 && lam11 > eps_base1) || + (lam01 > -eps_base1 && lam11 > -eps_base1 && lam21 > eps_base1); - bool ok2 = lam02 > eps_base1 || - (lam02 > -eps_base1 && lam12 > eps_base1) || - (lam02 > -eps_base1 && lam12 > -eps_base1 && lam22 > eps_base1); + bool ok2 = lam02 > eps_base1 || + (lam02 > -eps_base1 && lam12 > eps_base1) || + (lam02 > -eps_base1 && lam12 > -eps_base1 && lam22 > eps_base1); - bool ok3 = lam03 > eps_base1 || - (lam03 > -eps_base1 && lam13 > eps_base1) || - (lam03 > -eps_base1 && lam13 > -eps_base1 && lam23 > eps_base1); + bool ok3 = lam03 > eps_base1 || + (lam03 > -eps_base1 && lam13 > eps_base1) || + (lam03 > -eps_base1 && lam13 > -eps_base1 && lam23 > eps_base1); - if (ok1 && ok2 && ok3) - { - if (!surfind.Contains (GetSurfaceId(faces[i].planenr))) - surfind.Append (GetSurfaceId(faces[i].planenr)); - } - } -} + if (ok1 && ok2 && ok3) + { + if (!surfind.Contains (GetSurfaceId(faces[i].planenr))) + surfind.Append (GetSurfaceId(faces[i].planenr)); + } + } + } @@ -446,101 +726,101 @@ void Polyhedra :: GetTangentialVecSurfaceIndices2 (const Point<3> & p, const Vec -void Polyhedra :: GetPrimitiveData (const char *& classname, - NgArray & coeffs) const -{ - classname = "Polyhedra"; - coeffs.SetSize(0); - coeffs.Append (points.Size()); - coeffs.Append (faces.Size()); - coeffs.Append (planes.Size()); + void Polyhedra :: GetPrimitiveData (const char *& classname, + NgArray & coeffs) const + { + classname = "Polyhedra"; + coeffs.SetSize(0); + coeffs.Append (points.Size()); + coeffs.Append (faces.Size()); + coeffs.Append (planes.Size()); - /* - int i, j; - for (i = 1; i <= planes.Size(); i++) - { + /* + int i, j; + for (i = 1; i <= planes.Size(); i++) + { planes.Elem(i)->Print (*testout); - } - for (i = 1; i <= faces.Size(); i++) - { + } + for (i = 1; i <= faces.Size(); i++) + { (*testout) << "face " << i << " has plane " << faces.Get(i).planenr << endl; for (j = 1; j <= 3; j++) - (*testout) << points.Get(faces.Get(i).pnums[j-1]); + (*testout) << points.Get(faces.Get(i).pnums[j-1]); (*testout) << endl; - } - */ -} + } + */ + } -void Polyhedra :: SetPrimitiveData (NgArray & /* coeffs */) -{ - ; -} + void Polyhedra :: SetPrimitiveData (NgArray & /* coeffs */) + { + ; + } -void Polyhedra :: Reduce (const BoxSphere<3> & box) -{ - for (int i = 0; i < planes.Size(); i++) - surfaceactive[i] = 0; + void Polyhedra :: Reduce (const BoxSphere<3> & box) + { + for (int i = 0; i < planes.Size(); i++) + surfaceactive[i] = 0; - for (int i = 0; i < faces.Size(); i++) - if (FaceBoxIntersection (i, box)) - surfaceactive[faces[i].planenr] = 1; -} + for (int i = 0; i < faces.Size(); i++) + if (FaceBoxIntersection (i, box)) + surfaceactive[faces[i].planenr] = 1; + } -void Polyhedra :: UnReduce () -{ - for (int i = 0; i < planes.Size(); i++) - surfaceactive[i] = 1; -} + void Polyhedra :: UnReduce () + { + for (int i = 0; i < planes.Size(); i++) + surfaceactive[i] = 1; + } -int Polyhedra :: AddPoint (const Point<3> & p) -{ - if(points.Size() == 0) - poly_bbox.Set(p); - else - poly_bbox.Add(p); + int Polyhedra :: AddPoint (const Point<3> & p) + { + if(points.Size() == 0) + poly_bbox.Set(p); + else + poly_bbox.Add(p); - points.Append (p); - return points.Size(); -} + points.Append (p); + return points.Size(); + } -int Polyhedra :: AddFace (int pi1, int pi2, int pi3, int inputnum) -{ - (*testout) << "polyhedra, add face " << pi1 << ", " << pi2 << ", " << pi3 << endl; + int Polyhedra :: AddFace (int pi1, int pi2, int pi3, int inputnum) + { + (*testout) << "polyhedra, add face " << pi1 << ", " << pi2 << ", " << pi3 << endl; - if(pi1 == pi2 || pi2 == pi3 || pi3 == pi1) - { - ostringstream msg; - msg << "Illegal point numbers for polyhedron face: " << pi1+1 << ", " << pi2+1 << ", " << pi3+1; - throw NgException(msg.str()); - } + if(pi1 == pi2 || pi2 == pi3 || pi3 == pi1) + { + ostringstream msg; + msg << "Illegal point numbers for polyhedron face: " << pi1+1 << ", " << pi2+1 << ", " << pi3+1; + throw NgException(msg.str()); + } - faces.Append (Face (pi1, pi2, pi3, points, inputnum)); + faces.Append (Face (pi1, pi2, pi3, points, inputnum)); - Point<3> p1 = points[pi1]; - Point<3> p2 = points[pi2]; - Point<3> p3 = points[pi3]; + Point<3> p1 = points[pi1]; + Point<3> p2 = points[pi2]; + Point<3> p3 = points[pi3]; - Vec<3> v1 = p2 - p1; - Vec<3> v2 = p3 - p1; + Vec<3> v1 = p2 - p1; + Vec<3> v2 = p3 - p1; - Vec<3> n = Cross (v1, v2); - n.Normalize(); + Vec<3> n = Cross (v1, v2); + n.Normalize(); - Plane pl (p1, n); -// int inverse; -// int identicto = -1; -// for (int i = 0; i < planes.Size(); i++) -// if (pl.IsIdentic (*planes[i], inverse, 1e-9*max3(v1.Length(),v2.Length(),Dist(p2,p3)))) -// { -// if (!inverse) -// identicto = i; -// } -// // cout << "is identic = " << identicto << endl; -// identicto = -1; // changed April 10, JS + Plane pl (p1, n); + // int inverse; + // int identicto = -1; + // for (int i = 0; i < planes.Size(); i++) + // if (pl.IsIdentic (*planes[i], inverse, 1e-9*max3(v1.Length(),v2.Length(),Dist(p2,p3)))) + // { + // if (!inverse) + // identicto = i; + // } + // // cout << "is identic = " << identicto << endl; + // identicto = -1; // changed April 10, JS -// if (identicto != -1) -// faces.Last().planenr = identicto; -// else + // if (identicto != -1) + // faces.Last().planenr = identicto; + // else { planes.Append (new Plane (p1, n)); surfaceactive.Append (1); @@ -548,190 +828,190 @@ int Polyhedra :: AddFace (int pi1, int pi2, int pi3, int inputnum) faces.Last().planenr = planes.Size()-1; } -// (*testout) << "is plane nr " << faces.Last().planenr << endl; + // (*testout) << "is plane nr " << faces.Last().planenr << endl; - return faces.Size(); -} + return faces.Size(); + } -int Polyhedra :: FaceBoxIntersection (int fnr, const BoxSphere<3> & box) const -{ - /* - (*testout) << "check face box intersection, fnr = " << fnr << endl; - (*testout) << "box = " << box << endl; - (*testout) << "face-box = " << faces[fnr].bbox << endl; - */ + int Polyhedra :: FaceBoxIntersection (int fnr, const BoxSphere<3> & box) const + { + /* + (*testout) << "check face box intersection, fnr = " << fnr << endl; + (*testout) << "box = " << box << endl; + (*testout) << "face-box = " << faces[fnr].bbox << endl; + */ - if (!faces[fnr].bbox.Intersect (box)) - return 0; + if (!faces[fnr].bbox.Intersect (box)) + return 0; - const Point<3> & p1 = points[faces[fnr].pnums[0]]; - const Point<3> & p2 = points[faces[fnr].pnums[1]]; - const Point<3> & p3 = points[faces[fnr].pnums[2]]; + const Point<3> & p1 = points[faces[fnr].pnums[0]]; + const Point<3> & p2 = points[faces[fnr].pnums[1]]; + const Point<3> & p3 = points[faces[fnr].pnums[2]]; - double dist2 = MinDistTP2 (p1, p2, p3, box.Center()); - /* - (*testout) << "p1 = " << p1 << endl; - (*testout) << "p2 = " << p2 << endl; - (*testout) << "p3 = " << p3 << endl; + double dist2 = MinDistTP2 (p1, p2, p3, box.Center()); + /* + (*testout) << "p1 = " << p1 << endl; + (*testout) << "p2 = " << p2 << endl; + (*testout) << "p3 = " << p3 << endl; - (*testout) << "box.Center() = " << box.Center() << endl; - (*testout) << "center = " << box.Center() << endl; - (*testout) << "dist2 = " << dist2 << endl; - (*testout) << "diam = " << box.Diam() << endl; - */ - if (dist2 < sqr (box.Diam()/2)) - { - // (*testout) << "intersect" << endl; - return 1; - } - return 0; -} - - -void Polyhedra :: GetPolySurfs(NgArray < NgArray * > & polysurfs) -{ - int maxnum = -1; - - for(int i = 0; i maxnum) - maxnum = faces[i].inputnr; - } - - polysurfs.SetSize(maxnum+1); - for(int i=0; i; - - for(int i = 0; iAppend(faces[i].planenr); -} - - -void Polyhedra::CalcSpecialPoints (NgArray > & pts) const -{ - for (int i = 0; i < points.Size(); i++) - pts.Append (points[i]); -} - - -void Polyhedra :: AnalyzeSpecialPoint (const Point<3> & /* pt */, - NgArray > & /* specpts */) const -{ - ; -} - -Vec<3> Polyhedra :: SpecialPointTangentialVector (const Point<3> & p, int s1, int s2) const -{ - const double eps = 1e-10*poly_bbox.Diam(); - - for (int fi1 = 0; fi1 < faces.Size(); fi1++) - for (int fi2 = 0; fi2 < faces.Size(); fi2++) + (*testout) << "box.Center() = " << box.Center() << endl; + (*testout) << "center = " << box.Center() << endl; + (*testout) << "dist2 = " << dist2 << endl; + (*testout) << "diam = " << box.Diam() << endl; + */ + if (dist2 < sqr (box.Diam()/2)) { - int si1 = faces[fi1].planenr; - int si2 = faces[fi2].planenr; + // (*testout) << "intersect" << endl; + return 1; + } + return 0; + } - if (surfaceids[si1] != s1 || surfaceids[si2] != s2) continue; - //(*testout) << "check pair fi1/fi2 " << fi1 << "/" << fi2 << endl; + void Polyhedra :: GetPolySurfs(NgArray < NgArray * > & polysurfs) + { + int maxnum = -1; + + for(int i = 0; i maxnum) + maxnum = faces[i].inputnr; + } + + polysurfs.SetSize(maxnum+1); + for(int i=0; i; + + for(int i = 0; iAppend(faces[i].planenr); + } + + + void Polyhedra::CalcSpecialPoints (NgArray > & pts) const + { + for (int i = 0; i < points.Size(); i++) + pts.Append (points[i]); + } + + + void Polyhedra :: AnalyzeSpecialPoint (const Point<3> & /* pt */, + NgArray > & /* specpts */) const + { + ; + } + + Vec<3> Polyhedra :: SpecialPointTangentialVector (const Point<3> & p, int s1, int s2) const + { + const double eps = 1e-10*poly_bbox.Diam(); + + for (int fi1 = 0; fi1 < faces.Size(); fi1++) + for (int fi2 = 0; fi2 < faces.Size(); fi2++) + { + int si1 = faces[fi1].planenr; + int si2 = faces[fi2].planenr; + + if (surfaceids[si1] != s1 || surfaceids[si2] != s2) continue; + + //(*testout) << "check pair fi1/fi2 " << fi1 << "/" << fi2 << endl; - Vec<3> n1 = GetSurface(si1) . GetNormalVector (p); - Vec<3> n2 = GetSurface(si2) . GetNormalVector (p); - Vec<3> t = Cross (n1, n2); + Vec<3> n1 = GetSurface(si1) . GetNormalVector (p); + Vec<3> n2 = GetSurface(si2) . GetNormalVector (p); + Vec<3> t = Cross (n1, n2); - //(*testout) << "t = " << t << endl; + //(*testout) << "t = " << t << endl; - /* - int samepts = 0; - for (int j = 0; j < 3; j++) - for (int k = 0; k < 3; k++) + /* + int samepts = 0; + for (int j = 0; j < 3; j++) + for (int k = 0; k < 3; k++) if (Dist(points[faces[fi1].pnums[j]], - points[faces[fi2].pnums[k]]) < eps) - samepts++; - if (samepts < 2) continue; - */ + points[faces[fi2].pnums[k]]) < eps) + samepts++; + if (samepts < 2) continue; + */ - bool shareedge = false; - for(int j = 0; !shareedge && j < 3; j++) - { - Vec<3> v1 = points[faces[fi1].pnums[(j+1)%3]] - points[faces[fi1].pnums[j]]; - double smax = v1.Length(); - v1 *= 1./smax; + bool shareedge = false; + for(int j = 0; !shareedge && j < 3; j++) + { + Vec<3> v1 = points[faces[fi1].pnums[(j+1)%3]] - points[faces[fi1].pnums[j]]; + double smax = v1.Length(); + v1 *= 1./smax; - int pospos; - if(fabs(v1(0)) > 0.5) - pospos = 0; - else if(fabs(v1(1)) > 0.5) - pospos = 1; - else - pospos = 2; + int pospos; + if(fabs(v1(0)) > 0.5) + pospos = 0; + else if(fabs(v1(1)) > 0.5) + pospos = 1; + else + pospos = 2; - double sp = (p(pospos) - points[faces[fi1].pnums[j]](pospos)) / v1(pospos); - if(sp < -eps || sp > smax+eps) - continue; + double sp = (p(pospos) - points[faces[fi1].pnums[j]](pospos)) / v1(pospos); + if(sp < -eps || sp > smax+eps) + continue; - for (int k = 0; !shareedge && k < 3; k ++) - { - Vec<3> v2 = points[faces[fi2].pnums[(k+1)%3]] - points[faces[fi2].pnums[k]]; - v2.Normalize(); - if(v2 * v1 > 0) - v2 -= v1; - else - v2 += v1; + for (int k = 0; !shareedge && k < 3; k ++) + { + Vec<3> v2 = points[faces[fi2].pnums[(k+1)%3]] - points[faces[fi2].pnums[k]]; + v2.Normalize(); + if(v2 * v1 > 0) + v2 -= v1; + else + v2 += v1; - //(*testout) << "v2.Length2() " << v2.Length2() << endl; + //(*testout) << "v2.Length2() " << v2.Length2() << endl; - if(v2.Length2() > 1e-18) - continue; + if(v2.Length2() > 1e-18) + continue; - double sa,sb; + double sa,sb; - sa = (points[faces[fi2].pnums[k]](pospos) - points[faces[fi1].pnums[j]](pospos)) / v1(pospos); - sb = (points[faces[fi2].pnums[(k+1)%3]](pospos) - points[faces[fi1].pnums[j]](pospos)) / v1(pospos); + sa = (points[faces[fi2].pnums[k]](pospos) - points[faces[fi1].pnums[j]](pospos)) / v1(pospos); + sb = (points[faces[fi2].pnums[(k+1)%3]](pospos) - points[faces[fi1].pnums[j]](pospos)) / v1(pospos); - if(Dist(points[faces[fi1].pnums[j]] + sa*v1, points[faces[fi2].pnums[k]]) > eps) - continue; + if(Dist(points[faces[fi1].pnums[j]] + sa*v1, points[faces[fi2].pnums[k]]) > eps) + continue; - if(sa > sb) - { - double aux = sa; sa = sb; sb = aux; - } + if(sa > sb) + { + double aux = sa; sa = sb; sb = aux; + } - //testout->precision(20); - //(*testout) << "sa " << sa << " sb " << sb << " smax " << smax << " sp " << sp << " v1 " << v1 << endl; - //testout->precision(8); + //testout->precision(20); + //(*testout) << "sa " << sa << " sb " << sb << " smax " << smax << " sp " << sp << " v1 " << v1 << endl; + //testout->precision(8); - shareedge = (sa < -eps && sb > eps) || - (sa < smax-eps && sb > smax+eps) || - (sa > -eps && sb < smax+eps); + shareedge = (sa < -eps && sb > eps) || + (sa < smax-eps && sb > smax+eps) || + (sa > -eps && sb < smax+eps); - if(!shareedge) - continue; + if(!shareedge) + continue; - sa = max2(sa,0.); - sb = min2(sb,smax); + sa = max2(sa,0.); + sb = min2(sb,smax); - if(sp < sa+eps) - shareedge = (t * v1 > 0); - else if (sp > sb-eps) - shareedge = (t * v1 < 0); + if(sp < sa+eps) + shareedge = (t * v1 > 0); + else if (sp > sb-eps) + shareedge = (t * v1 < 0); - } - } - if (!shareedge) continue; + } + } + if (!shareedge) continue; - t.Normalize(); + t.Normalize(); - return t; - } + return t; + } - return Vec<3> (0,0,0); -} + return Vec<3> (0,0,0); + } } diff --git a/libsrc/csg/polyhedra.hpp b/libsrc/csg/polyhedra.hpp index f7a14cd4..4ba183d3 100644 --- a/libsrc/csg/polyhedra.hpp +++ b/libsrc/csg/polyhedra.hpp @@ -48,46 +48,63 @@ namespace netgen public: Polyhedra (); - virtual ~Polyhedra (); + virtual ~Polyhedra () override; static Primitive * CreateDefault (); - virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const; + virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const override; virtual INSOLID_TYPE PointInSolid (const Point<3> & p, - double eps) const; - virtual INSOLID_TYPE VecInSolid (const Point<3> & p, + double eps) const override; + virtual INSOLID_TYPE VecInSolidNew (const Point<3> & p, + const Vec<3> & v, + double eps, bool printing = false) const; + virtual INSOLID_TYPE VecInSolidOld (const Point<3> & p, const Vec<3> & v, double eps) const; + + virtual INSOLID_TYPE VecInSolid (const Point<3> & p, + const Vec<3> & v, + double eps) const override; - // checks if lim s->0 lim t->0 p + t(v1 + s v2) in solid virtual INSOLID_TYPE VecInSolid2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2, - double eps) const; + double eps) const override; + + virtual INSOLID_TYPE VecInSolid3 (const Point<3> & p, + const Vec<3> & v1, + const Vec<3> & v2, + double eps) const override; + virtual INSOLID_TYPE VecInSolid4 (const Point<3> & p, + const Vec<3> & v, + const Vec<3> & v2, + const Vec<3> & m, + double eps) const override; + virtual void GetTangentialSurfaceIndices (const Point<3> & p, - NgArray & surfind, double eps) const; + NgArray & surfind, double eps) const override; virtual void GetTangentialVecSurfaceIndices2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2, - NgArray & surfind, double eps) const; + NgArray & surfind, double eps) const override; - virtual void CalcSpecialPoints (NgArray > & pts) const; + virtual void CalcSpecialPoints (NgArray > & pts) const override; virtual void AnalyzeSpecialPoint (const Point<3> & pt, - NgArray > & specpts) const; - virtual Vec<3> SpecialPointTangentialVector (const Point<3> & p, int s1, int s2) const; + NgArray > & specpts) const override; + virtual Vec<3> SpecialPointTangentialVector (const Point<3> & p, int s1, int s2) const override; - virtual int GetNSurfaces() const + virtual int GetNSurfaces() const override { return planes.Size(); } - virtual Surface & GetSurface (int i) + virtual Surface & GetSurface (int i) override { return *planes[i]; } - virtual const Surface & GetSurface (int i) const + virtual const Surface & GetSurface (int i) const override { return *planes[i]; } - virtual void GetPrimitiveData (const char *& classname, NgArray & coeffs) const; - virtual void SetPrimitiveData (NgArray & coeffs); + virtual void GetPrimitiveData (const char *& classname, NgArray & coeffs) const override; + virtual void SetPrimitiveData (NgArray & coeffs) override; - virtual void Reduce (const BoxSphere<3> & box); - virtual void UnReduce (); + virtual void Reduce (const BoxSphere<3> & box) override; + virtual void UnReduce () override; int AddPoint (const Point<3> & p); int AddFace (int pi1, int pi2, int pi3, int inputnum); diff --git a/libsrc/csg/python_csg.cpp b/libsrc/csg/python_csg.cpp index 09063e51..a650a5ae 100644 --- a/libsrc/csg/python_csg.cpp +++ b/libsrc/csg/python_csg.cpp @@ -170,7 +170,8 @@ namespace netgen DLL_HEADER void ExportCSG(py::module &m) { - py::class_> (m, "SplineCurve2d") + py::class_, shared_ptr>> + (m, "SplineCurve2d") .def(py::init<>()) .def ("AddPoint", FunctionPointer ([] (SplineGeometry<2> & self, double x, double y) @@ -329,14 +330,31 @@ DLL_HEADER void ExportCSG(py::module &m) Solid * sol = new Solid(rev); return make_shared (sol); })); - m.def ("Extrusion", FunctionPointer([](const SplineGeometry<3> & path, - const SplineGeometry<2> & profile, - Vec<3> n) - { - Extrusion * extr = new Extrusion (path,profile,n); - Solid * sol = new Solid(extr); - return make_shared (sol); - })); + m.def ("Extrusion", [](shared_ptr> path, + shared_ptr> profile, + Vec<3> d) + { + Extrusion * extr = new Extrusion (path,profile,d); + Solid * sol = new Solid(extr); + return make_shared (sol); + }, py::arg("path"), py::arg("profile"), py::arg("d"), + R"delimiter(A body of extrusion is defined by its profile +(which has to be a closed, clockwiseoriented 2D curve), + by a path (a 3D curve) and a vector d. It is constructed + as follows: Take a point p on the path and denote the + (unit-)tangent of the path in this point by t. If we cut + the body by the plane given by p and t as normal vector, + the cut is the profile. The profile is oriented by the + (local) y-direction `y:=d−(d·t)t` and the (local) x-direction + `x:=t \times y`. +The following points have to be noticed: + * If the path is not closed, then also the body is NOT closed. + In this case e.g. planes or orthobricks have to be used to + construct a closed body. + * The path has to be smooth, i.e. the tangents at the end- resp. + start-point of two consecutive spline or line patches have to + have the same directions. +)delimiter"); m.def("EllipticCone", [](const Point<3>& a, const Vec<3>& v, const Vec<3>& w, double h, double r) { @@ -354,6 +372,35 @@ When r =1, the truncated elliptic cone becomes an elliptic cylinder. When r tends to zero, the truncated elliptic cone tends to a full elliptic cone. However, when r = 0, the top part becomes a point(tip) and meshing fails! )raw_string"); + + m.def("Polyhedron", [](py::list points, py::list faces) + { + auto poly = new Polyhedra(); + for(auto p : points) + poly->AddPoint(py::cast>(p)); + int fnr = 0; + for(auto face : faces) + { + auto lface = py::cast(face); + if(py::len(lface) == 3) + poly->AddFace(py::cast(lface[0]), + py::cast(lface[1]), + py::cast(lface[2]), + fnr++); + else if(py::len(lface) == 4) + { + poly->AddFace(py::cast(lface[0]), + py::cast(lface[1]), + py::cast(lface[2]), + fnr); + poly->AddFace(py::cast(lface[0]), + py::cast(lface[2]), + py::cast(lface[3]), + fnr++); + } + } + return make_shared(new Solid(poly)); + }); m.def ("Or", FunctionPointer([](shared_ptr s1, shared_ptr s2) { diff --git a/libsrc/csg/revolution.cpp b/libsrc/csg/revolution.cpp index 57498cb4..36f7c614 100644 --- a/libsrc/csg/revolution.cpp +++ b/libsrc/csg/revolution.cpp @@ -670,6 +670,23 @@ namespace netgen surfaceactive.Append(1); surfaceids.Append(0); } + + // checking + if (type == 2) + { + auto t0 = spline_in.GetSpline(0).GetTangent(0); + cout << "tstart (must be vertically): " << t0 << endl; + + auto tn = spline_in.GetSpline(nsplines-1).GetTangent(1); + cout << "tend (must be vertically): " << tn << endl; + + for (int i = 0; i < nsplines-1; i++) + { + auto ta = spline_in.GetSpline(i).GetTangent(1); + auto tb = spline_in.GetSpline(i+1).GetTangent(0); + cout << "sin (must not be 0) = " << abs(ta(0)*tb(1)-ta(1)*tb(0)) / (Abs(ta)*Abs(tb)); + } + } } Revolution::~Revolution() @@ -764,8 +781,9 @@ namespace netgen int intersections_before(0), intersections_after(0); double randomx = 7.42357; double randomy = 1.814756; - randomx *= 1./sqrt(randomx*randomx+randomy*randomy); - randomy *= 1./sqrt(randomx*randomx+randomy*randomy); + double randomlen = sqrt(randomx*randomx+randomy*randomy); + randomx *= 1./randomlen; + randomy *= 1./randomlen; const double a = randomy; @@ -930,6 +948,67 @@ namespace netgen return VecInSolid(p,v1+0.01*v2,eps); } + + void Revolution :: + GetTangentialVecSurfaceIndices2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2, + NgArray & surfind, double eps) const + { + *testout << "tangentialvecsurfind2, p = " << p << endl; + for (int i = 0; i < faces.Size(); i++) + if (faces[i]->PointInFace (p, eps)) + { + *testout << "check face " << i << endl; + Point<2> p2d; + Vec<2> v12d; + faces[i]->CalcProj(p,p2d,v1,v12d); + *testout << "v12d = " << v12d << endl; + auto & spline = faces[i]->GetSpline(); + if (Dist2 (spline.StartPI(), p2d) < sqr(eps)) + { + *testout << "start pi" << endl; + Vec<2> tang = spline.GetTangent(0); + double ip = tang*v12d; + *testout << "ip = " << ip << endl; + if (ip > eps) + surfind.Append(GetSurfaceId(i)); + else if (ip > -eps) + { + Vec<2> v22d; + faces[i]->CalcProj(p,p2d,v2,v22d); + double ip2 = tang*v22d; + *testout << "ip2 = " << ip2 << endl; + if (ip2 > -eps) + surfind.Append(GetSurfaceId(i)); + } + } + else if (Dist2 (faces[i]->GetSpline().EndPI(), p2d) < sqr(eps)) + { + *testout << "end pi" << endl; + + Vec<2> tang = spline.GetTangent(1); + double ip = tang*v12d; + *testout << "ip = " << ip << endl; + if (ip < -eps) + surfind.Append(GetSurfaceId(i)); + else if (ip < eps) + { + Vec<2> v22d; + faces[i]->CalcProj(p,p2d,v2,v22d); + double ip2 = tang*v22d; + *testout << "ip2 = " << ip2 << endl; + if (ip2 < eps) + surfind.Append(GetSurfaceId(i)); + } + } + else + { + *testout << "inner point" << endl; + surfind.Append(GetSurfaceId(i)); + } + } + } + + int Revolution :: GetNSurfaces() const { diff --git a/libsrc/csg/revolution.hpp b/libsrc/csg/revolution.hpp index 0a395e3f..67f9d9c9 100644 --- a/libsrc/csg/revolution.hpp +++ b/libsrc/csg/revolution.hpp @@ -67,7 +67,10 @@ namespace netgen virtual double MaxCurvature () const; //virtual double MaxCurvatureLoc (const Point<3> & /* c */ , // double /* rad */) const; - + + Point<3> P0() const { return p0; } + Vec<3> Axis() const { return v_axis; } + virtual void Project (Point<3> & p) const; virtual Point<3> GetSurfacePoint () const; @@ -155,7 +158,10 @@ namespace netgen const Vec<3> & v2, double eps) const; - + virtual void GetTangentialVecSurfaceIndices2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2, + NgArray & surfind, double eps) const; + + virtual int GetNSurfaces() const; virtual Surface & GetSurface (int i = 0); virtual const Surface & GetSurface (int i = 0) const; diff --git a/libsrc/csg/singularref.cpp b/libsrc/csg/singularref.cpp index a230c518..8dc1b7e5 100644 --- a/libsrc/csg/singularref.cpp +++ b/libsrc/csg/singularref.cpp @@ -168,7 +168,7 @@ void SingularPoint :: FindPoints (class Mesh & mesh) for (int k = 1; k <= 3; k++) { const Solid * solk(NULL); - Solid *tansol; + // Solid *tansol; switch (k) { case 1: solk = sol1; break; @@ -176,7 +176,7 @@ void SingularPoint :: FindPoints (class Mesh & mesh) case 3: solk = sol3; break; } - solk -> TangentialSolid (p, tansol, surfk, 1e-3); + auto tansol = solk -> TangentialSolid (p, surfk, 1e-3); (*testout) << "Tansol = " << *tansol << endl; if (!tansol) continue; @@ -195,7 +195,7 @@ void SingularPoint :: FindPoints (class Mesh & mesh) if (!surf.Contains (surfk[i])) surf.Append (surfk[i]); - delete tansol; + // delete tansol; } if (surf.Size() < 3) continue; diff --git a/libsrc/csg/solid.cpp b/libsrc/csg/solid.cpp index 6ba50cf7..52bd6321 100644 --- a/libsrc/csg/solid.cpp +++ b/libsrc/csg/solid.cpp @@ -6,21 +6,6 @@ namespace netgen { - //using namespace netgen; - - - /* - SolidIterator :: SolidIterator () - { - ; - } - - SolidIterator :: ~SolidIterator () - { - ; - } - */ - // int Solid :: cntnames = 0; @@ -193,9 +178,70 @@ namespace netgen + INSOLID_TYPE Solid :: + PointInSolid (const Point<3> & p, double eps) const + { + switch (op) + { + case TERM: case TERM_REF: + return prim->PointInSolid (p, eps); + case SECTION: + return Intersection (s1->PointInSolid (p, eps), s2->PointInSolid (p, eps)); + case UNION: + return Union (s1->PointInSolid (p, eps), s2->PointInSolid (p, eps)); + case SUB: + return Complement (s1->PointInSolid (p, eps)); + case ROOT: + return s1->PointInSolid (p, eps); + } + } + + + INSOLID_TYPE Solid :: + VecInSolid (const Point<3> & p, const Vec<3> & v, double eps) const + { + switch (op) + { + case TERM: case TERM_REF: + return prim->VecInSolid (p, v, eps); + case SECTION: + return Intersection (s1->VecInSolid (p, v, eps), s2->VecInSolid (p, v, eps)); + case UNION: + return Union (s1->VecInSolid (p, v, eps), s2->VecInSolid (p, v, eps)); + case SUB: + return Complement (s1->VecInSolid (p, v, eps)); + case ROOT: + return s1->VecInSolid (p, v, eps); + } + } + + // checks if lim s->0 lim t->0 p + t(v1 + s v2) in solid + INSOLID_TYPE Solid :: + VecInSolid2 (const Point<3> & p, const Vec<3> & v1, + const Vec<3> & v2, double eps) const + { + switch (op) + { + case TERM: case TERM_REF: + return prim->VecInSolid2 (p, v1, v2, eps); + case SECTION: + return Intersection (s1->VecInSolid2 (p, v1, v2, eps), s2->VecInSolid2 (p, v1, v2, eps)); + case UNION: + return Union (s1->VecInSolid2 (p, v1, v2, eps), s2->VecInSolid2 (p, v1, v2, eps)); + case SUB: + return Complement (s1->VecInSolid2 (p, v1, v2, eps)); + case ROOT: + return s1->VecInSolid2 (p, v1, v2, eps); + } + } + + + bool Solid :: IsIn (const Point<3> & p, double eps) const { + return PointInSolid (p,eps) != IS_OUTSIDE; + /* switch (op) { case TERM: case TERM_REF: @@ -213,10 +259,13 @@ namespace netgen return s1->IsIn (p, eps); } return 0; + */ } bool Solid :: IsStrictIn (const Point<3> & p, double eps) const { + return PointInSolid (p,eps) == IS_INSIDE; + /* switch (op) { case TERM: case TERM_REF: @@ -234,11 +283,14 @@ namespace netgen return s1->IsStrictIn (p, eps); } return 0; + */ } bool Solid :: VectorIn (const Point<3> & p, const Vec<3> & v, double eps) const { + return VecInSolid (p,v,eps) != IS_OUTSIDE; + /* Vec<3> hv; switch (op) { @@ -257,11 +309,14 @@ namespace netgen return s1->VectorIn(p, v, eps); } return 0; + */ } bool Solid :: VectorStrictIn (const Point<3> & p, const Vec<3> & v, double eps) const { + return VecInSolid (p,v,eps) == IS_INSIDE; + /* Vec<3> hv; switch (op) { @@ -282,9 +337,11 @@ namespace netgen return s1->VectorStrictIn(p, v, eps); } return 0; + */ } + /* bool Solid::VectorIn2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2, double eps) const { @@ -317,10 +374,57 @@ namespace netgen } return 0; } + */ + bool Solid::VectorIn2 (const Point<3> & p, const Vec<3> & v1, + const Vec<3> & v2, double eps) const + { + return VecInSolid2 (p,v1,v2,eps) != IS_OUTSIDE; + /* + switch (op) + { + case TERM: case TERM_REF: + { + auto res = prim->VecInSolid2 (p, v1, v2, eps); + return res != IS_OUTSIDE; + } + case SECTION: + return s1->VectorIn2 (p, v1, v2, eps) && s2->VectorIn2 (p, v1, v2, eps); + case UNION: + return s1->VectorIn2 (p, v1, v2, eps) || s2->VectorIn2 (p, v1, v2, eps); + case SUB: + return !s1->VectorStrictIn2 (p, v1, v2, eps); + case ROOT: + return s1->VectorIn2 (p, v1, v2, eps); + } + // return 0; + */ + } - + bool Solid :: VectorStrictIn2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2, + double eps) const + { + return VecInSolid2 (p,v1,v2,eps) == IS_INSIDE; + /* + switch (op) + { + case TERM: case TERM_REF: + { + auto res = prim->VecInSolid2 (p, v1, v2, eps); + return (res == IS_INSIDE); + } + case SECTION: + return s1->VectorStrictIn2 (p, v1, v2, eps) && s2->VectorStrictIn2 (p, v1, v2, eps); + case UNION: + return s1->VectorStrictIn2 (p, v1, v2, eps) || s2->VectorStrictIn2 (p, v1, v2, eps); + case SUB: + return !s1->VectorIn2 (p, v1, v2, eps); + case ROOT: + return s1->VectorStrictIn2 (p, v1, v2, eps); + } + */ + } void Solid :: Print (ostream & str) const @@ -638,18 +742,20 @@ namespace netgen } - void Solid :: TangentialSolid (const Point<3> & p, Solid *& tansol, NgArray & surfids, double eps) const + unique_ptr Solid :: TangentialSolid (const Point<3> & p, NgArray & surfids, double eps) const { - int in, strin; + bool in, strin; + Solid * tansol = nullptr; RecTangentialSolid (p, tansol, surfids, in, strin, eps); surfids.SetSize (0); if (tansol) tansol -> GetTangentialSurfaceIndices (p, surfids, eps); + return unique_ptr (tansol); } void Solid :: RecTangentialSolid (const Point<3> & p, Solid *& tansol, NgArray & surfids, - int & in, int & strin, double eps) const + bool & in, bool & strin, double eps) const { tansol = NULL; @@ -671,7 +777,7 @@ namespace netgen } case SECTION: { - int in1, in2, strin1, strin2; + bool in1, in2, strin1, strin2; Solid * tansol1, * tansol2; s1 -> RecTangentialSolid (p, tansol1, surfids, in1, strin1, eps); @@ -686,13 +792,13 @@ namespace netgen else if (tansol2) tansol = tansol2; } - in = (in1 && in2); - strin = (strin1 && strin2); + in = in1 && in2; + strin = strin1 && strin2; break; } case UNION: { - int in1, in2, strin1, strin2; + bool in1, in2, strin1, strin2; Solid * tansol1 = 0, * tansol2 = 0; s1 -> RecTangentialSolid (p, tansol1, surfids, in1, strin1, eps); @@ -712,13 +818,13 @@ namespace netgen delete tansol1; delete tansol2; } - in = (in1 || in2); - strin = (strin1 || strin2); + in = in1 || in2; + strin = strin1 || strin2; break; } case SUB: { - int hin, hstrin; + bool hin, hstrin; Solid * tansol1; s1 -> RecTangentialSolid (p, tansol1, surfids, hin, hstrin, eps); @@ -740,22 +846,24 @@ namespace netgen - void Solid :: TangentialSolid2 (const Point<3> & p, - const Vec<3> & t, - Solid *& tansol, NgArray & surfids, double eps) const + unique_ptr Solid :: TangentialSolid2 (const Point<3> & p, + const Vec<3> & t, + NgArray & surfids, double eps) const { - int in, strin; + Solid * tansol = nullptr; + bool in, strin; surfids.SetSize (0); RecTangentialSolid2 (p, t, tansol, surfids, in, strin, eps); if (tansol) tansol -> GetTangentialSurfaceIndices2 (p, t, surfids, eps); + return unique_ptr (tansol); } void Solid :: RecTangentialSolid2 (const Point<3> & p, const Vec<3> & t, Solid *& tansol, NgArray & surfids, - int & in, int & strin, double eps) const + bool & in, bool & strin, double eps) const { - tansol = NULL; + tansol = nullptr; switch (op) { @@ -774,8 +882,8 @@ namespace netgen if (ist == DOES_INTERSECT) ist = prim->VecInSolid (p, t, eps); - in = (ist == IS_INSIDE || ist == DOES_INTERSECT); - strin = (ist == IS_INSIDE); + in = (ist == IS_INSIDE) || (ist == DOES_INTERSECT); + strin = ist == IS_INSIDE; if (ist == DOES_INTERSECT) { @@ -786,7 +894,7 @@ namespace netgen } case SECTION: { - int in1, in2, strin1, strin2; + bool in1, in2, strin1, strin2; Solid * tansol1, * tansol2; s1 -> RecTangentialSolid2 (p, t, tansol1, surfids, in1, strin1, eps); @@ -801,13 +909,13 @@ namespace netgen else if (tansol2) tansol = tansol2; } - in = (in1 && in2); - strin = (strin1 && strin2); + in = in1 && in2; + strin = strin1 && strin2; break; } case UNION: { - int in1, in2, strin1, strin2; + bool in1, in2, strin1, strin2; Solid * tansol1, * tansol2; s1 -> RecTangentialSolid2 (p, t, tansol1, surfids, in1, strin1, eps); @@ -822,13 +930,13 @@ namespace netgen else if (tansol2) tansol = tansol2; } - in = (in1 || in2); - strin = (strin1 || strin2); + in = in1 || in2; + strin = strin1 || strin2; break; } case SUB: { - int hin, hstrin; + bool hin, hstrin; Solid * tansol1; s1 -> RecTangentialSolid2 (p, t, tansol1, surfids, hin, hstrin, eps); @@ -854,25 +962,28 @@ namespace netgen - void Solid :: TangentialSolid3 (const Point<3> & p, - const Vec<3> & t, const Vec<3> & t2, - Solid *& tansol, NgArray & surfids, - double eps) const + unique_ptr Solid :: TangentialSolid3 (const Point<3> & p, + const Vec<3> & t, const Vec<3> & t2, + NgArray & surfids, + double eps) const { - int in, strin; + bool in, strin; + Solid * tansol = nullptr; surfids.SetSize (0); RecTangentialSolid3 (p, t, t2, tansol, surfids, in, strin, eps); if (tansol) tansol -> GetTangentialSurfaceIndices3 (p, t, t2, surfids, eps); + + return unique_ptr(tansol); } void Solid :: RecTangentialSolid3 (const Point<3> & p, const Vec<3> & t, const Vec<3> & t2, Solid *& tansol, NgArray & surfids, - int & in, int & strin, double eps) const + bool & in, bool & strin, double eps) const { - tansol = NULL; + tansol = nullptr; switch (op) { @@ -882,8 +993,8 @@ namespace netgen if (ist == DOES_INTERSECT) ist = prim->VecInSolid3 (p, t, t2, eps); - in = (ist == IS_INSIDE || ist == DOES_INTERSECT); - strin = (ist == IS_INSIDE); + in = (ist == IS_INSIDE) || (ist == DOES_INTERSECT); + strin = ist == IS_INSIDE; if (ist == DOES_INTERSECT) { @@ -894,7 +1005,7 @@ namespace netgen } case SECTION: { - int in1, in2, strin1, strin2; + bool in1, in2, strin1, strin2; Solid * tansol1, * tansol2; s1 -> RecTangentialSolid3 (p, t, t2, tansol1, surfids, in1, strin1, eps); @@ -909,13 +1020,13 @@ namespace netgen else if (tansol2) tansol = tansol2; } - in = (in1 && in2); - strin = (strin1 && strin2); + in = in1 && in2; + strin = strin1 && strin2; break; } case UNION: { - int in1, in2, strin1, strin2; + bool in1, in2, strin1, strin2; Solid * tansol1, * tansol2; s1 -> RecTangentialSolid3 (p, t, t2, tansol1, surfids, in1, strin1, eps); @@ -930,13 +1041,13 @@ namespace netgen else if (tansol2) tansol = tansol2; } - in = (in1 || in2); - strin = (strin1 || strin2); + in = in1 || in2; + strin = strin1 || strin2; break; } case SUB: { - int hin, hstrin; + bool hin, hstrin; Solid * tansol1; s1 -> RecTangentialSolid3 (p, t, t2, tansol1, surfids, hin, hstrin, eps); @@ -965,12 +1076,13 @@ namespace netgen - void Solid :: TangentialEdgeSolid (const Point<3> & p, - const Vec<3> & t, const Vec<3> & t2, const Vec<3> & m, - Solid *& tansol, NgArray & surfids, - double eps) const + unique_ptr Solid :: TangentialEdgeSolid (const Point<3> & p, + const Vec<3> & t, const Vec<3> & t2, const Vec<3> & m, + NgArray & surfids, + double eps) const { - int in, strin; + Solid * tansol = nullptr; + bool in, strin; surfids.SetSize (0); // *testout << "tangentialedgesolid,sol = " << (*this) << endl; @@ -978,12 +1090,14 @@ namespace netgen if (tansol) tansol -> RecGetTangentialEdgeSurfaceIndices (p, t, t2, m, surfids, eps); + + return unique_ptr (tansol); } void Solid :: RecTangentialEdgeSolid (const Point<3> & p, const Vec<3> & t, const Vec<3> & t2, const Vec<3> & m, Solid *& tansol, NgArray & surfids, - int & in, int & strin, double eps) const + bool & in, bool & strin, double eps) const { tansol = NULL; @@ -1005,8 +1119,8 @@ namespace netgen // (*testout) << "ist2 = " << ist << endl; - in = (ist == IS_INSIDE || ist == DOES_INTERSECT); - strin = (ist == IS_INSIDE); + in = (ist == IS_INSIDE) || (ist == DOES_INTERSECT); + strin = ist == IS_INSIDE; if (ist == DOES_INTERSECT) { @@ -1017,7 +1131,7 @@ namespace netgen } case SECTION: { - int in1, in2, strin1, strin2; + bool in1, in2, strin1, strin2; Solid * tansol1, * tansol2; s1 -> RecTangentialEdgeSolid (p, t, t2, m, tansol1, surfids, in1, strin1, eps); @@ -1032,13 +1146,13 @@ namespace netgen else if (tansol2) tansol = tansol2; } - in = (in1 && in2); - strin = (strin1 && strin2); + in = in1 && in2; + strin = strin1 && strin2; break; } case UNION: { - int in1, in2, strin1, strin2; + bool in1, in2, strin1, strin2; Solid * tansol1, * tansol2; s1 -> RecTangentialEdgeSolid (p, t, t2, m, tansol1, surfids, in1, strin1, eps); @@ -1053,13 +1167,13 @@ namespace netgen else if (tansol2) tansol = tansol2; } - in = (in1 || in2); - strin = (strin1 || strin2); + in = in1 || in2; + strin = strin1 || strin2; break; } case SUB: { - int hin, hstrin; + bool hin, hstrin; Solid * tansol1; s1 -> RecTangentialEdgeSolid (p, t, t2, m, tansol1, surfids, hin, hstrin, eps); @@ -1095,29 +1209,31 @@ namespace netgen int Solid :: Edge (const Point<3> & p, const Vec<3> & v, double eps) const { - int in, strin, faces; + bool in, strin; + int faces; RecEdge (p, v, in, strin, faces, eps); return faces >= 2; } int Solid :: OnFace (const Point<3> & p, const Vec<3> & v, double eps) const { - int in, strin, faces; + bool in, strin; + int faces; RecEdge (p, v, in, strin, faces, eps); return faces >= 1; } void Solid :: RecEdge (const Point<3> & p, const Vec<3> & v, - int & in, int & strin, int & faces, double eps) const + bool & in, bool & strin, int & faces, double eps) const { switch (op) { case TERM: case TERM_REF: { INSOLID_TYPE ist = prim->VecInSolid (p, v, eps); - in = (ist == IS_INSIDE || ist == DOES_INTERSECT); - strin = (ist == IS_INSIDE); + in = (ist == IS_INSIDE) || (ist == DOES_INTERSECT); + strin = ist == IS_INSIDE; /* in = VectorIn (p, v); strin = VectorStrictIn (p, v); @@ -1143,7 +1259,8 @@ namespace netgen } case SECTION: { - int in1, in2, strin1, strin2, faces1, faces2; + bool in1, in2, strin1, strin2; + int faces1, faces2; s1 -> RecEdge (p, v, in1, strin1, faces1, eps); s2 -> RecEdge (p, v, in2, strin2, faces2, eps); @@ -1157,7 +1274,8 @@ namespace netgen } case UNION: { - int in1, in2, strin1, strin2, faces1, faces2; + bool in1, in2, strin1, strin2; + int faces1, faces2; s1 -> RecEdge (p, v, in1, strin1, faces1, eps); s2 -> RecEdge (p, v, in2, strin2, faces2, eps); @@ -1171,7 +1289,7 @@ namespace netgen } case SUB: { - int in1, strin1; + bool in1, strin1; s1 -> RecEdge (p, v, in1, strin1, faces, eps); in = !strin1; strin = !in1; diff --git a/libsrc/csg/solid.hpp b/libsrc/csg/solid.hpp index 51720275..b3c10807 100644 --- a/libsrc/csg/solid.hpp +++ b/libsrc/csg/solid.hpp @@ -33,6 +33,26 @@ namespace netgen }; + inline INSOLID_TYPE Intersection (INSOLID_TYPE ina, INSOLID_TYPE inb) + { + if (ina == IS_INSIDE && inb == IS_INSIDE) return IS_INSIDE; + if (ina == IS_OUTSIDE || inb == IS_OUTSIDE) return IS_OUTSIDE; + return DOES_INTERSECT; + } + + inline INSOLID_TYPE Union (INSOLID_TYPE ina, INSOLID_TYPE inb) + { + if (ina == IS_INSIDE || inb == IS_INSIDE) return IS_INSIDE; + if (ina == IS_OUTSIDE && inb == IS_OUTSIDE) return IS_OUTSIDE; + return DOES_INTERSECT; + } + + inline INSOLID_TYPE Complement (INSOLID_TYPE in) + { + if (in == IS_INSIDE) return IS_OUTSIDE; + if (in == IS_OUTSIDE) return IS_INSIDE; + return DOES_INTERSECT; + } class Solid { @@ -102,6 +122,14 @@ namespace netgen // geometric tests + INSOLID_TYPE PointInSolid (const Point<3> & p, double eps) const; + INSOLID_TYPE VecInSolid (const Point<3> & p, const Vec<3> & v, double eps) const; + + // checks if lim s->0 lim t->0 p + t(v1 + s v2) in solid + INSOLID_TYPE VecInSolid2 (const Point<3> & p, const Vec<3> & v1, + const Vec<3> & v2, double eps) const; + + bool IsIn (const Point<3> & p, double eps = 1e-6) const; bool IsStrictIn (const Point<3> & p, double eps = 1e-6) const; bool VectorIn (const Point<3> & p, const Vec<3> & v, double eps = 1e-6) const; @@ -109,21 +137,24 @@ namespace netgen bool VectorIn2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2, double eps) const; + /* bool VectorIn2Rec (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2, double eps) const; - + */ + bool VectorStrictIn2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2, + double eps) const; /// compute localization in point p - void TangentialSolid (const Point<3> & p, Solid *& tansol, NgArray & surfids, double eps) const; + unique_ptr TangentialSolid (const Point<3> & p, NgArray & surfids, double eps) const; /// compute localization in point p tangential to vector t - void TangentialSolid2 (const Point<3> & p, const Vec<3> & t, - Solid *& tansol, NgArray & surfids, double eps) const; + unique_ptr TangentialSolid2 (const Point<3> & p, const Vec<3> & t, + NgArray & surfids, double eps) const; /** compute localization in point p, with second order approximation to edge p + s t + s*s/2 t2 **/ - void TangentialSolid3 (const Point<3> & p, const Vec<3> & t, const Vec<3> & t2, - Solid *& tansol, NgArray & surfids, double eps) const; + unique_ptr TangentialSolid3 (const Point<3> & p, const Vec<3> & t, const Vec<3> & t2, + NgArray & surfids, double eps) const; @@ -133,9 +164,9 @@ namespace netgen p + s t + s*s/2 t2 + r m with first order **/ - void TangentialEdgeSolid (const Point<3> & p, const Vec<3> & t, const Vec<3> & t2, - const Vec<3> & m, - Solid *& tansol, NgArray & surfids, double eps) const; + unique_ptr TangentialEdgeSolid (const Point<3> & p, const Vec<3> & t, const Vec<3> & t2, + const Vec<3> & m, + NgArray & surfids, double eps) const; void CalcOnePrimitiveSpecialPoints (const Box<3> & box, NgArray > & pts) const; @@ -180,24 +211,24 @@ namespace netgen int & in, int & strin) const; /// void RecTangentialSolid (const Point<3> & p, Solid *& tansol, NgArray & surfids, - int & in, int & strin, double eps) const; + bool & in, bool & strin, double eps) const; void RecTangentialSolid2 (const Point<3> & p, const Vec<3> & vec, Solid *& tansol, NgArray & surfids, - int & in, int & strin, double eps) const; + bool & in, bool & strin, double eps) const; /// void RecTangentialSolid3 (const Point<3> & p, const Vec<3> & vec,const Vec<3> & vec2, Solid *& tansol, NgArray & surfids, - int & in, int & strin, double eps) const; + bool & in, bool & strin, double eps) const; /// void RecTangentialEdgeSolid (const Point<3> & p, const Vec<3> & t, const Vec<3> & t2, const Vec<3> & m, Solid *& tansol, NgArray & surfids, - int & in, int & strin, double eps) const; + bool & in, bool & strin, double eps) const; /// void RecEdge (const Point<3> & p, const Vec<3> & v, - int & in, int & strin, int & faces, double eps) const; + bool & in, bool & strin, int & faces, double eps) const; /// void CalcSurfaceInverseRec (int inv); /// diff --git a/libsrc/csg/specpoin.cpp b/libsrc/csg/specpoin.cpp index 1855508c..e9c4821d 100644 --- a/libsrc/csg/specpoin.cpp +++ b/libsrc/csg/specpoin.cpp @@ -286,36 +286,30 @@ namespace netgen dynamic_cast (geometry->GetSurface(locsurf[k3])), pts); - for (int j = 0; j < pts.Size(); j++) - if (Dist (pts[j], box.Center()) < box.Diam()/2) + for (auto pnt : pts) + if (Dist (pnt, box.Center()) < box.Diam()/2) { - Solid * tansol; - sol -> TangentialSolid (pts[j], tansol, surfids, 1e-9*size); - - if(!tansol) - continue; - - bool ok1 = false, ok2 = false, ok3 = false; - int rep1 = geometry->GetSurfaceClassRepresentant(locsurf[k1]); - int rep2 = geometry->GetSurfaceClassRepresentant(locsurf[k2]); - int rep3 = geometry->GetSurfaceClassRepresentant(locsurf[k3]); - for(int jj=0; jjGetSurfaceClassRepresentant(surfids[jj]); - if(actrep == rep1) ok1 = true; - if(actrep == rep2) ok2 = true; - if(actrep == rep3) ok3 = true; - } - - - if (tansol && ok1 && ok2 && ok3) - // if (sol -> IsIn (pts[j], 1e-6*size) && !sol->IsStrictIn (pts[j], 1e-6*size)) - { - if (AddPoint (pts[j], layer)) - (*testout) << "cross point found, 1: " << pts[j] << endl; - } - delete tansol; - } + auto tansol = sol -> TangentialSolid (pnt, surfids, 1e-9*size); + if (tansol) + { + bool ok1 = false, ok2 = false, ok3 = false; + int rep1 = geometry->GetSurfaceClassRepresentant(locsurf[k1]); + int rep2 = geometry->GetSurfaceClassRepresentant(locsurf[k2]); + int rep3 = geometry->GetSurfaceClassRepresentant(locsurf[k3]); + + for (auto surfid : surfids) + { + int actrep = geometry->GetSurfaceClassRepresentant(surfid); + if (actrep == rep1) ok1 = true; + if (actrep == rep2) ok2 = true; + if (actrep == rep3) ok3 = true; + } + + if (ok1 && ok2 && ok3) + if (AddPoint (pnt, layer)) + (*testout) << "cross point found, 1: " << pnt << endl; + } + } } @@ -330,39 +324,30 @@ namespace netgen qsurf, pts); //(*testout) << "checking pot. crosspoints: " << pts << endl; - for (int j = 0; j < pts.Size(); j++) - if (Dist (pts[j], box.Center()) < box.Diam()/2) + for (auto pnt : pts) + if (Dist (pnt, box.Center()) < box.Diam()/2) { - Solid * tansol; - sol -> TangentialSolid (pts[j], tansol, surfids, 1e-9*size); - - if(!tansol) - continue; - - bool ok1 = false, ok2 = false, ok3 = true;//false; - int rep1 = geometry->GetSurfaceClassRepresentant(locsurf[k1]); - int rep2 = geometry->GetSurfaceClassRepresentant(locsurf[k2]); - //int rep3 = geometry->GetSurfaceClassRepresentant(quadi); - for(int jj=0; jjGetSurfaceClassRepresentant(surfids[jj]); - if(actrep == rep1) ok1 = true; - if(actrep == rep2) ok2 = true; - //if(actrep == rep3) ok3 = true; - } - - - if (tansol && ok1 && ok2 && ok3) - //if (sol -> IsIn (pts[j], 1e-6*size) && !sol->IsStrictIn (pts[j], 1e-6*size) ) - { - if (AddPoint (pts[j], layer)) - (*testout) << "cross point found, 2: " << pts[j] << endl; - } - delete tansol; + auto tansol = sol -> TangentialSolid (pnt, surfids, 1e-9*size); + if (tansol) + { + bool ok1 = false, ok2 = false, ok3 = true;//false; + int rep1 = geometry->GetSurfaceClassRepresentant(locsurf[k1]); + int rep2 = geometry->GetSurfaceClassRepresentant(locsurf[k2]); + + for (auto surfid : surfids) + { + int actrep = geometry->GetSurfaceClassRepresentant(surfid); + if (actrep == rep1) ok1 = true; + if (actrep == rep2) ok2 = true; + } + + if (ok1 && ok2 && ok3) + if (AddPoint (pnt, layer)) + (*testout) << "cross point found, 2: " << pnt << endl; + } } } - for (int k1 = 0; k1 < numprim; k1++) if (k1 != quadi) { @@ -372,15 +357,10 @@ namespace netgen for (int j = 0; j < pts.Size(); j++) if (Dist (pts[j], box.Center()) < box.Diam()/2) { - Solid * tansol; - sol -> TangentialSolid (pts[j], tansol, surfids, 1e-9*size); + auto tansol = sol -> TangentialSolid (pts[j], surfids, 1e-9*size); if (tansol) - // sol -> IsIn (pts[j], 1e-6*size) && !sol->IsStrictIn (pts[j], 1e-6*size) ) - { - if (AddPoint (pts[j], layer)) - (*testout) << "extremal point found, 1: " << pts[j] << endl; - } - delete tansol; + if (AddPoint (pts[j], layer)) + (*testout) << "extremal point found, 1: " << pts[j] << endl; } } } @@ -395,8 +375,6 @@ namespace netgen NgArray > pts; NgArray surfids; - - for (int k1 = 0; k1 < numprim; k1++) for (int k2 = 0; k2 < k1; k2++) for (int k3 = 0; k3 < k2; k3++) @@ -409,16 +387,14 @@ namespace netgen for (int j = 0; j < pts.Size(); j++) if (Dist (pts[j], box.Center()) < box.Diam()/2) { - Solid * tansol; - sol -> TangentialSolid (pts[j], tansol, surfids, 1e-9*size); - - if(!tansol) - continue; + auto tansol = sol -> TangentialSolid (pts[j], surfids, 1e-9*size); + if (!tansol) continue; bool ok1 = false, ok2 = false, ok3 = false; int rep1 = geometry->GetSurfaceClassRepresentant(locsurf[k1]); int rep2 = geometry->GetSurfaceClassRepresentant(locsurf[k2]); int rep3 = geometry->GetSurfaceClassRepresentant(locsurf[k3]); + for(int jj=0; jjGetSurfaceClassRepresentant(surfids[jj]); @@ -427,14 +403,9 @@ namespace netgen if(actrep == rep3) ok3 = true; } - - if (tansol && ok1 && ok2 && ok3) - // if (sol -> IsIn (pts[j], 1e-6*size) && !sol->IsStrictIn (pts[j], 1e-6*size)) - { - if (AddPoint (pts[j], layer)) - (*testout) << "cross point found, 1: " << pts[j] << endl; - } - delete tansol; + if (ok1 && ok2 && ok3) + if (AddPoint (pts[j], layer)) + (*testout) << "cross point found, 1: " << pts[j] << endl; } } @@ -449,23 +420,37 @@ namespace netgen for (int j = 0; j < pts.Size(); j++) if (Dist (pts[j], box.Center()) < box.Diam()/2) { - Solid * tansol; - sol -> TangentialSolid (pts[j], tansol, surfids, 1e-9*size); + auto tansol = sol -> TangentialSolid (pts[j], surfids, 1e-9*size); if (tansol) - // sol -> IsIn (pts[j], 1e-6*size) && !sol->IsStrictIn (pts[j], 1e-6*size) ) - { - if (AddPoint (pts[j], layer)) - (*testout) << "extremal point found, spheres: " << pts[j] << endl; - } - delete tansol; + if (AddPoint (pts[j], layer)) + (*testout) << "extremal point found, spheres: " << pts[j] << endl; } } return; } - } + if (numprim == 2) + { + auto rev0 = dynamic_cast (geometry->GetSurface(locsurf[0])); + auto rev1 = dynamic_cast (geometry->GetSurface(locsurf[1])); + if (rev0 && rev1) + { + NgArray> pts; + bool check = ComputeExtremalPoints (rev0, rev1, pts); + if (check) + { + for (auto p : pts) + if (box.IsIn(p)) + AddPoint (p, layer); + return; + } + } + } + } // end if (numprim <= check_crosspoint) + + possiblecrossp = (numprim >= 3) && calccp; surecrossp = 0; @@ -494,7 +479,6 @@ namespace netgen (*testout) << "k1,2,3 = " << k1 << "," << k2 << "," << k3 << ", nc = " << nc << ", deg = " << deg << endl; #endif - if (!nc && !deg) decision = 0; if (nc) surecrossp = 1; } @@ -608,9 +592,9 @@ namespace netgen decision = 0; } } - - // (*testout) << "l = " << level << " dec/sureexp = " << decision << sureexp << endl; - +#ifdef DEVELOP + (*testout) << "edgepnt decision = " << decision << " sure = " << sureexp << endl; +#endif if (decision && sureexp) { for (int k1 = 0; k1 < locsurf.Size() - 1; k1++) @@ -890,9 +874,18 @@ namespace netgen f1->CalcGradient (p, g1); f2->CalcGradient (p, g2); - if ( sqr (g1 * g2) > (1 - 1e-10) * Abs2 (g1) * Abs2 (g2)) - return 1; + // if ( sqr (g1 * g2) > (1 - 1e-10) * Abs2 (g1) * Abs2 (g2)) + // return 1; + if ( Abs2 (Cross(g1,g2)) < 1e-10 * Abs2 (g1) * Abs2 (g2)) // same, but stable + { + if (Abs2(vrs) < 1e-12*sqr(size)) // degenerate only if on both surfaces + return 1; + else + return 0; + } + + for (int j = 0; j < 3; j++) { mat(0,j) = g1(j); @@ -1534,7 +1527,61 @@ namespace netgen } + bool SpecialPointCalculation :: + ComputeExtremalPoints (const RevolutionFace * rev1, + const RevolutionFace * rev2, + NgArray > & pts) + { + // if (rev1 -> P0() != rev2 -> P0()) return false; // missing ???? + if (Dist2 (rev1 -> P0(), rev2 -> P0()) > 1e-20*sqr(size)) return false; + // if (rev1 -> Axis() != rev2 -> Axis()) return false; + if ( (rev1 -> Axis()-rev2 -> Axis()).Length2() > 1e-16) return false; + Point<2> p1s = rev1->GetSpline().StartPI(); + Point<2> p1e = rev1->GetSpline().EndPI(); + Point<2> p2s = rev2->GetSpline().StartPI(); + Point<2> p2e = rev2->GetSpline().EndPI(); + + Point<2> p2d; + if (Dist2(p1s,p2e) < 1e-20*sqr(size)) + p2d = p1s; + else if (Dist2(p1e, p2s) < 1e-20*sqr(size)) + p2d = p1e; + else + return false; + *testout << "Norm axis = " << rev1->Axis().Length() << endl; + Point<3> center = rev1->P0() + p2d(0)*rev1->Axis(); + Vec<3> n = rev1->Axis(); + // extremal points of circle, center, normal axis, radius p2d(1) + // Lagrange: + // L(x, lam1, lam2) = x_i + lam1 * (x-c)*v + lam2 * ( |x-c|^2 - r^2 ) + for (double i = 0; i < 3; i++) + { + double lam1 = -n(i) / n.Length2(); + Vec<3> ei(0,0,0); ei(i) = 1; + // double lam2 = 1/(2*p2d(1)) * sqrt(1 - sqr(n(i))/n.Length2()); + double fac = 1-sqr(n(i))/n.Length2(); + // if (fabs(lam2) > 1e-10) + if (fac > 1e-10) + { + double lam2 = 1/(2*p2d(1)) * sqrt(fac); + Point<3> x = center - 1.0/(2*lam2) * (ei + lam1*n); + pts.Append (x); + x = center + 1.0/(2*lam2) * (ei + lam1*n); + pts.Append (x); + + /* + // check: + Point<2> p2d; + rev1 -> CalcProj (x, p2d); + *testout << "special solution, p2d = " << p2d << endl; + rev2 -> CalcProj (x, p2d); + *testout << "special solution, p2d = " << p2d << endl; + */ + } + } + return true; + } @@ -1739,9 +1786,7 @@ namespace netgen continue; - Solid * locsol; - sol -> TangentialSolid (p, locsol, surfind, ideps*geomsize); - + auto locsol = sol -> TangentialSolid (p, surfind, ideps*geomsize); rep_surfind.SetSize (surfind.Size()); int num_indep_surfs = 0; @@ -1769,10 +1814,10 @@ namespace netgen if (surf) { // locsol -> GetSurfaceIndices (surfind); - bool hassurf = 0; + bool hassurf = false; for (int m = 0; m < surfind.Size(); m++) if (ageometry.GetSurface(surfind[m]) == surf) - hassurf = 1; + hassurf = true; if (!hassurf) continue; @@ -1818,8 +1863,11 @@ namespace netgen } - if (Abs2 (t) < 1e-8) - continue; + if (Abs2 (t) < 1e-16) + { + // cerr << "normal vectors degenerated" << endl; + continue; + } #ifdef DEVELOP *testout << " tangential vector " << t << endl; @@ -1860,17 +1908,17 @@ namespace netgen rhs(0) = -t * (hessej * t); rhs(1) = -t * (hessek * t); - CalcInverse (mat, inv); - t2 = inv * rhs; - - + CalcInverse (mat, inv); + t2 = inv * rhs; +#ifdef DEVELOP + *testout << "t = " << t << ", t2 = " << t2 << endl; +#endif /* ageometry.GetIndependentSurfaceIndices (locsol, p, t, surfind2); */ - Solid * locsol2; - locsol -> TangentialSolid3 (p, t, t2, locsol2, surfind2, ideps*geomsize); + auto locsol2 = locsol -> TangentialSolid3 (p, t, t2, surfind2, ideps*geomsize); if (!locsol2) continue; // locsol2 -> GetTangentialSurfaceIndices3 (p, t, t2, surfind2, 1e-9*geomsize); @@ -1911,31 +1959,31 @@ namespace netgen Vec<3> nv = ageometry.GetSurface(surfind2[l]) -> GetNormalVector(p); - Vec<3> m1 = Cross (t, nv); Vec<3> m2 = -m1; bool isface1 = 0, isface2 = 0; - Solid * locsol3; - // locsol2 -> TangentialSolid2 (p, m1, locsol3, surfind3, 1e-9*geomsize); - locsol -> TangentialEdgeSolid (p, t, t2, m1, locsol3, surfind3, ideps*geomsize); - + auto locsol3 = locsol -> TangentialEdgeSolid (p, t, t2, m1, surfind3, ideps*geomsize); +#ifdef DEVELOP + (*testout) << "m1 = " << m1 << ", surfind3 = " << surfind3 << endl; +#endif //ageometry.GetIndependentSurfaceIndices (surfind3); if (surfind3.Contains(surfind2[l])) isface1 = 1; - delete locsol3; // locsol2 -> TangentialSolid2 (p, m2, locsol3, surfind3, 1e-9*geomsize); - locsol -> TangentialEdgeSolid (p, t, t2, m2, locsol3, surfind3, ideps*geomsize); + locsol3 = locsol -> TangentialEdgeSolid (p, t, t2, m2, surfind3, ideps*geomsize); +#ifdef DEVELOP + (*testout) << "m2 = " << m2 << ", surfind3 = " << surfind3 << endl; +#endif // ageometry.GetIndependentSurfaceIndices (surfind3); if (surfind3.Contains(surfind2[l])) isface2 = 1; - delete locsol3; if (isface1 != isface2) cnt_tang_faces++; @@ -1948,7 +1996,6 @@ namespace netgen if (cnt_tang_faces < 1) ok = false; - delete locsol2; if (!ok) continue; } @@ -1973,16 +2020,47 @@ namespace netgen continue; Vec<3> s = Cross (normalvecs[m], t); + Vec<3> t2a = t + 0.01 *s; Vec<3> t2b = t - 0.01 *s; - - bool isface = + + bool isfaceold = (locsol->VectorIn (p, t2a, 1e-6*geomsize) && !locsol->VectorStrictIn (p, t2a, 1e-6*geomsize)) || (locsol->VectorIn (p, t2b, 1e-6*geomsize) && !locsol->VectorStrictIn (p, t2b, 1e-6*geomsize)); - + + bool isfacenew = + locsol -> VecInSolid2(p, t, s, 1e-6*geomsize) == DOES_INTERSECT || + locsol -> VecInSolid2(p, t, -s, 1e-6*geomsize) == DOES_INTERSECT; + /* + (locsol->VectorIn2 (p, t, s, 1e-6*geomsize) && !locsol->VectorStrictIn2 (p, t, s, 1e-6*geomsize)) || + (locsol->VectorIn2 (p, t, -s, 1e-6*geomsize) && !locsol->VectorStrictIn2 (p, t, -s, 1e-6*geomsize)); + */ + + bool isface = isfacenew; + + if (isfaceold != isfacenew) + { + *testout << "different, p = " << p << ", t = " << t << ", s = " << s << endl; + *testout << "tlo = " << si << endl; + *testout << "isface, old = " << isface << ", isfacenew = " << isfacenew << endl; + + *testout << "t2a = " << t2a << endl; + *testout << "vecin(p,t2a) = " << locsol->VectorIn (p, t2a, 1e-6*geomsize) << endl; + *testout << "vecstrictin(p,t2a) = " << locsol->VectorStrictIn (p, t2a, 1e-6*geomsize) << endl; + *testout << "vectorin2 = " << locsol->VectorIn2 (p, t, s, 1e-6*geomsize) << endl; + *testout << "vectorstrictin2 = " << locsol->VectorStrictIn2 (p, t, s, 1e-6*geomsize) << endl; + + *testout << "t2b = " << t2b << endl; + *testout << "vecin(p,t2b) = " << locsol->VectorIn (p, t2b, 1e-6*geomsize) << endl; + *testout << "vecstrictin(p,t2b) = " << locsol->VectorStrictIn (p, t2b, 1e-6*geomsize) << endl; + *testout << "vectorin2- = " << locsol->VectorIn2 (p, t, -s, 1e-6*geomsize) << endl; + *testout << "vectorstrictin2- = " << locsol->VectorStrictIn2 (p, t, -s, 1e-6*geomsize) << endl; + } + + /* bool isface = (locsol->VectorIn (p, t2a) && @@ -1992,10 +2070,8 @@ namespace netgen !locsol->VectorStrictIn (p, t2b)); */ - if (isface) - { - cnts++; - } + if (isface) + cnts++; } if (cnts < 2) isedge = 0; } @@ -2054,54 +2130,22 @@ namespace netgen } } - - delete locsol; } } - /* - NgBitArray testuncond (specpoints.Size()); - testuncond.Clear(); - for(int i = 0; i same; - same.Append(i); - - for(int j = i+1; j > & pts); + bool ComputeExtremalPoints (const RevolutionFace * rev1, + const RevolutionFace * rev2, + NgArray > & pts); void ComputeCrossPoints (const Plane * plane1, const Plane * plane2, diff --git a/libsrc/csg/surface.cpp b/libsrc/csg/surface.cpp index 8efe1f7a..6231897b 100644 --- a/libsrc/csg/surface.cpp +++ b/libsrc/csg/surface.cpp @@ -16,6 +16,7 @@ Surface :: Surface () strcpy (name, "noname"); bcprop = -1; bcname = "default"; + inverse = false; } Surface :: ~Surface() @@ -427,11 +428,20 @@ VecInSolid2 (const Point<3> & p, if (hv1 >= eps) return IS_OUTSIDE; + double hv2 = v2 * hv; + if (hv2 <= -eps) + return IS_INSIDE; + if (hv2 >= eps) + return IS_OUTSIDE; + return DOES_INTERSECT; + + /* double hv2 = v2 * hv; if (hv2 <= 0) return IS_INSIDE; else return IS_OUTSIDE; + */ } diff --git a/libsrc/csg/surface.hpp b/libsrc/csg/surface.hpp index a17fd3da..f09d8c51 100644 --- a/libsrc/csg/surface.hpp +++ b/libsrc/csg/surface.hpp @@ -213,8 +213,6 @@ namespace netgen INSOLID_TYPE; - - class DummySurface : public Surface { virtual double CalcFunctionValue (const Point<3> & /* point */) const @@ -293,9 +291,13 @@ namespace netgen const Vec<3> & m, double eps) const; + // for a point p in the surface, into which (closed) surfaces does v point into ? virtual void GetTangentialVecSurfaceIndices (const Point<3> & p, const Vec<3> & v, NgArray & surfind, double eps) const; + // a point p in the surface, and v a tangential vector + // for arbitrary small, but positive t consider q := Project(p+t*v) + // into which (closed) surfaces does v2 point into, when starting from q ? virtual void GetTangentialVecSurfaceIndices2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2, NgArray & surfind, double eps) const; diff --git a/libsrc/general/CMakeLists.txt b/libsrc/general/CMakeLists.txt index 50cd53ce..cf1f4c63 100644 --- a/libsrc/general/CMakeLists.txt +++ b/libsrc/general/CMakeLists.txt @@ -11,7 +11,7 @@ target_sources(gen INTERFACE install(FILES ngarray.hpp autodiff.hpp autoptr.hpp ngbitarray.hpp dynamicmem.hpp hashtabl.hpp mpi_interface.hpp myadt.hpp - ngsimd.hpp mystring.hpp netgenout.hpp ngpython.hpp + mystring.hpp netgenout.hpp ngpython.hpp optmem.hpp parthreads.hpp seti.hpp sort.hpp spbita2d.hpp stack.hpp table.hpp template.hpp gzstream.h diff --git a/libsrc/general/mpi_interface.hpp b/libsrc/general/mpi_interface.hpp index b7de6d64..82f40720 100644 --- a/libsrc/general/mpi_interface.hpp +++ b/libsrc/general/mpi_interface.hpp @@ -14,65 +14,8 @@ namespace netgen { - // using ngcore::id; - // using ngcore::ntasks; - -#ifndef PARALLEL - /** without MPI, we need a dummy typedef **/ - // typedef int MPI_Comm; -#endif - - /** This is the "standard" communicator that will be used for netgen-objects. **/ - // extern DLL_HEADER NgMPI_Comm ng_comm; #ifdef OLD -#ifdef PARALLEL - inline int MyMPI_GetNTasks (MPI_Comm comm /* = ng_comm */) - { - int ntasks; - MPI_Comm_size(comm, &ntasks); - return ntasks; - } - inline int MyMPI_GetId (MPI_Comm comm /* = ng_comm */) - { - int id; - MPI_Comm_rank(comm, &id); - return id; - } -#else - // enum { MPI_COMM_WORLD = 12345, MPI_COMM_NULL = 0}; - inline int MyMPI_GetNTasks (MPI_Comm comm /* = ng_comm */) { return 1; } - inline int MyMPI_GetId (MPI_Comm comm /* = ng_comm */) { return 0; } -#endif -#endif - - /* -#ifdef PARALLEL - // For python wrapping of communicators - struct PyMPI_Comm { - MPI_Comm comm; - bool owns_comm; - PyMPI_Comm (MPI_Comm _comm, bool _owns_comm = false) : comm(_comm), owns_comm(_owns_comm) { } - PyMPI_Comm (const PyMPI_Comm & c) = delete; - ~PyMPI_Comm () { - if (owns_comm) - MPI_Comm_free(&comm); - } - inline int Rank() const { return MyMPI_GetId(comm); } - inline int Size() const { return MyMPI_GetNTasks(comm); } - }; -#else - // dummy without MPI - struct PyMPI_Comm { - MPI_Comm comm = 0; - PyMPI_Comm (MPI_Comm _comm, bool _owns_comm = false) { } - ~PyMPI_Comm () { } - inline int Rank() const { return 0; } - inline int Size() const { return 1; } - }; -#endif - */ - #ifdef PARALLEL template inline MPI_Datatype MyGetMPIType ( ) @@ -93,32 +36,37 @@ namespace netgen typedef int MPI_Datatype; template inline MPI_Datatype MyGetMPIType ( ) { return 0; } #endif +#endif + #ifdef PARALLEL enum { MPI_TAG_CMD = 110 }; enum { MPI_TAG_MESH = 210 }; enum { MPI_TAG_VIS = 310 }; - inline void MyMPI_Send (int i, int dest, int tag, MPI_Comm comm /* = ng_comm */) + + [[deprecated("mympi_send int, use comm.Send instead")]] + inline void MyMPI_Send (int i, int dest, int tag, MPI_Comm comm) { int hi = i; MPI_Send( &hi, 1, MPI_INT, dest, tag, comm); } - - inline void MyMPI_Recv (int & i, int src, int tag, MPI_Comm comm /* = ng_comm */) + + [[deprecated("mympi_revc int, use comm.Recv instead")]] + inline void MyMPI_Recv (int & i, int src, int tag, MPI_Comm comm) { MPI_Status status; MPI_Recv( &i, 1, MPI_INT, src, tag, comm, &status); } - - - inline void MyMPI_Send (const string & s, int dest, int tag, MPI_Comm comm /* = ng_comm */) + [[deprecated("mympi_send string, use comm.Send instead")]] + inline void MyMPI_Send (const string & s, int dest, int tag, MPI_Comm comm) { MPI_Send( const_cast (s.c_str()), s.length(), MPI_CHAR, dest, tag, comm); } - inline void MyMPI_Recv (string & s, int src, int tag, MPI_Comm comm /* = ng_comm */) + [[deprecated("mympi_revc string, use comm.Recv instead")]] + inline void MyMPI_Recv (string & s, int src, int tag, MPI_Comm comm) { MPI_Status status; int len; @@ -130,34 +78,35 @@ namespace netgen - template - inline void MyMPI_Send (NgFlatArray s, int dest, int tag, MPI_Comm comm /* = ng_comm */) + [[deprecated("mympi_send ngflatarray, use comm.send instead")]] + inline void MyMPI_Send (NgFlatArray s, int dest, int tag, MPI_Comm comm) { - MPI_Send( &s.First(), s.Size(), MyGetMPIType(), dest, tag, comm); + MPI_Send( &s.First(), s.Size(), GetMPIType(), dest, tag, comm); } template - inline void MyMPI_Recv ( NgFlatArray s, int src, int tag, MPI_Comm comm /* = ng_comm */) + [[deprecated("mympi_recv ngflatarray, use comm.Recv instead")]] + inline void MyMPI_Recv ( NgFlatArray s, int src, int tag, MPI_Comm comm) { MPI_Status status; - MPI_Recv( &s.First(), s.Size(), MyGetMPIType(), src, tag, comm, &status); + MPI_Recv( &s.First(), s.Size(), GetMPIType(), src, tag, comm, &status); } template - inline void MyMPI_Recv ( NgArray & s, int src, int tag, MPI_Comm comm /* = ng_comm */) + inline void MyMPI_Recv ( NgArray & s, int src, int tag, MPI_Comm comm) { MPI_Status status; int len; MPI_Probe (src, tag, comm, &status); - MPI_Get_count (&status, MyGetMPIType(), &len); + MPI_Get_count (&status, GetMPIType(), &len); s.SetSize (len); - MPI_Recv( &s.First(), len, MyGetMPIType(), src, tag, comm, &status); + MPI_Recv( &s.First(), len, GetMPIType(), src, tag, comm, &status); } template - inline int MyMPI_Recv ( NgArray & s, int tag, MPI_Comm comm /* = ng_comm */) + inline int MyMPI_Recv ( NgArray & s, int tag, MPI_Comm comm) { MPI_Status status; int len; @@ -165,10 +114,10 @@ namespace netgen int src = status.MPI_SOURCE; - MPI_Get_count (&status, MyGetMPIType(), &len); + MPI_Get_count (&status, GetMPIType(), &len); s.SetSize (len); - MPI_Recv( &s.First(), len, MyGetMPIType(), src, tag, comm, &status); + MPI_Recv( &s.First(), len, GetMPIType(), src, tag, comm, &status); return src; } @@ -190,22 +139,23 @@ namespace netgen */ template - inline MPI_Request MyMPI_ISend (NgFlatArray s, int dest, int tag, MPI_Comm comm /* = ng_comm */) + [[deprecated("mympi_isend ngflatarray, use comm.send instead")]] + inline MPI_Request MyMPI_ISend (NgFlatArray s, int dest, int tag, MPI_Comm comm) { MPI_Request request; - MPI_Isend( &s.First(), s.Size(), MyGetMPIType(), dest, tag, comm, &request); + MPI_Isend( &s.First(), s.Size(), GetMPIType(), dest, tag, comm, &request); return request; } - template - inline MPI_Request MyMPI_IRecv (NgFlatArray s, int dest, int tag, MPI_Comm comm /* = ng_comm */) + [[deprecated("mympi_irecv ngflatarray, use comm.recv instead")]] + inline MPI_Request MyMPI_IRecv (NgFlatArray s, int dest, int tag, MPI_Comm comm) { MPI_Request request; - MPI_Irecv( &s.First(), s.Size(), MyGetMPIType(), dest, tag, comm, &request); + MPI_Irecv( &s.First(), s.Size(), GetMPIType(), dest, tag, comm, &request); return request; } - + /* template inline void MyMPI_ISend (NgFlatArray s, int dest, int tag) @@ -232,106 +182,59 @@ namespace netgen receive-table entries will be set */ - /* template inline void MyMPI_ExchangeTable (TABLE & send_data, TABLE & recv_data, int tag, - MPI_Comm comm = MPI_COMM_WORLD) + const NgMPI_Comm & comm) { - int ntasks, rank; - MPI_Comm_size(comm, &ntasks); - MPI_Comm_rank(comm, &rank); - - NgArray requests; - for (int dest = 0; dest < ntasks; dest++) - if (dest != rank) - requests.Append (MyMPI_ISend (send_data[dest], dest, tag, comm)); - - for (int i = 0; i < ntasks-1; i++) - { - MPI_Status status; - MPI_Probe (MPI_ANY_SOURCE, tag, comm, &status); - int size, src = status.MPI_SOURCE; - MPI_Get_count (&status, MPI_INT, &size); - recv_data.SetEntrySize (src, size, sizeof(T)); - requests.Append (MyMPI_IRecv (recv_data[src], src, tag, comm)); - } - MPI_Barrier (comm); - MPI_Waitall (requests.Size(), &requests[0], MPI_STATUS_IGNORE); - } - */ - - template - inline void MyMPI_ExchangeTable (TABLE & send_data, - TABLE & recv_data, int tag, - const NgMPI_Comm & comm /* = ng_comm */) - { - /* - int rank = MyMPI_GetId(comm); - int ntasks = MyMPI_GetNTasks(comm); - */ int rank = comm.Rank(); int ntasks = comm.Size(); - NgArray send_sizes(ntasks); - NgArray recv_sizes(ntasks); + Array send_sizes(ntasks); + Array recv_sizes(ntasks); for (int i = 0; i < ntasks; i++) send_sizes[i] = send_data[i].Size(); + + comm.AllToAll (send_sizes, recv_sizes); - MPI_Alltoall (&send_sizes[0], 1, MPI_INT, - &recv_sizes[0], 1, MPI_INT, comm); - - // in-place is buggy ! -// MPI_Alltoall (MPI_IN_PLACE, 1, MPI_INT, -// &recv_sizes[0], 1, MPI_INT, comm); - - for (int i = 0; i < ntasks; i++) recv_data.SetEntrySize (i, recv_sizes[i], sizeof(T)); - NgArray requests; + Array requests; for (int dest = 0; dest < ntasks; dest++) if (dest != rank && send_data[dest].Size()) - requests.Append (MyMPI_ISend (send_data[dest], dest, tag, comm)); + requests.Append (comm.ISend (FlatArray(send_data[dest]), dest, tag)); for (int dest = 0; dest < ntasks; dest++) if (dest != rank && recv_data[dest].Size()) - requests.Append (MyMPI_IRecv (recv_data[dest], dest, tag, comm)); + requests.Append (comm.IRecv (FlatArray(recv_data[dest]), dest, tag)); - // MPI_Barrier (comm); - MPI_Waitall (requests.Size(), &requests[0], MPI_STATUS_IGNORE); + MyMPI_WaitAll (requests); } - - - - - extern void MyMPI_SendCmd (const char * cmd); extern string MyMPI_RecvCmd (); - - template - inline void MyMPI_Bcast (T & s, MPI_Comm comm /* = ng_comm */) + inline void MyMPI_Bcast (T & s, MPI_Comm comm) { - MPI_Bcast (&s, 1, MyGetMPIType(), 0, comm); + MPI_Bcast (&s, 1, GetMPIType(), 0, comm); } template - inline void MyMPI_Bcast (NgArray & s, NgMPI_Comm comm /* = ng_comm */) + inline void MyMPI_Bcast (NgArray & s, NgMPI_Comm comm) { int size = s.Size(); MyMPI_Bcast (size, comm); // if (MyMPI_GetId(comm) != 0) s.SetSize (size); if (comm.Rank() != 0) s.SetSize (size); - MPI_Bcast (&s[0], size, MyGetMPIType(), 0, comm); + MPI_Bcast (&s[0], size, GetMPIType(), 0, comm); } template - inline void MyMPI_Bcast (NgArray & s, int root, MPI_Comm comm /* = ng_comm */) + inline void MyMPI_Bcast (NgArray & s, int root, MPI_Comm comm) { int id; MPI_Comm_rank(comm, &id); @@ -340,67 +243,23 @@ namespace netgen MPI_Bcast (&size, 1, MPI_INT, root, comm); if (id != root) s.SetSize (size); if ( !size ) return; - MPI_Bcast (&s[0], size, MyGetMPIType(), root, comm); + MPI_Bcast (&s[0], size, GetMPIType(), root, comm); } template - inline void MyMPI_Allgather (const T & send, NgFlatArray recv, MPI_Comm comm /* = ng_comm */) + [[deprecated("mympi_allgather deprecated, use comm.allgather")]] + inline void MyMPI_Allgather (const T & send, NgFlatArray recv, MPI_Comm comm) { - MPI_Allgather( const_cast (&send), 1, MyGetMPIType(), &recv[0], 1, MyGetMPIType(), comm); + MPI_Allgather( const_cast (&send), 1, GetMPIType(), &recv[0], 1, GetMPIType(), comm); } template - inline void MyMPI_Alltoall (NgFlatArray send, NgFlatArray recv, MPI_Comm comm /* = ng_comm */) + [[deprecated("mympi_alltoall deprecated, use comm.alltoall")]] + inline void MyMPI_Alltoall (NgFlatArray send, NgFlatArray recv, MPI_Comm comm) { - MPI_Alltoall( &send[0], 1, MyGetMPIType(), &recv[0], 1, MyGetMPIType(), comm); + MPI_Alltoall( &send[0], 1, GetMPIType(), &recv[0], 1, GetMPIType(), comm); } -// template -// inline void MyMPI_Alltoall_Block (NgFlatArray send, NgFlatArray recv, int blocklen, MPI_Comm comm = ng_comm) -// { -// MPI_Alltoall( &send[0], blocklen, MyGetMPIType(), &recv[0], blocklen, MyGetMPIType(), comm); -// } - - - - /* - inline void MyMPI_Send ( int *& s, int len, int dest, int tag) - { - int hlen = len; - MPI_Send( &hlen, 1, MPI_INT, dest, tag, MPI_COMM_WORLD); - MPI_Send( s, len, MPI_INT, dest, tag, MPI_COMM_WORLD); - } - - - inline void MyMPI_Recv ( int *& s, int & len, int src, int tag) - { - MPI_Status status; - MPI_Recv( &len, 1, MPI_INT, src, tag, MPI_COMM_WORLD, &status); - if ( s ) - delete [] s; - s = new int [len]; - MPI_Recv( s, len, MPI_INT, src, tag, MPI_COMM_WORLD, &status); - } - - - - inline void MyMPI_Send ( double * s, int len, int dest, int tag) - { - MPI_Send( &len, 1, MPI_INT, dest, tag, MPI_COMM_WORLD); - MPI_Send( s, len, MPI_DOUBLE, dest, tag, MPI_COMM_WORLD); - } - - - inline void MyMPI_Recv ( double *& s, int & len, int src, int tag) - { - MPI_Status status; - MPI_Recv( &len, 1, MPI_INT, src, tag, MPI_COMM_WORLD, &status); - if ( s ) - delete [] s; - s = new double [len]; - MPI_Recv( s, len, MPI_DOUBLE, src, tag, MPI_COMM_WORLD, &status); - } - */ #endif // PARALLEL diff --git a/libsrc/general/myadt.hpp b/libsrc/general/myadt.hpp index 123bd04c..ad6fd1c4 100644 --- a/libsrc/general/myadt.hpp +++ b/libsrc/general/myadt.hpp @@ -47,7 +47,5 @@ namespace netgen #include "netgenout.hpp" #include "gzstream.h" -#include "ngsimd.hpp" - #endif diff --git a/libsrc/general/ngarray.hpp b/libsrc/general/ngarray.hpp index f3eea9d4..05c773a3 100644 --- a/libsrc/general/ngarray.hpp +++ b/libsrc/general/ngarray.hpp @@ -205,6 +205,12 @@ namespace netgen { return ( Pos(elem) >= 0 ); } + + operator FlatArray () const + { + static_assert (BASE==0); + return FlatArray(size, data); + } }; @@ -730,7 +736,7 @@ namespace netgen /// bubble sort array template - inline void BubbleSort (NgFlatArray & data, NgFlatArray & slave) + inline void BubbleSort (NgFlatArray & data, NgFlatArray & index) { for (int i = 0; i < data.Size(); i++) for (int j = i+1; j < data.Size(); j++) @@ -740,16 +746,16 @@ namespace netgen data[i] = data[j]; data[j] = hv; - S hvs = slave[i]; - slave[i] = slave[j]; - slave[j] = hvs; + S hvs = index[i]; + index[i] = index[j]; + index[j] = hvs; } } template void QuickSortRec (NgFlatArray & data, - NgFlatArray & slave, + NgFlatArray & index, int left, int right) { int i = left; @@ -764,20 +770,20 @@ namespace netgen if (i <= j) { ngcore::Swap (data[i], data[j]); - ngcore::Swap (slave[i], slave[j]); + ngcore::Swap (index[i], index[j]); i++; j--; } } while (i <= j); - if (left < j) QuickSortRec (data, slave, left, j); - if (i < right) QuickSortRec (data, slave, i, right); + if (left < j) QuickSortRec (data, index, left, j); + if (i < right) QuickSortRec (data, index, i, right); } template - void QuickSort (NgFlatArray & data, NgFlatArray & slave) + void QuickSort (NgFlatArray & data, NgFlatArray & index) { if (data.Size() > 1) - QuickSortRec (data, slave, 0, data.Size()-1); + QuickSortRec (data, index, 0, data.Size()-1); } diff --git a/libsrc/general/ngsimd.hpp b/libsrc/general/ngsimd.hpp deleted file mode 100644 index 9170e402..00000000 --- a/libsrc/general/ngsimd.hpp +++ /dev/null @@ -1,672 +0,0 @@ -#ifndef FILE_NGSIMD -#define FILE_NGSIMD -/**************************************************************************/ -/* File: ngsimd.hpp */ -/* Author: Joachim Schoeberl */ -/* Date: 25. Mar. 16 */ -/**************************************************************************/ - -#include -#include -#include -#include -#include -#include - -#include - -#ifdef WIN32 -#ifndef AVX_OPERATORS_DEFINED -#define AVX_OPERATORS_DEFINED -NG_INLINE __m128d operator- (__m128d a) { return _mm_xor_pd(a, _mm_set1_pd(-0.0)); } -NG_INLINE __m128d operator+ (__m128d a, __m128d b) { return _mm_add_pd(a,b); } -NG_INLINE __m128d operator- (__m128d a, __m128d b) { return _mm_sub_pd(a,b); } -NG_INLINE __m128d operator* (__m128d a, __m128d b) { return _mm_mul_pd(a,b); } -NG_INLINE __m128d operator/ (__m128d a, __m128d b) { return _mm_div_pd(a,b); } -NG_INLINE __m128d operator* (double a, __m128d b) { return _mm_set1_pd(a)*b; } -NG_INLINE __m128d operator* (__m128d b, double a) { return _mm_set1_pd(a)*b; } - -NG_INLINE __m128d operator+= (__m128d &a, __m128d b) { return a = a+b; } -NG_INLINE __m128d operator-= (__m128d &a, __m128d b) { return a = a-b; } -NG_INLINE __m128d operator*= (__m128d &a, __m128d b) { return a = a*b; } -NG_INLINE __m128d operator/= (__m128d &a, __m128d b) { return a = a/b; } - -NG_INLINE __m256d operator- (__m256d a) { return _mm256_xor_pd(a, _mm256_set1_pd(-0.0)); } -NG_INLINE __m256d operator+ (__m256d a, __m256d b) { return _mm256_add_pd(a,b); } -NG_INLINE __m256d operator- (__m256d a, __m256d b) { return _mm256_sub_pd(a,b); } -NG_INLINE __m256d operator* (__m256d a, __m256d b) { return _mm256_mul_pd(a,b); } -NG_INLINE __m256d operator/ (__m256d a, __m256d b) { return _mm256_div_pd(a,b); } -NG_INLINE __m256d operator* (double a, __m256d b) { return _mm256_set1_pd(a)*b; } -NG_INLINE __m256d operator* (__m256d b, double a) { return _mm256_set1_pd(a)*b; } - -NG_INLINE __m256d operator+= (__m256d &a, __m256d b) { return a = a+b; } -NG_INLINE __m256d operator-= (__m256d &a, __m256d b) { return a = a-b; } -NG_INLINE __m256d operator*= (__m256d &a, __m256d b) { return a = a*b; } -NG_INLINE __m256d operator/= (__m256d &a, __m256d b) { return a = a/b; } -#endif -#endif - - - -namespace ngsimd -{ - // MSVC does not define SSE. It's always present on 64bit cpus -#if (defined(_M_AMD64) || defined(_M_X64) || defined(__AVX__)) -#ifndef __SSE__ -#define __SSE__ -#endif -#ifndef __SSE2__ -#define __SSE2__ -#endif -#endif - - - constexpr int GetDefaultSIMDSize() { -#if defined __AVX512F__ - return 8; -#elif defined __AVX__ - return 4; -#elif defined __SSE__ - return 2; -#else - return 1; -#endif - } - -#if defined __AVX512F__ - typedef __m512 tAVX; - typedef __m512d tAVXd; -#elif defined __AVX__ - typedef __m256 tAVX; - typedef __m256d tAVXd; -#elif defined __SSE__ - typedef __m128 tAVX; - typedef __m128d tAVXd; -#endif - - - - template class SIMD; - - template - struct has_call_operator - { - template static std::true_type check( decltype( sizeof(&C::operator() )) ) { return std::true_type(); } - template static std::false_type check(...) { return std::false_type(); } - typedef decltype( check(sizeof(char)) ) type; - static constexpr type value = type(); - }; - - template - // a*b+c - NG_INLINE auto FMA(T1 a, T2 b, T3 c) - { - return a*b+c; - } - - template::value, int>::type = 0> - NG_INLINE SIMD operator+ (T a, SIMD b) { return SIMD(a) + b; } - template::value, int>::type = 0> - NG_INLINE SIMD operator- (T a, SIMD b) { return SIMD(a) - b; } - template::value, int>::type = 0> - NG_INLINE SIMD operator* (T a, SIMD b) { return SIMD(a) * b; } - template::value, int>::type = 0> - NG_INLINE SIMD operator/ (T a, SIMD b) { return SIMD(a) / b; } - template::value, int>::type = 0> - NG_INLINE SIMD operator+ (SIMD a, T b) { return a + SIMD(b); } - template::value, int>::type = 0> - NG_INLINE SIMD operator- (SIMD a, T b) { return a - SIMD(b); } - template::value, int>::type = 0> - NG_INLINE SIMD operator* (SIMD a, T b) { return a * SIMD(b); } - template::value, int>::type = 0> - NG_INLINE SIMD operator/ (SIMD a, T b) { return a / SIMD(b); } - - -using std::sqrt; -using std::fabs; - - class ExceptionNOSIMD : public std::runtime_error - { - public: - using std::runtime_error::runtime_error; - std::string What() { return what(); } - }; - - using std::exp; - template NG_INLINE SIMD exp (SIMD a) - { - return SIMD([&](int i)->double { return exp(a[i]); } ); - } - - using std::log; - template NG_INLINE SIMD log (SIMD a) - { - return SIMD([&](int i)->double { return log(a[i]); } ); - } - - using std::pow; - template NG_INLINE SIMD pow (SIMD a, double x) - { - return SIMD([&](int i)->double { return pow(a[i],x); } ); - } - - using std::sin; - template NG_INLINE SIMD sin (SIMD a) - { - return SIMD([&](int i)->double { return sin(a[i]); } ); - } - - using std::cos; - template NG_INLINE SIMD cos (SIMD a) - { - return SIMD([&](int i)->double { return cos(a[i]); } ); - } - - using std::tan; - template NG_INLINE SIMD tan (SIMD a) - { - return SIMD([&](int i)->double { return tan(a[i]); } ); - } - - using std::atan; - template NG_INLINE SIMD atan (SIMD a) - { - return SIMD([&](int i)->double { return atan(a[i]); } ); - } - - -///////////////////////////////////////////////////////////////////////////// -// SIMD width 1 (in case no AVX support is available) -///////////////////////////////////////////////////////////////////////////// - template<> - class SIMD - { - double data; - - public: - static constexpr int Size() { return 1; } - SIMD () = default; - SIMD (const SIMD &) = default; - SIMD & operator= (const SIMD &) = default; - - // only called if T has a call operator of appropriate type - template>::value, int>::type = 0> - SIMD (const T & func) - { - data = func(0); - } - - // only called if T is arithmetic (integral or floating point types) - template::value, int>::type = 0> - SIMD (const T & val) - { - data = val; - } - - SIMD (double const * p) - { - data = *p; - } - - NG_INLINE operator double() const { return data; } - NG_INLINE double operator[] (int i) const { return ((double*)(&data))[i]; } - NG_INLINE double Data() const { return data; } - NG_INLINE double & Data() { return data; } - - NG_INLINE SIMD &operator+= (SIMD b) { data+=b.Data(); return *this; } - NG_INLINE SIMD &operator-= (SIMD b) { data-=b.Data(); return *this; } - NG_INLINE SIMD &operator*= (SIMD b) { data*=b.Data(); return *this; } - NG_INLINE SIMD &operator/= (SIMD b) { data/=b.Data(); return *this; } - - }; - - NG_INLINE SIMD operator+ (SIMD a, SIMD b) { return a.Data()+b.Data(); } - NG_INLINE SIMD operator- (SIMD a, SIMD b) { return a.Data()-b.Data(); } - NG_INLINE SIMD operator- (SIMD a) { return -a.Data(); } - NG_INLINE SIMD operator* (SIMD a, SIMD b) { return a.Data()*b.Data(); } - NG_INLINE SIMD operator/ (SIMD a, SIMD b) { return a.Data()/b.Data(); } - - NG_INLINE SIMD sqrt (SIMD a) { return std::sqrt(a.Data()); } - NG_INLINE SIMD floor (SIMD a) { return std::floor(a.Data()); } - NG_INLINE SIMD ceil (SIMD a) { return std::ceil(a.Data()); } - NG_INLINE SIMD fabs (SIMD a) { return std::fabs(a.Data()); } - NG_INLINE SIMD L2Norm2 (SIMD a) { return a.Data()*a.Data(); } - NG_INLINE SIMD Trans (SIMD a) { return a; } - NG_INLINE SIMD IfPos (SIMD a, SIMD b, SIMD c) - { - return (a.Data() > 0) ? b : c; - } - - NG_INLINE double HSum (SIMD sd) - { - return sd.Data(); - } - - NG_INLINE auto HSum (SIMD sd1, SIMD sd2) - { - return std::make_tuple(sd1.Data(), sd2.Data()); - } - - NG_INLINE auto HSum (SIMD sd1, SIMD sd2, SIMD sd3, SIMD sd4) - { - return std::make_tuple(sd1.Data(), sd2.Data(), sd3.Data(), sd4.Data()); - } - - -///////////////////////////////////////////////////////////////////////////// -// SSE - Simd width 2 -///////////////////////////////////////////////////////////////////////////// -#ifdef __SSE__ - template<> - class alignas(16) SIMD - { - __m128d data; - - public: - static constexpr int Size() { return 2; } - SIMD () = default; - SIMD (const SIMD &) = default; - SIMD & operator= (const SIMD &) = default; - - SIMD (__m128d adata) - : data(adata) - { ; } - - // only called if T has a call operator of appropriate type - template>::value, int>::type = 0> - SIMD (const T & func) - { - data = _mm_set_pd(func(1), func(0)); - } - - // only called if T is arithmetic (integral or floating point types) - template::value, int>::type = 0> - SIMD (const T & val) - { - data = _mm_set1_pd(val); - } - - SIMD (double const * p) - { - data = _mm_loadu_pd(p); - } - - NG_INLINE operator __m128d() const { return data; } - NG_INLINE double operator[] (int i) const { return ((double*)(&data))[i]; } - NG_INLINE double& operator[] (int i) { return ((double*)(&data))[i]; } - NG_INLINE __m128d Data() const { return data; } - NG_INLINE __m128d & Data() { return data; } - - // NG_INLINE operator std::tuple () - // { return std::tuple((*this)[0], (*this)[1], (*this)[2], (*this)[3]); } - - - NG_INLINE SIMD &operator+= (SIMD b) { data+=b.Data(); return *this; } - NG_INLINE SIMD &operator-= (SIMD b) { data-=b.Data(); return *this; } - NG_INLINE SIMD &operator*= (SIMD b) { data*=b.Data(); return *this; } - NG_INLINE SIMD &operator/= (SIMD b) { data/=b.Data(); return *this; } - - }; - - NG_INLINE SIMD operator+ (SIMD a, SIMD b) { return a.Data()+b.Data(); } - NG_INLINE SIMD operator- (SIMD a, SIMD b) { return a.Data()-b.Data(); } - NG_INLINE SIMD operator- (SIMD a) { return -a.Data(); } - NG_INLINE SIMD operator* (SIMD a, SIMD b) { return a.Data()*b.Data(); } - NG_INLINE SIMD operator/ (SIMD a, SIMD b) { return a.Data()/b.Data(); } - - /* - NG_INLINE SIMD sqrt (SIMD a) { return _mm256_sqrt_pd(a.Data()); } - NG_INLINE SIMD floor (SIMD a) { return _mm256_floor_pd(a.Data()); } - NG_INLINE SIMD ceil (SIMD a) { return _mm256_ceil_pd(a.Data()); } - NG_INLINE SIMD fabs (SIMD a) { return _mm256_max_pd(a.Data(), -a.Data()); } - NG_INLINE SIMD L2Norm2 (SIMD a) { return a.Data()*a.Data(); } - NG_INLINE SIMD Trans (SIMD a) { return a; } - NG_INLINE SIMD IfPos (SIMD a, SIMD b, SIMD c) - { - auto cp = _mm256_cmp_pd (a.Data(), _mm256_setzero_pd(), _CMP_GT_OS); - return _mm256_blendv_pd(c.Data(), b.Data(), cp); - } - - NG_INLINE double HSum (SIMD sd) - { - __m128d hv = _mm_add_pd (_mm256_extractf128_pd(sd.Data(),0), _mm256_extractf128_pd(sd.Data(),1)); - return _mm_cvtsd_f64 (_mm_hadd_pd (hv, hv)); - } - - NG_INLINE auto HSum (SIMD sd1, SIMD sd2) - { - __m256d hv = _mm256_hadd_pd(sd1.Data(), sd2.Data()); - __m128d hv2 = _mm_add_pd (_mm256_extractf128_pd(hv,0), _mm256_extractf128_pd(hv,1)); - return std::make_tuple(_mm_cvtsd_f64 (hv2), _mm_cvtsd_f64(_mm_shuffle_pd (hv2, hv2, 3))); - } - - NG_INLINE auto HSum (SIMD v1, SIMD v2, SIMD v3, SIMD v4) - { - __m256d hsum1 = _mm256_hadd_pd (v1.Data(), v2.Data()); - __m256d hsum2 = _mm256_hadd_pd (v3.Data(), v4.Data()); - __m256d hsum = _mm256_add_pd (_mm256_permute2f128_pd (hsum1, hsum2, 1+2*16), - _mm256_blend_pd (hsum1, hsum2, 12)); - return SIMD(hsum); - } - */ -#endif // __SSE__ - - - -///////////////////////////////////////////////////////////////////////////// -// AVX - Simd width 4 -///////////////////////////////////////////////////////////////////////////// -#ifdef __AVX__ - template<> - class alignas(32) SIMD - { - __m256d data; - - public: - static constexpr int Size() { return 4; } - SIMD () = default; - SIMD (const SIMD &) = default; - SIMD & operator= (const SIMD &) = default; - - SIMD (__m256d adata) - : data(adata) - { ; } - - // only called if T has a call operator of appropriate type - template>::value, int>::type = 0> - SIMD (const T & func) - { - data = _mm256_set_pd(func(3), func(2), func(1), func(0)); - } - - // only called if T is arithmetic (integral or floating point types) - template::value, int>::type = 0> - SIMD (const T & val) - { - data = _mm256_set1_pd(val); - } - - SIMD (double const * p) - { - data = _mm256_loadu_pd(p); - } - - NG_INLINE operator __m256d() const { return data; } - NG_INLINE double operator[] (int i) const { return ((double*)(&data))[i]; } - NG_INLINE double& operator[] (int i) { return ((double*)(&data))[i]; } - NG_INLINE __m256d Data() const { return data; } - NG_INLINE __m256d & Data() { return data; } - - NG_INLINE operator std::tuple () - { return std::tuple((*this)[0], (*this)[1], (*this)[2], (*this)[3]); } - - - NG_INLINE SIMD &operator+= (SIMD b) { data+=b.Data(); return *this; } - NG_INLINE SIMD &operator-= (SIMD b) { data-=b.Data(); return *this; } - NG_INLINE SIMD &operator*= (SIMD b) { data*=b.Data(); return *this; } - NG_INLINE SIMD &operator/= (SIMD b) { data/=b.Data(); return *this; } - - }; - - NG_INLINE SIMD operator+ (SIMD a, SIMD b) { return a.Data()+b.Data(); } - NG_INLINE SIMD operator- (SIMD a, SIMD b) { return a.Data()-b.Data(); } - NG_INLINE SIMD operator- (SIMD a) { return -a.Data(); } - NG_INLINE SIMD operator* (SIMD a, SIMD b) { return a.Data()*b.Data(); } - NG_INLINE SIMD operator/ (SIMD a, SIMD b) { return a.Data()/b.Data(); } - - NG_INLINE SIMD sqrt (SIMD a) { return _mm256_sqrt_pd(a.Data()); } - NG_INLINE SIMD floor (SIMD a) { return _mm256_floor_pd(a.Data()); } - NG_INLINE SIMD ceil (SIMD a) { return _mm256_ceil_pd(a.Data()); } - NG_INLINE SIMD fabs (SIMD a) { return _mm256_max_pd(a.Data(), -a.Data()); } - NG_INLINE SIMD L2Norm2 (SIMD a) { return a.Data()*a.Data(); } - NG_INLINE SIMD Trans (SIMD a) { return a; } - NG_INLINE SIMD IfPos (SIMD a, SIMD b, SIMD c) - { - auto cp = _mm256_cmp_pd (a.Data(), _mm256_setzero_pd(), _CMP_GT_OS); - return _mm256_blendv_pd(c.Data(), b.Data(), cp); - } - - NG_INLINE double HSum (SIMD sd) - { - __m128d hv = _mm_add_pd (_mm256_extractf128_pd(sd.Data(),0), _mm256_extractf128_pd(sd.Data(),1)); - return _mm_cvtsd_f64 (_mm_hadd_pd (hv, hv)); - } - - NG_INLINE auto HSum (SIMD sd1, SIMD sd2) - { - __m256d hv = _mm256_hadd_pd(sd1.Data(), sd2.Data()); - __m128d hv2 = _mm_add_pd (_mm256_extractf128_pd(hv,0), _mm256_extractf128_pd(hv,1)); - return std::make_tuple(_mm_cvtsd_f64 (hv2), _mm_cvtsd_f64(_mm_shuffle_pd (hv2, hv2, 3))); - } - - NG_INLINE auto HSum (SIMD v1, SIMD v2, SIMD v3, SIMD v4) - { - __m256d hsum1 = _mm256_hadd_pd (v1.Data(), v2.Data()); - __m256d hsum2 = _mm256_hadd_pd (v3.Data(), v4.Data()); - __m256d hsum = _mm256_add_pd (_mm256_permute2f128_pd (hsum1, hsum2, 1+2*16), - _mm256_blend_pd (hsum1, hsum2, 12)); - return SIMD(hsum); - } - -#endif // __AVX__ - -///////////////////////////////////////////////////////////////////////////// -// AVX512 - Simd width 8 -///////////////////////////////////////////////////////////////////////////// -#ifdef __AVX512F__ - template<> - class alignas(64) SIMD - { - __m512d data; - - public: - static constexpr int Size() { return 8; } - SIMD () = default; - SIMD (const SIMD &) = default; - SIMD & operator= (const SIMD &) = default; - - SIMD (__m512d adata) - : data(adata) - { ; } - - // only called if T has a call operator of appropriate type - template>::value, int>::type = 0> - SIMD (const T & func) - { - data = _mm512_set_pd(func(7), func(6), func(5), func(4), - func(3), func(2), func(1), func(0)); - } - - // only called if T is arithmetic (integral or floating point types) - template::value, int>::type = 0> - SIMD (const T & val) - { - data = _mm512_set1_pd(val); - } - - SIMD (double const * p) - { - data = _mm512_loadu_pd(p); - } - - NG_INLINE operator __m512d() const { return data; } - NG_INLINE double operator[] (int i) const { return ((double*)(&data))[i]; } - NG_INLINE __m512d Data() const { return data; } - NG_INLINE __m512d & Data() { return data; } - - NG_INLINE SIMD &operator+= (SIMD b) { data+=b.Data(); return *this; } - NG_INLINE SIMD &operator-= (SIMD b) { data-=b.Data(); return *this; } - NG_INLINE SIMD &operator*= (SIMD b) { data*=b.Data(); return *this; } - NG_INLINE SIMD &operator/= (SIMD b) { data/=b.Data(); return *this; } - - }; - - NG_INLINE SIMD operator- (SIMD a) { return _mm512_sub_pd(_mm512_setzero_pd(), a.Data()); } - - NG_INLINE SIMD operator+ (SIMD a, SIMD b) { return _mm512_add_pd(a.Data(),b.Data()); } - NG_INLINE SIMD operator- (SIMD a, SIMD b) { return _mm512_sub_pd(a.Data(),b.Data()); } - NG_INLINE SIMD operator* (SIMD a, SIMD b) { return _mm512_mul_pd(a.Data(),b.Data()); } - NG_INLINE SIMD operator/ (SIMD a, SIMD b) { return _mm512_div_pd(a.Data(),b.Data()); } - - NG_INLINE SIMD sqrt (SIMD a) { return _mm512_sqrt_pd(a.Data()); } - NG_INLINE SIMD floor (SIMD a) { return _mm512_floor_pd(a.Data()); } - NG_INLINE SIMD ceil (SIMD a) { return _mm512_ceil_pd(a.Data()); } - NG_INLINE SIMD fabs (SIMD a) { return _mm512_max_pd(a.Data(), -a.Data()); } - NG_INLINE SIMD L2Norm2 (SIMD a) { return a.Data()*a.Data(); } - NG_INLINE SIMD Trans (SIMD a) { return a; } - NG_INLINE SIMD IfPos (SIMD a, SIMD b, SIMD c) - { - auto cp = _mm512_cmp_pd_mask (a.Data(), _mm512_setzero_pd(), _MM_CMPINT_GT); - return _mm512_mask_blend_pd(cp, c.Data(), b.Data()); - } - - - template<> NG_INLINE auto FMA (SIMD a, SIMD b, SIMD c) - { - return _mm512_fmadd_pd (a.Data(), b.Data(), c.Data()); - } - - NG_INLINE double HSum (SIMD sd) - { - SIMD low = _mm512_extractf64x4_pd(sd.Data(),0); - SIMD high = _mm512_extractf64x4_pd(sd.Data(),1); - return HSum(low)+HSum(high); - } - - NG_INLINE auto HSum (SIMD sd1, SIMD sd2) - { - return std::make_tuple(HSum(sd1), HSum(sd2)); - } - - NG_INLINE SIMD HSum (SIMD v1, SIMD v2, SIMD v3, SIMD v4) - { - SIMD high1 = _mm512_extractf64x4_pd(v1.Data(),1); - SIMD high2 = _mm512_extractf64x4_pd(v2.Data(),1); - SIMD high3 = _mm512_extractf64x4_pd(v3.Data(),1); - SIMD high4 = _mm512_extractf64x4_pd(v4.Data(),1); - SIMD low1 = _mm512_extractf64x4_pd(v1.Data(),0); - SIMD low2 = _mm512_extractf64x4_pd(v2.Data(),0); - SIMD low3 = _mm512_extractf64x4_pd(v3.Data(),0); - SIMD low4 = _mm512_extractf64x4_pd(v4.Data(),0); - return HSum(low1,low2,low3,low4) + HSum(high1,high2,high3,high4); - } -#endif // __AVX512F__ - - -//////////////////////////////////////////////////////////////////////////////// -// MultiSIMD - Multiple SIMD values in one struct using head-tail implementation -//////////////////////////////////////////////////////////////////////////////// - template - class MultiSIMD - { - SIMD head; - MultiSIMD tail; - public: - MultiSIMD () = default; - MultiSIMD (const MultiSIMD & ) = default; - MultiSIMD (T v) : head(v), tail(v) { ; } - MultiSIMD (SIMD _head, MultiSIMD _tail) - : head(_head), tail(_tail) { ; } - SIMD Head() const { return head; } - MultiSIMD Tail() const { return tail; } - SIMD & Head() { return head; } - MultiSIMD & Tail() { return tail; } - - template - SIMD Get() const { return NR==0 ? head : tail.template Get(); } - template - SIMD & Get() { return NR==0 ? head : tail.template Get(); } - auto MakeTuple() { return std::tuple_cat(std::tuple&> (head), tail.MakeTuple()); } - // not yet possible for MSVC - // operator auto () { return MakeTuple(); } - }; - - template - class MultiSIMD<2,T> - { - SIMD v0, v1; - public: - MultiSIMD () = default; - MultiSIMD (const MultiSIMD & ) = default; - MultiSIMD (T v) : v0(v), v1(v) { ; } - MultiSIMD (SIMD _v0, SIMD _v1) : v0(_v0), v1(_v1) { ; } - - SIMD Head() const { return v0; } - SIMD Tail() const { return v1; } - SIMD & Head() { return v0; } - SIMD & Tail() { return v1; } - - template - SIMD Get() const { return NR==0 ? v0 : v1; } - template - SIMD & Get() { return NR==0 ? v0 : v1; } - auto MakeTuple() { return std::tuple&, SIMD&> (v0, v1); } - operator std::tuple&, SIMD&>() { return MakeTuple(); } - }; - - template NG_INLINE MultiSIMD operator+ (MultiSIMD a, MultiSIMD b) - { return MultiSIMD (a.Head()+b.Head(), a.Tail()+b.Tail()); } - template NG_INLINE MultiSIMD operator+ (double a, MultiSIMD b) - { return MultiSIMD (a+b.Head(), a+b.Tail()); } - template NG_INLINE MultiSIMD operator+ (MultiSIMD b, double a) - { return MultiSIMD (a+b.Head(), a+b.Tail()); } - - template NG_INLINE MultiSIMD operator- (MultiSIMD a, MultiSIMD b) - { return MultiSIMD (a.Head()-b.Head(), a.Tail()-b.Tail()); } - template NG_INLINE MultiSIMD operator- (double a, MultiSIMD b) - { return MultiSIMD (a-b.Head(), a-b.Tail()); } - template NG_INLINE MultiSIMD operator- (MultiSIMD b, double a) - { return MultiSIMD (b.Head()-a, b.Tail()-a); } - template NG_INLINE MultiSIMD operator- (MultiSIMD a) - { return MultiSIMD (-a.Head(), -a.Tail()); } - template NG_INLINE MultiSIMD operator* (MultiSIMD a, MultiSIMD b) - { return MultiSIMD (a.Head()*b.Head(), a.Tail()*b.Tail()); } - template NG_INLINE MultiSIMD operator/ (MultiSIMD a, MultiSIMD b) - { return MultiSIMD (a.Head()/b.Head(), a.Tail()/b.Tail()); } - template NG_INLINE MultiSIMD operator* (double a, MultiSIMD b) - { return MultiSIMD ( a*b.Head(), a*b.Tail()); } - template NG_INLINE MultiSIMD operator* (MultiSIMD b, double a) - { return MultiSIMD ( a*b.Head(), a*b.Tail()); } - - template NG_INLINE MultiSIMD & operator+= (MultiSIMD & a, MultiSIMD b) - { a.Head()+=b.Head(); a.Tail()+=b.Tail(); return a; } - template NG_INLINE MultiSIMD operator-= (MultiSIMD & a, double b) - { a.Head()-=b; a.Tail()-=b; return a; } - template NG_INLINE MultiSIMD operator-= (MultiSIMD & a, MultiSIMD b) - { a.Head()-=b.Head(); a.Tail()-=b.Tail(); return a; } - template NG_INLINE MultiSIMD & operator*= (MultiSIMD & a, MultiSIMD b) - { a.Head()*=b.Head(); a.Tail()*=b.Tail(); return a; } - template NG_INLINE MultiSIMD & operator*= (MultiSIMD & a, double b) - { a.Head()*=b; a.Tail()*=b; return a; } - // NG_INLINE MultiSIMD operator/= (MultiSIMD & a, MultiSIMD b) { return a.Data()/=b.Data(); } - - NG_INLINE SIMD HVSum (SIMD a) { return a; } - template - NG_INLINE SIMD HVSum (MultiSIMD a) { return a.Head() + HVSum(a.Tail()); } - - template NG_INLINE double HSum (MultiSIMD a) { return HSum(HVSum(a)); } - template NG_INLINE auto HSum (MultiSIMD a, MultiSIMD b) - { return HSum(HVSum(a), HVSum(b)); } - - template - std::ostream & operator<< (std::ostream & ost, MultiSIMD multi) - { - ost << multi.Head() << " " << multi.Tail(); - return ost; - } - - template - std::ostream & operator<< (std::ostream & ost, SIMD simd) - { - ost << simd[0]; - for (int i = 1; i < simd.Size(); i++) - ost << " " << simd[i]; - return ost; - } -} - -namespace netgen -{ - using namespace ngsimd; -} -#endif diff --git a/libsrc/general/optmem.hpp b/libsrc/general/optmem.hpp index 99ffb67d..b2be31d3 100644 --- a/libsrc/general/optmem.hpp +++ b/libsrc/general/optmem.hpp @@ -26,39 +26,13 @@ private: mutex block_allocator_mutex; public: /// - BlockAllocator (unsigned asize, unsigned ablocks = 100); + DLL_HEADER BlockAllocator (unsigned asize, unsigned ablocks = 100); /// - ~BlockAllocator (); + DLL_HEADER ~BlockAllocator (); /// - - void * Alloc (); - /* - { - if (!freelist) - Alloc2(); - - void * p = freelist; - // freelist = *(void**)freelist; - freelist = *static_cast (freelist); - - return p; - } - */ - - + DLL_HEADER void * Alloc (); /// - void Free (void * p); - /* - { - if (!bablocks.Size()) return; - *(void**)p = freelist; - freelist = p; - } - */ - - -private: - // void Alloc2 (); + DLL_HEADER void Free (void * p); }; } diff --git a/libsrc/general/parthreads.hpp b/libsrc/general/parthreads.hpp index 2e242484..d7f42a4a 100644 --- a/libsrc/general/parthreads.hpp +++ b/libsrc/general/parthreads.hpp @@ -96,8 +96,8 @@ void ParallelFor( int first, int next, const TFunc & f ) - typedef void (*TaskManager)(std::function); - typedef void (*Tracer)(string, bool); // false .. start, true .. stop + typedef void (*NgTaskManager)(std::function); + typedef void (*NgTracer)(string, bool); // false .. start, true .. stop inline void DummyTaskManager (std::function func) { @@ -108,7 +108,7 @@ void ParallelFor( int first, int next, const TFunc & f ) inline void DummyTracer (string, bool) { ; } template - inline void ParallelFor (TaskManager tm, size_t n, FUNC func) + inline void ParallelFor (NgTaskManager tm, size_t n, FUNC func) { (*tm) ([n,func] (size_t nr, size_t nums) { @@ -121,7 +121,7 @@ void ParallelFor( int first, int next, const TFunc & f ) } template - inline void ParallelForRange (TaskManager tm, size_t n, FUNC func) + inline void ParallelForRange (NgTaskManager tm, size_t n, FUNC func) { (*tm) ([n,func] (size_t nr, size_t nums) { diff --git a/libsrc/general/table.hpp b/libsrc/general/table.hpp index 7d2f6999..832fc751 100644 --- a/libsrc/general/table.hpp +++ b/libsrc/general/table.hpp @@ -114,11 +114,22 @@ public: /// Creates table of size size inline TABLE (int size) : BASE_TABLE (size) { ; } + TABLE (TABLE && tab2) + : BASE_TABLE(move(tab2)) + { } + /// Creates fixed maximal element size table inline TABLE (const NgFlatArray & entrysizes) : BASE_TABLE (NgFlatArray (entrysizes.Size(), const_cast(&entrysizes[BASE])), sizeof(T)) { ; } + + TABLE & operator= (TABLE && tab2) + { + BASE_TABLE::operator=(move(tab2)); + return *this; + } + /// Changes Size of table to size, deletes data inline void SetSize (int size) diff --git a/libsrc/general/template.hpp b/libsrc/general/template.hpp index afca79c7..31fac8d7 100644 --- a/libsrc/general/template.hpp +++ b/libsrc/general/template.hpp @@ -14,6 +14,8 @@ namespace netgen templates, global types, defines and variables */ +DLL_HEADER extern const string netgen_version; + /// The following value may be adapted to the hardware ! #ifndef CLOCKS_PER_SEC #define CLOCKS_PER_SEC 1000000 diff --git a/libsrc/geom2d/CMakeLists.txt b/libsrc/geom2d/CMakeLists.txt index f496448d..26fe62fc 100644 --- a/libsrc/geom2d/CMakeLists.txt +++ b/libsrc/geom2d/CMakeLists.txt @@ -1,10 +1,10 @@ -add_definitions(-DNGLIB_EXPORTS) -add_library(geom2d ${NG_LIB_TYPE} genmesh2d.cpp geometry2d.cpp python_geom2d.cpp ) +add_definitions(-DNGINTERFACE_EXPORTS) +add_library(geom2d ${NG_LIB_TYPE} csg2d.cpp genmesh2d.cpp geometry2d.cpp python_geom2d.cpp ) if(APPLE) set_target_properties( geom2d PROPERTIES SUFFIX ".so") endif(APPLE) -target_link_libraries(geom2d PUBLIC ngcore mesh PRIVATE netgen_python) +target_link_libraries(geom2d PUBLIC ngcore mesh PRIVATE "$") if(NOT WIN32) install( TARGETS geom2d ${NG_INSTALL_DIR}) endif(NOT WIN32) @@ -20,6 +20,6 @@ endif(USE_GUI) install(FILES geometry2d.hpp spline2d.hpp - vsgeom2d.hpp + vsgeom2d.hpp csg2d.hpp DESTINATION ${NG_INSTALL_DIR_INCLUDE}/geom2d COMPONENT netgen_devel ) diff --git a/libsrc/geom2d/csg2d.cpp b/libsrc/geom2d/csg2d.cpp new file mode 100644 index 00000000..dd8cf21a --- /dev/null +++ b/libsrc/geom2d/csg2d.cpp @@ -0,0 +1,2235 @@ +#include +#include +#include +#include +#include + +#include "csg2d.hpp" + +// Polygon clipping algorithm based on: +// Foster, Erich & Hormann, Kai & Popa, Romeo. (2019). Clipping Simple Polygons with Degenerate Intersections. Computers & Graphics: X. 2. 100007. 10.1016/j.cagx.2019.100007. +// extended to handle quadratic spline segments + +namespace netgen +{ +using ngcore::INT; + +constexpr static double EPSILON=0.000000001; + +void ComputeWeight( Spline & s, Point<2> p ) +{ + Point<2> a = s.StartPI(); + Point<2> b = s.TangentPoint(); + Point<2> c = s.EndPI(); + + double A = (p[1]-a[1])*(b[0]-p[0]) - (p[0]-a[0])*(b[1]-p[1]); + double B = (p[1]-c[1])*(b[0]-p[0]) - (p[0]-c[0])*(b[1]-p[1]); + double det = sqrt(-A*B); + double tt = fabs(A+det) fabs(v[1]) ? 0 : 1; + double weight = fabs(tt*(p[dim]-a[dim])/v[dim] + 1.0/tt*(p[dim]-c[dim])/v[dim]); + s.SetWeight(weight); +} + +void ToggleLabel(EntryExitLabel& status) +{ + if (status == ENTRY) + { + status = EXIT; + return; + } + if (status == EXIT) + { + status = ENTRY; + return; + } +} + +Spline Split( const Spline & s, double t0, double t1 ) +{ + if(t0==0.0 && t1==1.0) return s; + + Point<2> a = s.StartPI(); + if(t0!=0.0) + a = s.GetPoint(t0); + + Point<2> c = s.EndPI(); + if(t1!=1.0) + c = s.GetPoint(t1); + + // Find new midpoints by cutting the tangents at the new end points + auto tang0 = s.GetTangent(t0); + auto tang1 = s.GetTangent(t1); + + netgen::Mat<2,2> m, minv; + m(0,0) = tang0[0]; + m(1,0) = tang0[1]; + m(0,1) = -tang1[0]; + m(1,1) = -tang1[1]; + + CalcInverse(m, minv); + + Vec<2> lam = minv*(c-a); + + Point<2> b = a+lam[0]*tang0; + + auto res = Spline{a, b, c}; + + // compute weight of new spline such that p lies on it + Point<2> p = s.GetPoint(0.5*(t0+t1)); + ComputeWeight(res, p); + return res; +} + +Vertex * Vertex :: Insert(Point<2> p, double lam) +{ + auto vnew = make_unique(p); + vnew->lam = lam; + + Vertex * current = this; + + if(lam > -1.0) + { + do { + current = current->next; + } while (!current->is_source && current->lam < lam); + } + else + current = current->next; + + auto pre = current->prev; + if(lam > -1.0) + vnew->info = pre->info; + + pre->next = vnew.get(); + vnew->prev = pre; + vnew->next = current; + + vnew->pnext = std::move(current->prev->pnext); + + current->prev = vnew.get(); + + pre->pnext = std::move(vnew); + + return pre->next; +} + +IntersectionType ClassifyNonOverlappingIntersection( double alpha, double beta ) +{ + // classify alpha + bool alpha_is_0 = false; + bool alpha_in_0_1 = false; + + if ( (alpha > EPSILON) && (alpha < 1.0-EPSILON) ) + alpha_in_0_1 = true; + else + if (fabs(alpha) <= EPSILON) + alpha_is_0 = true; + + // classify beta + bool beta_is_0 = false; + bool beta_in_0_1 = false; + + if ( (beta > EPSILON) && (beta < 1.0-EPSILON) ) + beta_in_0_1 = true; + else + if (fabs(beta) <= EPSILON) + beta_is_0 = true; + + // distinguish intersection types + if (alpha_in_0_1 && beta_in_0_1) + return (X_INTERSECTION); + + if (alpha_is_0 && beta_in_0_1) + return (T_INTERSECTION_Q); + + if (beta_is_0 && alpha_in_0_1) + return (T_INTERSECTION_P); + + if (alpha_is_0 && beta_is_0) + return (V_INTERSECTION); + + return NO_INTERSECTION; +} + +IntersectionType ClassifyOverlappingIntersection( double alpha, double beta ) +{ + // classify alpha + bool alpha_is_0 = false; + bool alpha_in_0_1 = false; + bool alpha_not_in_0_1 = false; + + if ( (alpha > EPSILON) && (alpha < 1.0-EPSILON) ) + alpha_in_0_1 = true; + else + if (fabs(alpha) <= EPSILON) + alpha_is_0 = true; + else + alpha_not_in_0_1 = true; + + // classify beta + bool beta_is_0 = false; + bool beta_in_0_1 = false; + bool beta_not_in_0_1 = false; + + if ( (beta > EPSILON) && (beta < 1.0-EPSILON) ) + beta_in_0_1 = true; + else + if (fabs(alpha) <= EPSILON) + beta_is_0 = true; + else + beta_not_in_0_1 = true; + + // distinguish intersection types + if (alpha_in_0_1 && beta_in_0_1) + return (X_OVERLAP); + + if (alpha_not_in_0_1 && beta_in_0_1) + return (T_OVERLAP_Q); + + if (beta_not_in_0_1 && alpha_in_0_1) + return (T_OVERLAP_P); + + if (alpha_is_0 && beta_is_0) + return (V_OVERLAP); + + return NO_INTERSECTION; +} + +IntersectionType intersect(const Point<2> P1, const Point<2> P2, const Point<2> Q1, const Point<2> Q2, double& alpha, double& beta) +{ + double AP1 = Area(P1,Q1,Q2); + double AP2 = Area(P2,Q1,Q2); + + if (fabs(AP1-AP2) > EPSILON) + { + // (P1,P2) and (Q1,Q2) are not parallel + + double AQ1 = Area(Q1,P1,P2); + double AQ2 = Area(Q2,P1,P2); + + alpha = AP1 / (AP1-AP2); + beta = AQ1 / (AQ1-AQ2); + + return ClassifyNonOverlappingIntersection(alpha, beta); + } + else + if (fabs(AP1) < EPSILON) + { + // (P1,P2) and (Q1,Q2) are collinear + + auto dP = P2-P1; + auto dQ = Q2-Q1; + auto PQ = Q1-P1; + + alpha = (PQ*dP) / (dP*dP); + beta = -(PQ*dQ) / (dQ*dQ); + + return ClassifyOverlappingIntersection(alpha, beta); + } + return NO_INTERSECTION; +} + +IntersectionType IntersectSplineSegment( const Spline & s, const Point<2> & r0, const Point<2> & r1, double& alpha, double& beta ) +{ + Point<2> p0 = s.StartPI(); + Point<2> p1 = s.TangentPoint(); + Point<2> p2 = s.EndPI(); + + auto vr = r1-r0; + double a0 = vr[1]*(p0[0] - r0[0]) - vr[0]*(p0[1] - r0[1]); + double a1 = vr[1]*(p1[0] - r0[0]) - vr[0]*(p1[1] - r0[1]); + double a2 = vr[1]*(p2[0] - r0[0]) - vr[0]*(p2[1] - r0[1]); + a1 *= s.GetWeight(); + + double a_ = a0-a1+a2; + double b_ = a1-2*a0; + double c_ = a0; + + double det = b_*b_ - 4*a_*c_; + if(det<0.0) + return NO_INTERSECTION; + + double t; + + if(fabs(a_)>EPSILON) + { + double sqrt_det = sqrt(det); + double t1 = 1.0/(2*a_) * (-b_ + sqrt_det); + double t2 = 1.0/(2*a_) * (-b_ - sqrt_det); + t = min(t1, t2); + if(t fabs(vr[1]) ? 0 : 1; + beta = 1.0/vr[dim] * (s.GetPoint(t)[dim] - r0[dim]); + + return ClassifyNonOverlappingIntersection(alpha, beta); +} + +IntersectionType IntersectSplineSegment1( const Spline & s, const Point<2> & r0, const Point<2> & r1, double& alpha, double& beta, bool first=false) +{ + Point<2> p0 = s.StartPI(); + Point<2> p1 = s.TangentPoint(); + Point<2> p2 = s.EndPI(); + + auto vr = r1-r0; + double a0 = vr[1]*(p0[0] - r0[0]) - vr[0]*(p0[1] - r0[1]); + double a1 = vr[1]*(p1[0] - r0[0]) - vr[0]*(p1[1] - r0[1]); + double a2 = vr[1]*(p2[0] - r0[0]) - vr[0]*(p2[1] - r0[1]); + a1 *= s.GetWeight(); + + double a_ = a0-a1+a2; + double b_ = a1-2*a0; + double c_ = a0; + + double det = b_*b_ - 4*a_*c_; + if(det<-EPSILON) + return NO_INTERSECTION; + + if(detEPSILON) + { + vbeta[0] = 1.0/(2*a_) * (-b_ + sqrt_det); + vbeta[1] = 1.0/(2*a_) * (-b_ - sqrt_det); + } + else // degenrate quadratic equation + vbeta[0] = vbeta[1] = -c_/b_; + + int dim = fabs(vr[0]) > fabs(vr[1]) ? 0 : 1; + double valpha[2]; + valpha[0] = 1.0/vr[dim] * (s.GetPoint(vbeta[0])[dim] - r0[dim]); + valpha[1] = 1.0/vr[dim] * (s.GetPoint(vbeta[1])[dim] - r0[dim]); + + + IntersectionType vtype[2]; + vtype[0] = ClassifyNonOverlappingIntersection(valpha[0], vbeta[0]); + vtype[1] = ClassifyNonOverlappingIntersection(valpha[1], vbeta[1]); + + if(valpha[0]>valpha[1]) + { + swap(valpha[0], valpha[1]); + swap(vbeta[0], vbeta[1]); + swap(vtype[0], vtype[1]); + } + + int choice = 0; + if(!first) + { + if(vtype[0]==NO_INTERSECTION && vtype[1]!=NO_INTERSECTION) + choice = 1; + + if(valpha[0] < alpha+EPSILON) + choice = 1; + } + + if(valpha[choice] < alpha+EPSILON) + return NO_INTERSECTION; + + alpha = valpha[choice]; + beta = vbeta[choice]; + return vtype[choice]; +} + +bool IsOverlapping( Spline p, Spline s, double & alpha, double & beta, IntersectionType & type ) +{ + + auto p_mid = Center(p.StartPI(), p.EndPI()); + auto s_mid = Center(s.StartPI(), s.EndPI()); + + double lam0 = -1e3*EPSILON; + double lam1 = -1e3*EPSILON; + double lam2 = -1e3*EPSILON; + double lam3 = -1e3*EPSILON; + alpha=-1e8; + beta=-1e8; + double alpha_mid=-1e8; + double beta_mid=-1e8; + + // Check if s.p0 lies on p and vice versa, also check if tangents are in same direction (TODO: TEST) + // If so, assume overlapping splines + // TODO: Better checks! False positives could happen here! + if(Dist(s.StartPI(), p.StartPI())eps) return false; + if(fabs(lam1)>eps) return false; + if(fabs(lam2)>eps) return false; + if(fabs(lam3)>eps) return false; + if(fabs(1.0-err)>eps) return false; + + type = ClassifyOverlappingIntersection( alpha, beta ); + return true; +} + +bool IsInsideTrig( const array,3> & t, Point<2> r ) +{ + int w = 0; + Point<2> trig[4] = {t[0],t[1],t[2],t[0]}; + for(auto i : Range(3)) + w += CalcSide(trig[i], trig[i+1], r); + return ( (w % 2) != 0 ); +} + +bool IsCloseToTrig( const array,3> & t, Point<2> r, double eps=1e-4 ) +{ + r += eps * (Center(t[0], t[1], t[2])-r); // move point a bit to center of trig + return IsInsideTrig( t, r ); +} + +bool IsLeft( const Spline & s, Point<2> p ) +{ + Point<2> a = s.StartPI(); + Point<2> b = s.TangentPoint(); + Point<2> c = s.EndPI(); + + // simple check by approximating spline with segment + bool is_left = Area(p, a, c) > 0.0; + + // not close to spline -> simple check valid + if(!IsCloseToTrig( {a, b, c} , p )) + return is_left; + + // p is control point -> simple check valid + auto bp = p-b; + if(bp.Length2() < EPSILON) + return is_left; + + double sab = Area(p, a, b); + double sbc = Area(p, b, c); + if(fabs(sab) same side of spline as control point, simple test gives correct result + // weight decreases -> opposite side of spline as control point, adding control point to test polygon gives correct result + double old_weight = s.GetWeight(); + auto s_tmp = s; + ComputeWeight( s_tmp, p ); + double new_weight = s_tmp.GetWeight(); + + if(new_weight>old_weight) + return is_left; + + double sabc = Area(a, b, c); + + if (sabc > 0) + { + // chain makes a left turn + if (sab > 0 && sbc > 0) + return true; + else + return false; + } + else + { + // chain makes a right turn (or is straight) + if (sab < 0 && sbc < 0) + return false; + else + return true; + } +} + + + +IntersectionType IntersectTrig( Point<2> p0, Point<2> p1, const array,3> & trig) +{ + Point<2> lt[4] = { trig[0], trig[1], trig[2], trig[0] }; + + double alpha, beta; + for(auto i : IntRange(3)) + { + auto type = intersect(p0, p1, lt[i], lt[i+1], alpha, beta); + if(type != NO_INTERSECTION) + return type; + } + + return NO_INTERSECTION; +} + +bool IntersectTrigs( const array,3> & trig0, const array,3> & trig1) +{ + Point<2> lt0[4] = { trig0[0], trig0[1], trig0[2], trig0[0] }; + + for(auto i : IntRange(3)) + { + if(IntersectTrig(lt0[i], lt0[i+1], trig1)) + return true; + if(IsInsideTrig(trig0, trig1[i])) + return true; + if(IsInsideTrig(trig1, trig0[i])) + return true; + } + return false; +} + +bool BisectIntersect( Spline p, Spline s, double &t0, double &t1, double &s0, double &s1, int depth=-50) +{ + if(depth==0) + { + s0 = s1; + t0 = t1; + return true; + } + + bool side = depth%2==0; + + double & lam0 = side ? t0 : s0; + double & lam1 = side ? t1 : s1; + Spline & spline = side ? p : s; + Spline & spline_other = side ? s : p; + + double lam_mid = 0.5*(lam0+lam1); + auto left = Split(spline, lam0, lam_mid); + auto right = Split(spline, lam_mid, lam1); + + double & lam0_other = side ? s0 : t0; + double & lam1_other = side ? s1 : t1; + auto curr = Split(spline_other, lam0_other, lam1_other); + + bool left_hull_intersecting = IntersectTrigs( {left.StartPI(), left.TangentPoint(), left.EndPI()}, {curr.StartPI(), curr.TangentPoint(), curr.EndPI()}); + bool right_hull_intersecting = IntersectTrigs( {right.StartPI(), right.TangentPoint(), right.EndPI()}, {curr.StartPI(), curr.TangentPoint(), curr.EndPI()}); + + // TODO: Additionaly check if one spline intersects with convex hull of other? + // // Check if one spline intersects with convex hull of spline + // if(left_hull_intersecting) + // { + // double a,b; + // left_hull_intersecting = left.Intersect( curr.p0, curr.p1, a, b ); + // left_hull_intersecting |= left.Intersect( curr.p1, curr.p2, a, b ); + // left_hull_intersecting |= left.Intersect( curr.p2, curr.p0, a, b ); + // } + // + // if(right_hull_intersecting) + // { + // double a,b; + // right_hull_intersecting = right.Intersect( curr.p0, curr.p1, a, b ); + // right_hull_intersecting |= right.Intersect( curr.p1, curr.p2, a, b ); + // right_hull_intersecting |= right.Intersect( curr.p2, curr.p0, a, b ); + // } + + + if(!left_hull_intersecting && !right_hull_intersecting) + return false; + + if(left_hull_intersecting && right_hull_intersecting) + { + // cout << "intersect both sides " << endl; + double temp_lam; + temp_lam = lam1; + lam1 = lam_mid; + + double t0_ = t0; + double t1_ = t1; + double s0_ = s0; + double s1_ = s1; + + // cout << "recursive bisect " << t0 << ',' << t1 << ',' << s0 << ',' << s1 << endl; + bool first_intersecting = BisectIntersect(p, s, t0_, t1_, s0_, s1_, depth+1); + if(first_intersecting) + { + t0 = t0_; + t1 = t1_; + s0 = s0_; + s1 = s1_; + return true; + } + else + { + // cout << "search other side " << endl; + // no first intersection -> search other side + lam1 = temp_lam; + left_hull_intersecting = false; + } + } + + if(left_hull_intersecting) + lam1 = lam_mid; + else + lam0 = lam_mid; + + return BisectIntersect(p, s, t0, t1, s0, s1, depth+1); +} + +bool NewtonIntersect( Spline p, Spline s, double & alpha, double & beta ) +{ + + Point<2> p0, s0; + Vec<2> dp, ds, ddp, dds; + + p.GetDerivatives(alpha, p0, dp, ddp); + s.GetDerivatives(beta, s0, ds, dds); + + netgen::Mat<2,2> m, minv; + + m(0,0) = dp[0]; + m(1,0) = dp[1]; + m(0,1) = -ds[0]; + m(1,1) = -ds[1]; + + CalcInverse(m, minv); + + Vec<2> res = s0-p0; + Vec<2> h = minv*res; + alpha +=h[0]; + beta +=h[1]; + return true; +} + + +IntersectionType Intersect( Spline p, Spline s, double &alpha, double &beta) +{ + bool is_convex_hull_intersecting = IntersectTrigs( {p.StartPI(), p.TangentPoint(), p.EndPI()}, {s.StartPI(), s.TangentPoint(), s.EndPI()}); + if(!is_convex_hull_intersecting) + return NO_INTERSECTION; + + { + // Check if splines overlap + double alpha_ = alpha; + double beta_ = beta; + IntersectionType overlap_type; + bool have_overlap = IsOverlapping( p, s, alpha_, beta_, overlap_type ); + if(have_overlap) + { + alpha = alpha_; + beta = beta_; + return overlap_type; + } + } + + // Bisection + double t1 = 1.0; + double s1 = 1.0; + + bool have_intersection = false; + if(alpha>0.0) // alpha > 0 means, we have found one intersection already + { + // reverse parametrization of first spline to make sure, we find the second intersection first + auto p_ = Spline{p.EndPI(), p.TangentPoint(), p.StartPI(), p.GetWeight()}; + t1 = 1.0-alpha; + alpha = 0.0; + beta = 0.0; + + have_intersection = BisectIntersect(p_,s,alpha,t1,beta,s1); + alpha = 1.0-alpha; + } + else + have_intersection = BisectIntersect(p,s,alpha,t1,beta,s1); + + if(have_intersection) + { + for(auto i : IntRange(10)) + NewtonIntersect(p, s, alpha, beta); + return ClassifyNonOverlappingIntersection( alpha, beta ); + } + + return NO_INTERSECTION; +} + + +IntersectionType intersect(const Edge& edgeP, const Edge& edgeQ, double& alpha, double& beta) +{ + const Point<2>& P1 = *edgeP.v0; + const Point<2>& P2 = *edgeP.v1; + const Point<2>& Q1 = *edgeQ.v0; + const Point<2>& Q2 = *edgeQ.v1; + + if(edgeP.v0->spline) + { + if(edgeQ.v0->spline) + return Intersect(*edgeP.v0->spline, *edgeQ.v0->spline, alpha, beta); + else + return IntersectSplineSegment(*edgeP.v0->spline, Q1, Q2, alpha, beta); + } + else + { + if(edgeQ.v0->spline) + return IntersectSplineSegment1(*edgeQ.v0->spline, P1, P2, alpha, beta); + else + return intersect(P1, P2, Q1, Q2, alpha, beta); + } +} + +void AddIntersectionPoint(Edge edgeP, Edge edgeQ, IntersectionType i, double alpha, double beta) +{ + Point<2> I; + Vertex* I_P; + Vertex* I_Q; + + Vertex* P1 = edgeP.v0; + Vertex* Q1 = edgeQ.v0; + + switch(i) + { + case X_INTERSECTION: + if(edgeP.v0->spline) + I = edgeP.v0->spline->GetPoint(alpha); + else + I = *edgeP.v0 + alpha*(*edgeP.v1 - *edgeP.v0); + I_P = edgeP.v0->Insert(I, alpha); + I_Q = edgeQ.v0->Insert(I, beta); + I_P->Link(I_Q); + break; + + case X_OVERLAP: + I_Q = edgeQ.v0->Insert(*P1, beta); + P1->Link( I_Q); + + I_P = edgeP.v0->Insert(*Q1, alpha); + I_P->Link( Q1); + break; + + case T_INTERSECTION_Q: + case T_OVERLAP_Q: + I_Q = edgeQ.v0->Insert(*P1, beta); + P1->Link( I_Q); + break; + + case T_INTERSECTION_P: + case T_OVERLAP_P: + I_P = edgeP.v0->Insert(*Q1, alpha); + I_P->Link( Q1); + break; + + case V_INTERSECTION: + case V_OVERLAP: + P1->Link(Q1); + break; + default: + break; + } +} + +void RemoveDuplicates(Loop & poly) +{ + if(poly.first==nullptr) + return; + + Vertex * last = poly.first->prev; + for(auto v : poly.Vertices(ALL)) + { + if(Dist2(*v, *last)spline) + continue; + Spline ori{*v->spline}; + Vertex * curr = v; + do + { + auto next = curr->next; + if(!curr->is_source || !next->is_source) + { + double t0 = curr->is_source ? 0.0 : curr->lam; + double t1 = next->is_source ? 1.0 : next->lam; + curr->spline = Split(ori, t0, t1); + curr->lam = -1; + curr->is_source = true; + } + curr = next; + } while(!curr->is_source); + }; + RemoveDuplicates(l); +} + +void ComputeIntersections(Edge edgeP , Loop & l2) +{ + for (Edge edgeQ : l2.Edges(SOURCE)) + { + double alpha = -EPSILON; + double beta = -EPSILON; + IntersectionType i = intersect(edgeP, edgeQ, alpha, beta); + AddIntersectionPoint(edgeP, edgeQ, i, alpha, beta); + if(i==X_INTERSECTION && (edgeP.v0->spline || edgeQ.v0->spline)) + { + double alpha1 = alpha+1e2*EPSILON; + double beta1 = 0.0; //beta+1e2*EPSILON; + + // search for possible second intersection + i = intersect(edgeP, edgeQ, alpha1, beta1); + if(i!=NO_INTERSECTION && alpha+EPSILON MP; + if(edgeP.v0->spline) + { + MP = edgeP.v0->spline->GetPoint(alpha_mid); + edgeP.v0->Insert(MP, alpha_mid); + } + else + MP = edgeQ.v0->spline->GetPoint(beta_mid); + + if(edgeQ.v0->spline) + edgeQ.v0->Insert(MP, beta_mid); + + AddIntersectionPoint(edgeP, edgeQ, i, alpha1, beta1); + } + } + } +} + +void ComputeIntersections(Loop & l1, Loop & l2) +{ + static Timer t_intersect("find intersections"); + static Timer t_split("split splines"); + + t_intersect.Start(); + for (Edge edgeP : l1.Edges(SOURCE)) + ComputeIntersections(edgeP, l2); + t_intersect.Stop(); + + RegionTimer rt_split(t_split); + + SplitSplines(l1); + SplitSplines(l2); +} + +void ComputeIntersections(Solid2d & s1, Solid2d & s2) +{ + static Timer tall("ComputeIntersections"); RegionTimer rtall(tall); + + for (Loop& l1 : s1.polys) + for (Edge edgeP : l1.Edges(SOURCE)) + for (Loop& l2 : s2.polys) + ComputeIntersections(edgeP, l2); + + for (Loop& l1 : s1.polys) + SplitSplines(l1); + + for (Loop& l2 : s2.polys) + SplitSplines(l2); +} + +enum RelativePositionType +{ + LEFT, + RIGHT, + IS_P_m, + IS_P_p +}; + +inline RelativePositionType oracle_decide( double s1, double s2, double s3 ) +{ + if (s3 > 0) + { + // chain makes a left turn + if (s1 > 0 && s2 > 0) + return(LEFT); + else + return(RIGHT); + } + else + { + // chain makes a right turn (or is straight) + if (s1 < 0 && s2 < 0) + return(RIGHT); + else + return(LEFT); + } +} + +// no splines involved here +// decides if Point q is left or right of chain (p1,p2,p3) +RelativePositionType oracle_simple(Point<2> q, Point<2> p1, Point<2> p2, Point<2> p3) +{ + double s1 = Area( q, p1, p2); + double s2 = Area( q, p2, p3); + double s3 = Area( p1, p2, p3); + + // check relative position of q with respect to chain (p1,p2,p3) + return oracle_decide(s1, s2, s3); +} + +// (p1,p2) or (p2,p3) is a spline segment, compare with tangent (p1t,p2) instead of Segment (p1,p2) +// BUT take care if tangent is collinear with (q,p2) (then use the segment (p1,p2) again) +RelativePositionType oracle_spline_p(Point<2> q, Point<2> p1, Point<2> p1t, Point<2> p2, Point<2> p3, Point<2> p3t) +{ + double s1 = Area( q, p1t, p2); + double s2 = Area( q, p2, p3t); + + if(fabs(s1) < EPSILON) + { + p1t = p1; + s1 = Area( q, p1t, p2 ); + } + + if(fabs(s2) < EPSILON) + { + p3t = p3; + s2 = Area( q, p2, p3t ); + } + + double s3 = Area( p1t, p2, p3t); + + return oracle_decide(s1, s2, s3); +} + +// (q,p2) is a spline segment, compare with tangent (qt,p2) instead of Segment (q,p2) +// BUT take care if tangent at p2 is collinear with eiter (p1,p2) or (p2,p3) (then use the segment (q,p2) again) +RelativePositionType oracle_spline_q(Point<2> q, Point<2> qt, Point<2> p1, Point<2> p2, Point<2> p3) +{ + double s1 = Area( qt, p1, p2); + double s2 = Area( qt, p2, p3); + double s3 = Area( p1, p2, p3); + + if(fabs(s1) < EPSILON) + s1 = Area( q, p1, p2 ); + + if(fabs(s2) < EPSILON) + s2 = Area( q, p2, p3 ); + + return oracle_decide(s1, s2, s3); +} + +// splines at (Q,P2) and either (P1,P2) or (P2,P3) +// first use tangents to decide local orientation +// if tangents of two splines match, use IsLeft(spline, other end point) +// if tangent of spline and segment match, use simple methond (just end points) +RelativePositionType oracle_spline(bool prev, Vertex *Q, Vertex *P1, Vertex *P2, Vertex *P3) +{ + Point<2> p1t = *P1; + Point<2> p3t = *P3; + + auto sq = prev ? Q->spline : Q->prev->spline; + auto qt = sq->TangentPoint(); + if(P1->spline) p1t = P1->spline->TangentPoint(); + if(P2->spline) p3t = P2->spline->TangentPoint(); + + // Check using tangent directions first + double s1 = Area( qt, p1t, *P2 ); + double s2 = Area( qt, *P2 , p3t); + double s3 = Area( p1t, *P2, p3t); + + // tangents are facing in same direction + if(fabs(s1) < EPSILON) + { + if(P1->spline) + s1 = IsLeft(*P1->spline, *Q) ? 1 : -1; + else + s1 = Area( *Q, *P1, *P2 ); + } + + // tangents are facing in same direction + if(fabs(s2) < EPSILON) + { + if(P2->spline) + s2 = IsLeft(*P2->spline, *Q) ? 1 : -1; + else + s2 = Area( *Q, *P2, *P3 ); + } + + return oracle_decide(s1, s2, s3); +} + + +RelativePositionType oracle(bool prev, Vertex* P2) +{ + auto Q = prev ? P2->neighbour->prev : P2->neighbour->next; + auto sq = prev ? Q->spline : Q->prev->spline; + Vertex* P1 = P2->prev; + Vertex* P3 = P2->next; + + // is Q linked to P1 ? + if ( P1->is_intersection && (P1->neighbour == Q) ) + return(IS_P_m); + + // is Q linked to P2 ? + if ( P3->is_intersection && (P3->neighbour == Q) ) + return(IS_P_p); + + // no splines -> simple variant + if(!P1->spline && !P2->spline && !Q->spline) + return oracle_simple(*Q, *P1, *P2, *P3); + + Point<2> qt=*Q, p1t=*P1, p3t=*P3; + + // splines -> also consider tangent points + if( sq) qt = Q->spline->TangentPoint(); + if(P1->spline) p1t = P1->spline->TangentPoint(); + if(P2->spline) p3t = P2->spline->TangentPoint(); + + // only spline at Q + if(!P1->spline && !P2->spline && Q->spline) + return oracle_spline_q(*Q, qt, *P1, *P2, *P3); + + // only spline at P + if((P1->spline || !P2->spline) && !Q->spline) + return oracle_spline_p(*Q, *P1, p1t, *P2, *P3, p3t); + + // spline at Q and P1 or P2 + return oracle_spline(prev, Q, P1, P2, P3); +} + + +void LabelIntersections(Solid2d & sp, Solid2d & sq, Solid2d & sr, bool UNION) +{ + auto & PP = sp.polys; + auto & QQ = sq.polys; + auto & RR = sr.polys; + + // 1) initial classification + for (Loop& P : PP) + for (Vertex* I : P.Vertices(INTERSECTION)) + { + + // determine local configuration at this intersection vertex + // check positions of Q- and Q+ relative to (P-, I, P+) + RelativePositionType Q_m_type = oracle(true, I); + RelativePositionType Q_p_type = oracle(false, I); + + // check non-overlapping cases + if ((Q_m_type == LEFT && Q_p_type == RIGHT) || + (Q_m_type == RIGHT && Q_p_type == LEFT )) + { + I->label = CROSSING; + } + + if ((Q_m_type == LEFT && Q_p_type == LEFT ) || + (Q_m_type == RIGHT && Q_p_type == RIGHT)) + { + I->label = BOUNCING; + } + + // check overlapping cases + if ( ( (Q_p_type == IS_P_p) && (Q_m_type == RIGHT) ) || + ( (Q_m_type == IS_P_p) && (Q_p_type == RIGHT) ) ) + I->label = LEFT_ON; + + if ( ( (Q_p_type == IS_P_p) && (Q_m_type == LEFT) ) || + ( (Q_m_type == IS_P_p) && (Q_p_type == LEFT) ) ) + I->label = RIGHT_ON; + + if ( ( (Q_p_type == IS_P_p) && (Q_m_type == IS_P_m) ) || + ( (Q_m_type == IS_P_p) && (Q_p_type == IS_P_m) ) ) + I->label = ON_ON; + + if ( ( (Q_m_type == IS_P_m) && (Q_p_type == RIGHT) ) || + ( (Q_p_type == IS_P_m) && (Q_m_type == RIGHT) ) ) + I->label = ON_LEFT; + + if ( ( (Q_m_type == IS_P_m) && (Q_p_type == LEFT) ) || + ( (Q_p_type == IS_P_m) && (Q_m_type == LEFT) ) ) + I->label = ON_RIGHT; + } + + // 2) classify intersection chains + for (Loop& P : PP) + for (Vertex* I : P.Vertices(INTERSECTION)) + { + + // start of an intersection chain ? + if (I->label == LEFT_ON || + I->label == RIGHT_ON) + { + + // remember status of the first chain vertex and vertex itself + RelativePositionType x; + if (I->label == LEFT_ON) + x = LEFT; + else + x = RIGHT; + Vertex* X = I; + + // proceed to end of intersection chain and mark all visited vertices as NONE + do { + I->label = NONE; + I = I->next; + } while (I->label == ON_ON); + + RelativePositionType y; + if (I->label == ON_LEFT) + y = LEFT; + else + y = RIGHT; + + // determine type of intersection chain + IntersectionLabel chainType; + if (x != y) + chainType = DELAYED_CROSSING; + else + chainType = DELAYED_BOUNCING; + + // mark both ends of an intersection chain with chainType (i.e., as DELAYED_*) + X->label = chainType; + I->label = chainType; + } + } + + // 3) copy labels from P to Q + // loop over intersection vertices of P + for (Loop& P : PP) + for (Vertex* I : P.Vertices(INTERSECTION)) + I->neighbour->label = I->label; + + // 3.5) check for special cases + + set noIntersection[2]; + set identical[2]; + + for (int i=0; i<2; ++i) + { + Array* P_or_Q = &PP; // if i=0, then do it for P w.r.t. Q + Array* Q_or_P = &QQ; + + if (i==1) { // if i=1, then do it for Q w.r.t. P + P_or_Q = &QQ; + Q_or_P = &PP; + } + + // loop over all components of P (or Q) + for (Loop& P : *P_or_Q) + if (P.noCrossingVertex(UNION)) + { + // P_ has no crossing vertex (but may have bounces or delayed bounces, except for UNION), + // hence it does not intersect with Q_or_P + noIntersection[i].insert(&P); // remember component, and ignore it later in step 4 + + // is P identical to some component of and Q_or_P? + if (P.allOnOn()) + { + identical[i].insert(&P); // -> remember for further processing below + } + else + { + // is P inside Q_or_P? + bool isInside = false; + auto p = P.getNonIntersectionPoint(); + for (Loop& Q : *Q_or_P) + if ( Q.IsInside(p) ) + isInside = !isInside; + if (isInside ^ UNION) + RR.Append(P); // -> add P to the result + } + } + } + + // handle components of P that are identical to some component of Q + for (Loop* P : identical[0]) + { + // is P a hole? + bool P_isHole = false; + for (Loop& P_ : PP) + if ( ( P_.first.get() != P->first.get() ) && (P_.IsInside(*P->first)) ) + P_isHole = !P_isHole; + + for (Loop* Q : identical[1]) + for (Vertex* V : Q->Vertices(ALL)) + if (V == P->first->neighbour) { // found Q that matches P + // is Q a hole? + bool Q_isHole = false; + for (Loop& Q_ : QQ) + if ( ( Q_.first.get() != Q->first.get() ) && (Q_.IsInside(*Q->first)) ) + Q_isHole = !Q_isHole; + + // if P and Q are both holes or both are not holes + if (P_isHole == Q_isHole) + RR.Append(*P); // -> add P to the result + goto next_P; + } +next_P: ; + } + + // 4) set entry/exit flags + set split[2]; // split vertex candidates for P and Q + set crossing[2]; // CROSSING vertex candidates for P and Q + + for (int i=0; i<2; ++i) + { + Array* P_or_Q = &PP; // if i=0, then do it for P w.r.t. Q + Array* Q_or_P = &QQ; + + if (i==1) { // if i=1, then do it for Q w.r.t. P + P_or_Q = &QQ; + Q_or_P = &PP; + } + + // loop over all components of P (or Q) + for (Loop& P : *P_or_Q) + { + + // ignore P if it does not intersect with Q_or_P (detected in step 3.5 above) + if(noIntersection[i].find(&P) != noIntersection[i].end()) + continue; + + // start at a non-intersection vertex of P + Vertex* V = P.getNonIntersectionVertex(); + + // check if it is inside or outside Q (or P) + // and set ENTRY/EXIT status accordingly + EntryExitLabel status = ENTRY; + for (Loop& Q : *Q_or_P) + if (Q.IsInside(*V)) + ToggleLabel(status); + + // starting at V, loop over those vertices of P, that are either + // a crossing intersection or marked as ends of an intersection chain + bool first_chain_vertex = true; // needed for dealing with crossing chains + + for (Vertex* I : P.Vertices(INTERSECTION, V)) + { + // in the case of normal crossings, we... + if (I->label == CROSSING) + { + // mark vertex with current ENTRY/EXIT status + I->enex = status; + // toggle status from ENTRY to EXIT or vice versa + ToggleLabel(status); + } + + // identify split vertex candidates (INTERIOR bouncing vertices) + if ( (I->label == BOUNCING) && ((status == EXIT) ^ UNION) ) + split[i].insert(I); + + // + // in the case of a delayed crossing chain, we + // mark both end points of the chain with the current ENTRY/EXIT status, + // toggling the status only at the end last chain vertex, + // and, in case of a delayed EXIT crossing, the first vertex + // or, in case of a delayed ENTRY crossing, the last vertex, + // of the chain as CROSSING + // + if (I->label == DELAYED_CROSSING) + { + // mark vertex with current ENTRY/EXIT status + I->enex = status; + + if (first_chain_vertex) { // are we at the first vertex of a delayed crossing chain? + if ((status == EXIT) ^ UNION) + I->label = CROSSING; // mark first vertex as CROSSING + first_chain_vertex = false; + } + else { // here we are at the last vertex of a delayed crossing chain + if ((status == ENTRY) ^ UNION) + I->label = CROSSING; // mark last vertex as CROSSING + first_chain_vertex = true; + + // toggle status from ENTRY to EXIT or vice versa (only for last chain vertex) + ToggleLabel(status); + } + } + + // + // in the case of a delayed bouncing chain, we + // mark both end points of the chain with the current ENTRY/EXIT status + // toggling the status at both end points of the chain, + // and, in case of a delayed INTERIOR bouncing, both end points + // of the chain as CROSSING candidates + // + if (I->label == DELAYED_BOUNCING) + { + // mark vertex with current ENTRY/EXIT status + I->enex = status; + + if (first_chain_vertex) { // are we at the first vertex of a delayed crossing chain? + if ((status == EXIT) ^ UNION) + crossing[i].insert(I); // mark first EXIT vertex as CROSSING candidate + first_chain_vertex = false; + } + else { // here we are at the last vertex of a delayed crossing chain + if ((status == ENTRY) ^ UNION) + crossing[i].insert(I); // mark last ENTRY vertex as CROSSING candidate + first_chain_vertex = true; + + } + // toggle status from ENTRY to EXIT or vice versa (for first AND last chain vertex) + ToggleLabel(status); + } + } + } + } + + // 5) handle split vertex pairs + // loop over P's split candidates + for (Vertex* I_P : split[0]) + { + Vertex* I_Q = I_P->neighbour; + + // check if the neighbour on Q is also a split candidate + if (split[1].find(I_Q) != split[1].end()) + { + // compute areas to compare local orientation + Point<2> p_prev = *I_P->prev; + if(I_P->prev->spline) + p_prev = I_P->prev->spline->TangentPoint(); + + Point<2> p_next = *I_P->next; + if(I_P->spline) + p_next = I_P->spline->TangentPoint(); + + Point<2> q_prev = *I_Q->prev; + if(I_Q->prev->spline) + q_prev = I_Q->prev->spline->TangentPoint(); + + Point<2> q_next = *I_Q->next; + if(I_Q->spline) + q_next = I_Q->spline->TangentPoint(); + + + double sP = Area( p_prev, *I_P, p_next ); + double sQ = Area( q_prev, *I_Q, q_next ); + + // add duplicate vertices to P and Q + auto V_P = I_P->Insert(*I_P, I_P->lam); + V_P->spline = I_P->spline; + V_P->pinfo = I_P->pinfo; + auto V_Q = I_Q->Insert(*I_Q, I_Q->lam); + V_Q->spline = I_Q->spline; + V_Q->pinfo = I_Q->pinfo; + + // link vertices correctly + if (sP*sQ > 0) { // same local orientation + I_P->Link( V_Q); + I_Q->Link( V_P); + } + else { // different local orientation + V_P->Link( V_Q); + } + + // mark all four vertices correctly + if (!UNION) + { + I_P->enex = EXIT; + V_P->enex = ENTRY; + I_Q->enex = EXIT; + V_Q->enex = ENTRY; + } + else + { + I_P->enex = ENTRY; + V_P->enex = EXIT; + I_Q->enex = ENTRY; + V_Q->enex = EXIT; + } + + I_P->label = CROSSING; + V_P->label = CROSSING; + I_Q->label = CROSSING; + V_Q->label = CROSSING; + } + } + + // 6) handle CROSSING vertex candidates + // loop over P's CROSSING candidates + for (Vertex* I_P : crossing[0]) + { + Vertex* I_Q = I_P->neighbour; + + // check if the neighbour on Q is also a CROSSING candidate + if (crossing[1].find(I_Q) != crossing[1].end()) + { + // mark CROSSING candidate pair as such + I_P->label = CROSSING; + I_Q->label = CROSSING; + } + } +} + +void CreateResult(Solid2d & sp, Solid2d & sr, bool UNION) +{ + auto & PP = sp.polys; + auto & RR = sr.polys; + // + // for all crossing vertices + // + // NOTE: all crossing vertices that are visited while contructing a + // component of the result polygon are marked as "not intersection", + // so that they cannot serve as start vertex of another component + // + + for (Loop& P : PP) + { + for (Vertex* I : P.Vertices(CROSSING_INTERSECTION)) + { + Loop R; // result polygon component + + Vertex* V = I; // start traversal at I + V->is_intersection = false; // mark visited vertices + + do { + EntryExitLabel status = V->enex; + ToggleLabel(status); + while ( !(V->enex == status)) // ... we arrive at a vertex with opposite entry/exit flag, or + { + auto & vnew = R.AppendVertex(*V); + if ((status == EXIT) ^ UNION) + { + vnew.info = V->info; + vnew.pinfo = V->pinfo; + if(V->spline) + vnew.spline = *V->spline; + else + vnew.spline = nullopt; + V = V->next; // move forward from an ENTRY vertex to the next EXIT vertex + V->is_intersection = false; // mark visited vertices + } + else + { + V = V->prev; // move backward from an EXIT vertex to the next ENTRY vertex + if(V->spline) + { + auto & s = *V->spline; + vnew.spline = Spline{s.EndPI(), s.TangentPoint(), s.StartPI(), s.GetWeight()}; + } + else + vnew.spline = nullopt; + vnew.info = V->info; + vnew.pinfo = V->pinfo; + V->is_intersection = false; // mark visited vertices + } + if(V == I) + break; + } + + if (V != I) + { + V = V->neighbour; // switch from P to Q or vice versa + V->is_intersection = false; // mark visited vertices + } + } while (V != I); // the result polygon component is complete, + // if we are back to the initial vertex I + RR.Append(R); + } + } +} + +// Check if vertex v is not necessary (i.e. is on the line v->prev, v->next and has same info as v->prev and no pinfo +bool canRemoveVertex( Vertex * v ) +{ + return false; + if(v->spline) + return false; + if(v->pinfo.name != POINT_NAME_DEFAULT) + return false; + if(v->pinfo.maxh != MAXH_DEFAULT) + return false; + + if(v->info.bc != v->prev->info.bc || v->info.maxh != v->prev->info.maxh ) + return false; + + if(fabs(Area(*v->prev,*v,*v->next)) >= EPSILON) + return false; + + return true; +} + +void CleanUpResult(Solid2d & sr) +{ + auto & RR = sr.polys; + for (Loop& R : RR) + { + while ( (R.first.get() != NULL) && canRemoveVertex(R.first.get())) + R.Remove(R.first.get()); + + if (R.first.get() != NULL) + for (Vertex* V : R.Vertices(ALL)) + if (canRemoveVertex(V)) + R.Remove(V); + } + for (int i = RR.Size()-1; i>=0; i--) + if(RR[i].Size()==0) + RR.RemoveElement(i); +} + +Loop RectanglePoly(double x0, double x1, double y0, double y1, string bc) +{ + Loop r; + r.Append( {x0, y0} ); + r.Append( {x1, y0} ); + r.Append( {x1, y1} ); + r.Append( {x0, y1} ); + r.SetBC(bc); + return r; +} + +Solid2d Rectangle(Point<2> p0, Point<2> p1, string name, string bc) +{ + using P = Point<2>; + return { {p0, P{p1[0],p0[1]}, p1, P{p0[0],p1[1]}}, name, bc }; +} + +Solid2d Circle(Point<2> center, double r, string name, string bc) +{ + double x = center[0]; + double y = center[1]; + using P = Point<2>; + + Point<2> p[] = + { + {x+r, y+0}, + {x+0, y+r}, + {x-r, y+0}, + {x+0, y-r}, + }; + + EdgeInfo cp[] = + { + P{x+r, y+r}, + P{x-r, y+r}, + P{x-r, y-r}, + P{x+r, y-r} + }; + + return Solid2d( { p[0], cp[0], p[1], cp[1], p[2], cp[2], p[3], cp[3] }, name, bc ); +} + +void AddIntersectionPoints ( Solid2d & s1, Solid2d & s2 ) +{ + ComputeIntersections(s1, s2); + RemoveDuplicates(s1); + RemoveDuplicates(s2); +} + +void AddIntersectionPoints ( Loop & l1, Loop & l2 ) +{ + ComputeIntersections(l1, l2); + RemoveDuplicates(l1); + RemoveDuplicates(l2); +} + + +Solid2d ClipSolids ( const Solid2d & s1, const Solid2d & s2, char op) +{ + return ClipSolids(Solid2d{s1}, Solid2d{s2}, op); +} + +Solid2d ClipSolids ( const Solid2d & s1, Solid2d && s2, char op) +{ + return ClipSolids(Solid2d{s1}, std::move(s2), op); +} + +Solid2d ClipSolids ( Solid2d && s1, const Solid2d & s2, char op) +{ + return ClipSolids(std::move(s1), Solid2d{s2}, op); +} + +Solid2d ClipSolids ( Solid2d && s1, Solid2d && s2, char op) +{ + static Timer tall("ClipSolids"); RegionTimer rtall(tall); + static Timer t0("copy"); + static Timer t02("tree"); + static Timer t03("search intersections"); + static Timer t01("prepare"); + static Timer t1("intersection"); + static Timer t2("label"); + static Timer t3("cut"); + static Timer t4("cleanup"); + static Timer t6("trivial union"); + + bool intersect = (op=='*' || op=='-'); + + Solid2d res; + res.name = s1.name; + + t0.Start(); + // Try to quickly handle parts of both solids that cannot intersect with the other one + int n1 = s1.polys.Size(); + int n2 = s2.polys.Size(); + Array res_polys(n1+n2); + res_polys.SetSize(0); + + t02.Start(); + auto s1_box = s1.GetBoundingBox(); + netgen::BoxTree <2, int> tree1(s1_box); + + for(auto li : IntRange(n1)) + { + auto box = s1.polys[li].GetBoundingBox(); + tree1.Insert(box, li); + } + + auto s2_box = s2.GetBoundingBox(); + netgen::BoxTree <2, int> tree2(s2.GetBoundingBox()); + + for(auto li : IntRange(n2)) + { + auto box = s2.polys[li].GetBoundingBox(); + tree2.Insert(box, li); + } + t02.Stop(); + + t03.Start(); + + for(auto li : IntRange(n1)) + { + bool have_intersections = false; + auto & poly = s1.polys[li]; + auto box = poly.GetBoundingBox(); + tree2.GetFirstIntersecting(box.PMin(), box.PMax(), [&] (int li2) + { + return have_intersections = true; + }); + if(!have_intersections) + { + if(op=='+' || op=='-') + res_polys.Append(std::move(poly)); + else + poly.Clear(); + } + } + t03.Stop(); + + for(auto li: IntRange(n1)) + while(s1.polys.Size()>li && s1.polys[li].Size()==0) + s1.polys.DeleteElement(li); + + t03.Start(); + for(auto li : IntRange(n2)) + { + bool have_intersections = false; + auto & poly = s2.polys[li]; + auto box = poly.GetBoundingBox(); + tree1.GetFirstIntersecting(box.PMin(), box.PMax(), [&] (int li2) + { + return have_intersections = true; + }); + if(!have_intersections) + { + if(op=='+') + res_polys.Append(std::move(poly)); + else + poly.Clear(); + } + } + t03.Stop(); + + for(auto li: IntRange(n2)) + while(s2.polys.Size()>li && s2.polys[li].Size()==0) + s2.polys.DeleteElement(li); + + t0.Stop(); + + if(s1.polys.Size()==0 || s2.polys.Size()==0) + { + res.polys = std::move(res_polys); + return res; + } + + t01.Start(); + + if(op=='-') + { + // take complement of s2 by adding loop around everything + auto box = s1_box; + box.Add(s2_box.PMin()); + box.Add(s2_box.PMax()); + box.Increase(2); + auto pmin = box.PMin(); + auto pmax = box.PMax(); + s2.Append(RectanglePoly(pmin[0], pmax[0], pmin[1], pmax[1], "JUST_FOR_CLIPPING")); + } + + + for(auto & poly : s1.polys) + for(auto v : poly.Vertices(ALL)) + { + v->is_source = true; + v->neighbour = nullptr; + v->lam = -1.0; + v->is_intersection = false; + v->label = NONE; + v->enex = NEITHER; + } + + for(auto & poly : s2.polys) + for(auto v : poly.Vertices(ALL)) + { + v->is_source = true; + v->neighbour = nullptr; + v->lam = -1.0; + v->is_intersection = false; + v->label = NONE; + v->enex = NEITHER; + } + + t01.Stop(); + + t1.Start(); + ComputeIntersections(s1, s2); + t1.Stop(); + + t2.Start(); + LabelIntersections(s1, s2, res, !intersect); + t2.Stop(); + + t3.Start(); + CreateResult(s1, res, !intersect); + t3.Stop(); + + t4.Start(); + CleanUpResult(res); + RemoveDuplicates(res); + t4.Stop(); + + res.polys.Append(std::move(res_polys)); + + return std::move(res); +} + +Vertex* Loop :: getNonIntersectionVertex() + { + for (Vertex* v : Vertices(ALL)) + if (!v->is_intersection) + return(v); + + // no non-intersection vertex found -> generate and return temporary vertex + for (Vertex* v : Vertices(ALL)) + // make sure that edge from V to V->next is not collinear with other polygon + if ( (v->next->neighbour != v->neighbour->prev) && (v->next->neighbour != v->neighbour->next) ) + { + // add edge midpoint as temporary vertex + if(v->spline) + { + auto p = v->spline->GetPoint(0.5); + auto s = *v->spline; + v->spline = Split(s, 0, 0.5); + auto vnew = v->Insert(p); + vnew->info = v->info; + vnew->spline = Split(s, 0.5, 1.0); + return vnew; + } + else + { + auto p = Center(*v, *v->next); + auto vnew = v->Insert(p); + vnew->info = v->info; + return vnew; + } + } + return(NULL); + } + +bool Loop :: IsInside( Point<2> r ) const +{ + int w = 0; + for(auto e : Edges(ALL)) + { + int w_simple = CalcSide(*e.v0, *e.v1, r); + if(!e.v0->spline) + w += w_simple; + else + { + auto s = *e.v0->spline; + auto s0 = s.StartPI(); + auto s1 = s.TangentPoint(); + auto s2 = s.EndPI(); + if(!IsCloseToTrig( {s0, s1, s2} , r )) + w += w_simple; + else + { + // r close to spline, need exact test + // idea: compute weight, such that r lies on spline + // weight increases -> same side of spline as control point, simple test gives correct result + // weight decreases -> opposite side of spline as control point, adding control point to test polygon gives correct result + double old_weight = s.GetWeight(); + ComputeWeight( s, r ); + double new_weight = s.GetWeight(); + + if(new_weight >= old_weight) + w += w_simple; + else + w += CalcSide(s0, s1, r) + CalcSide(s1, s2, r); + } + } + } + return ( (w % 2) != 0 ); +} + + +Solid2d :: Solid2d(const Array, EdgeInfo, PointInfo>> & points, string name_, string bc) + : name(name_) +{ + Loop l; + for (auto & v : points) + { + if(auto point = std::get_if>(&v)) + l.Append(*point, true); + if(auto edge_info = std::get_if(&v)) + l.first->prev->info.Assign( *edge_info ); + if(auto point_info = std::get_if(&v)) + l.first->prev->pinfo.Assign(*point_info); + } + + for(auto v : l.Vertices(ALL)) + { + if(v->info.bc==BC_DEFAULT) + v->info.bc = bc; + + if(v->info.control_point) + v->spline = Spline(*v, *v->info.control_point, *v->next); + } + + polys.Append(l); +} + +Solid2d Solid2d :: operator+(const Solid2d & other) const +{ + static Timer t("Solid2d::operator+"); RegionTimer rt(t); + return ClipSolids(*this, other, '+'); +} + +Solid2d Solid2d :: operator*(const Solid2d & other) const +{ + static Timer t("Solid2d::operator*"); RegionTimer rt(t); + return ClipSolids(*this, other, '*'); +} + +Solid2d Solid2d :: operator-(const Solid2d & other) const +{ + static Timer t("Solid2d::operator-"); RegionTimer rt(t); + return ClipSolids(*this, other, '-'); +} + +Solid2d & Solid2d :: operator+=(const Solid2d & other) +{ + static Timer t("Solid2d::operator+="); RegionTimer rt(t); + *this = ClipSolids(std::move(*this), other, '+'); + return *this; +} + +Solid2d & Solid2d :: operator*=(const Solid2d & other) +{ + *this = ClipSolids(std::move(*this), other, '*'); + return *this; +} + +Solid2d & Solid2d :: operator-=(const Solid2d & other) +{ + *this = ClipSolids(std::move(*this), other, '-'); + return *this; +} + +Solid2d & Solid2d :: Move( Vec<2> v ) +{ + return Transform( [v](Point<2> p) -> Point<2> { return p+v; } ); +} + +Solid2d & Solid2d :: Scale( double s ) +{ + return Transform( [s](Point<2> p) -> Point<2> { return{p[0]*s, p[1]*s}; } ); +} + +Solid2d & Solid2d :: Scale( Vec<2> s ) +{ + return Transform( [s](Point<2> p) -> Point<2> { return{p[0]*s[0], p[1]*s[1]}; } ); +} + +Solid2d & Solid2d :: RotateRad( double ang, Point<2> center ) +{ + double sina = sin(ang); + double cosa = cos(ang); + Vec<2> c = { center[0], center[1] }; + return Transform( [c, sina, cosa](Point<2> p) -> Point<2> + { + p -= c; + double x = p[0]; + double y = p[1]; + p[0] = cosa*x-sina*y; + p[1] = sina*x+cosa*y; + p += c; + return p; + } ); +} + + +bool Solid2d :: IsInside( Point<2> r ) const +{ + int w = 0; + for(auto & poly : polys) + w += poly.IsInside(r); + return ( (w % 2) != 0 ); +} + +bool Loop :: IsLeftInside( const Vertex & p0 ) +{ + auto & p1 = *p0.next; + if(p0.spline) + { + auto s = *p0.spline; + auto v = s.GetTangent(0.5); + auto n = Vec<2>{-v[1], v[0]}; + auto q = s.GetPoint(0.5) + 1e-6*n; + return IsInside(q); + } + auto v = p1-p0; + auto n = Vec<2>{-v[1], v[0]}; + auto q = p0 + 0.5*v + 1e-6*n; + + return IsInside(q); +} + +bool Loop :: IsRightInside( const Vertex & p0 ) +{ + auto & p1 = *p0.next; + if(p0.spline) + { + auto s = *p0.spline; + auto v = s.GetTangent(0.5); + auto n = Vec<2>{v[1], -v[0]}; + auto q = s.GetPoint(0.5) + 1e-6*n; + return IsInside(q); + } + + auto v = p1-p0; + auto n = Vec<2>{v[1], -v[0]}; + auto q = p0 + 0.5*v + 1e-6*n; + return IsInside(q); +} + +bool Solid2d :: IsLeftInside( const Vertex & p0 ) +{ + auto & p1 = *p0.next; + if(p0.spline) + { + auto s = *p0.spline; + auto v = s.GetTangent(0.5); + auto n = Vec<2>{-v[1], v[0]}; + auto q = s.GetPoint(0.5) + 1e-6*n; + return IsInside(q); + } + auto v = p1-p0; + auto n = Vec<2>{-v[1], v[0]}; + auto q = p0 + 0.5*v + 1e-6*n; + + return IsInside(q); +} + +bool Solid2d :: IsRightInside( const Vertex & p0 ) +{ + auto & p1 = *p0.next; + if(p0.spline) + { + auto s = *p0.spline; + auto v = s.GetTangent(0.5); + auto n = Vec<2>{v[1], -v[0]}; + auto q = s.GetPoint(0.5) + 1e-6*n; + return IsInside(q); + } + + auto v = p1-p0; + auto n = Vec<2>{v[1], -v[0]}; + auto q = p0 + 0.5*v + 1e-6*n; + return IsInside(q); +} + +netgen::Box<2> Solid2d :: GetBoundingBox() const +{ + static Timer tall("Solid2d::GetBoundingBox"); RegionTimer rtall(tall); + netgen::Box<2> box(netgen::Box<2>::EMPTY_BOX); + for(auto & poly : polys) + { + auto pbox = poly.GetBoundingBox(); + box.Add(pbox.PMin()); + box.Add(pbox.PMax()); + } + + return box; +} + +shared_ptr CSG2d :: GenerateSplineGeometry() +{ + static Timer tall("CSG2d - GenerateSplineGeometry()"); + static Timer t_points("add points"); + static Timer t_segments_map("build segments map"); + static Timer t_is_inside("is inside check"); + static Timer t_segments("add segments"); + static Timer t_intersections("add intersections"); + static Timer t_segtree("seg trees"); + RegionTimer rt(tall); + + struct Seg + { + int p0; + int p1; + int left; + int right; + int bc; + int p2; + double weight; + double maxh = 1e99; + }; + + auto geo = std::make_shared(); + std::map, Seg> seg_map; + Array bcnames; + Array points; + + // Cut each solid with each other one to add all possible intersection points and have conforming edges from both domains + t_intersections.Start(); + + // First build bounding boxes for each solid to skip non-overlapping pairs + netgen::Box<2> box(netgen::Box<2>::EMPTY_BOX); + for(auto i : Range(solids)) + { + auto sbox = solids[i].GetBoundingBox(); + box.Add(sbox.PMin()); + box.Add(sbox.PMax()); + } + + netgen::BoxTree <2> solid_tree(box); + Array> loop_list; + + for(auto i : Range(solids)) + for(auto li : Range(solids[i].polys)) + { + solid_tree.Insert(solids[i].polys[li].GetBoundingBox(), loop_list.Size()); + loop_list.Append(INT<2>(i, li)); + } + + for(auto i1 : Range(solids)) + for(auto li1 : Range(solids[i1].polys)) + { + auto & poly1 = solids[i1].polys[li1]; + auto box = poly1.GetBoundingBox(); + solid_tree.GetFirstIntersecting(box.PMin(), box.PMax(), [&] (int ii) + { + auto i2 = loop_list[ii][0]; + auto li2 = loop_list[ii][1]; + if(i1 ptree(box); + + auto getPoint = [&](Point<2> p ) + { + int res = -1; + ptree.GetFirstIntersecting(p, p, [&] (int pi) + { + res = pi; + return true; + }); + return res; + }; + + t_points.Start(); + auto insertPoint = [&](const Vertex& p ) + { + int pi = getPoint(p); + if(pi==-1) + { + // not found -> insert to tree + netgen::GeomPoint<2> gp(p); + geo->geompoints.Append(gp); + pi = geo->geompoints.Size()-1; + ptree.Insert(p,p,geo->geompoints.Size()-1); + } + geo->geompoints[pi].hmax = min2(geo->geompoints[pi].hmax, p.pinfo.maxh); + if(p.pinfo.name != POINT_NAME_DEFAULT) + geo->geompoints[pi].name = p.pinfo.name; + }; + + for(auto & s : solids) + for(auto & poly : s.polys) + for(auto v : poly.Vertices(ALL)) + { + box.Add(*v); + insertPoint(*v); + if(v->spline) + insertPoint(v->spline->TangentPoint()); + } + t_points.Stop(); + + + // Generate segments from polygon edges and find left/right domain of each segment + t_segments_map.Start(); + int dom = 0; + int bc = 1; + for(auto & s : solids) + { + dom++; + bool is_solid_degenerated = true; // Don't create new domain for degenerated solids + for(auto & poly : s.polys) + { + bool first = true; + bool is_poly_left_inside = false; + bool is_poly_right_inside = false; + + for(auto v : poly.Vertices(ALL)) + { + auto & p0 = *v; + auto & p1 = *v->next; + + auto pi0 = getPoint(p0); + auto pi1 = getPoint(p1); + int pi2 = -1; + double weight = 0.0; + + if(v->spline) + { + auto p2 = v->spline->TangentPoint(); + pi2 = getPoint(p2); + weight = v->spline->GetWeight(); + } + + bool flip = false; + if(pi1SetMaterial(dom, s.name); + else + dom--; // degenerated solid, use same domain index again + } + t_segments_map.Stop(); + + for(auto bc : Range(bcnames)) + geo->SetBCName(bc+1, bcnames[bc]); + + t_segments.Start(); + for(auto const &m : seg_map) + { + auto ls = m.second; + netgen::SplineSegExt * seg; + if(ls.p2!=-1) + { + // spline segment + auto * seg3 = new netgen::SplineSeg3<2>( geo->GetPoint(ls.p0), geo->GetPoint(ls.p2), geo->GetPoint(ls.p1), ls.weight ); + seg = new netgen::SplineSegExt(*seg3); + } + else + { + // line segment + auto * l = new netgen::LineSeg<2>(geo->GetPoint(ls.p0), geo->GetPoint(ls.p1)); + seg = new netgen::SplineSegExt(*l); + } + + seg->leftdom = ls.left; + seg->rightdom = ls.right; + seg->bc = ls.bc; + seg->reffak = 1; + seg->copyfrom = -1; + seg->hmax = ls.maxh; + seg->hpref_left = 0.; + seg->hpref_right = 0.; + geo->AppendSegment(seg); + } + t_segments.Stop(); + return geo; +} + +shared_ptr CSG2d :: GenerateMesh(MeshingParameters & mp) +{ + auto geo = GenerateSplineGeometry(); + auto mesh = make_shared(); + geo->GenerateMesh(mesh, mp); + return mesh; +} + +} diff --git a/libsrc/geom2d/csg2d.hpp b/libsrc/geom2d/csg2d.hpp new file mode 100644 index 00000000..afc1e654 --- /dev/null +++ b/libsrc/geom2d/csg2d.hpp @@ -0,0 +1,741 @@ +#ifndef NETGEN_CSG2D_HPP_INCLUDED +#define NETGEN_CSG2D_HPP_INCLUDED + +#include + +#include "geometry2d.hpp" + +namespace netgen +{ + +using namespace ngcore; +using netgen::Point; +using netgen::Vec; +using Spline = SplineSeg3<2>; +using netgen::Box; + +inline double Area(const Point<2>& P, const Point<2>& Q, const Point<2>& R) +{ + return (Q[0]-P[0]) * (R[1]-P[1]) - (Q[1]-P[1]) * (R[0]-P[0]); +} + +// compute weight of spline such that p lies on it +void ComputeWeight( Spline & s, Point<2> p ); + +enum IntersectionType +{ // types of intersection (detected in the first phase) + NO_INTERSECTION = 0, + X_INTERSECTION, + T_INTERSECTION_Q, + T_INTERSECTION_P, + V_INTERSECTION, + X_OVERLAP, // Q0 -- P1 -- Q1 -- P0 (different direction) + T_OVERLAP_Q, // same direction or P inside Q + T_OVERLAP_P, // same direction or Q inside P + V_OVERLAP // one common point +}; + +enum IntersectionLabel +{ // for the classification of intersection vertices in the second phase + NONE, + CROSSING, + BOUNCING, + LEFT_ON, + RIGHT_ON, + ON_ON, + ON_LEFT, + ON_RIGHT, + DELAYED_CROSSING, + DELAYED_BOUNCING +}; + +enum EntryExitLabel +{ // for marking intersection vertices as "entry" or "exit" + EXIT, + ENTRY, + NEITHER +}; + +enum IteratorType +{ + SOURCE, + INTERSECTION, + CROSSING_INTERSECTION, + ALL +}; + +inline constexpr const double MAXH_DEFAULT{1e99}; +inline const string POINT_NAME_DEFAULT{""}; +inline const string BC_DEFAULT{""}; +inline const string MAT_DEFAULT{""}; + +struct EdgeInfo +{ + optional> control_point = nullopt; // for spline segments + double maxh = MAXH_DEFAULT; + string bc = BC_DEFAULT; + + EdgeInfo() = default; + EdgeInfo(Point<2> p) : control_point(p) {} + EdgeInfo(double h) : maxh(h) {} + EdgeInfo(string s) : bc(s) {} + EdgeInfo(optional> p, double h, string s) + : control_point(p), maxh(h), bc(s) + {} + + void Assign( EdgeInfo other ) + { + if(other.control_point != nullopt) + control_point = other.control_point; + if(other.bc != BC_DEFAULT) + bc = other.bc; + if(other.maxh != MAXH_DEFAULT) + maxh = min(maxh, other.maxh); + } +}; + +struct PointInfo +{ + double maxh = MAXH_DEFAULT; + string name = POINT_NAME_DEFAULT; + PointInfo() = default; + PointInfo(const PointInfo& other) = default; + PointInfo(double amaxh) : maxh(amaxh) {} + PointInfo(string aname) : name(aname) {} + PointInfo(double amaxh, string aname) : maxh(amaxh), name(aname) {} + + void Assign(const PointInfo& other) + { + maxh = min(maxh, other.maxh); + if(other.name != POINT_NAME_DEFAULT) + name = other.name; + } +}; + +struct Vertex : Point<2> +{ + Vertex (Point<2> p) : Point<2>(p) {} + Vertex (const Vertex & v) : Point<2>(v) + { + spline = v.spline; + info = v.info; + pinfo = v.pinfo; + is_source = true; + } + + Vertex * prev = nullptr; + Vertex * next = nullptr; + unique_ptr pnext = nullptr; + Vertex * neighbour = nullptr; // same vertex in other polygon (at intersections) + double lam = -1.0; + bool is_intersection = false; + bool is_source = false; + + IntersectionLabel label = NONE; // type of intersection vertex + EntryExitLabel enex = NEITHER; // entry/exit "flag" + + // In case the edge this - next is curved, store the spline information here + optional spline = nullopt; + EdgeInfo info; + PointInfo pinfo; + + DLL_HEADER Vertex * Insert(Point<2> p, double lam = -1.0); + + void Link( Vertex * v ) + { + neighbour = v; + v->neighbour = this; + is_intersection = true; + v->is_intersection = true; + } +}; + +struct VertexIterator +{ + struct iterator + { + iterator(Vertex* root, IteratorType IterType) : + root(root), V(NULL), iterType(IterType) + { + if (root == NULL) + return; + + if (nextVertex() == NULL) // no (source/intersection) vertex found + root = V = NULL; // -> mark iterator as "end" + } + + const iterator& operator++() + { + nextVertex(); + return *this; + } + + Vertex* operator*() + { + return V; + } + + bool operator!=(const iterator& other) const + { + return (root != other.root) || (V != other.V); + } + + private: + Vertex* root; + Vertex* V; + IteratorType iterType; + + // + // find the next vertex + // if iterType is ALL, then it is just the next vertex + // if iterType is SOURCE, then it is the next source vertex + // if iterType is INTERSECTION, then it is the next intersection vertex + // if iterType is CROSSING_INTERSECTION, then it is the next intersection vertex with CROSSING label + // + Vertex* nextVertex() + { + bool nextFound = false; + + if (V == NULL) + { // find first (source/intersection) vertex + V = root; + switch(iterType) + { + case ALL: + nextFound = true; + break; + case SOURCE: + if (V->is_source) + nextFound = true; + break; + case INTERSECTION: + if (V->is_intersection) + nextFound = true; + break; + case CROSSING_INTERSECTION: + if (V->is_intersection && (V->label == CROSSING)) + nextFound = true; + break; + } + } + + while (!nextFound) + { // find next (source/intersection) vertex + switch(iterType) + { + case ALL: + V = V->next; + break; + case SOURCE: + do { + V = V->next; + } while (!V->is_source && V != root); + break; + case INTERSECTION: + do { + V = V->next; + } while (!V->is_intersection && V != root); + break; + case CROSSING_INTERSECTION: + do { + V = V->next; + } while ( ( !V->is_intersection || (V->label != CROSSING) ) && V != root); + break; + } + + if (V == root) + { // back at the root vertex? + root = V = NULL; // -> mark iterator as "end" + return(V); + } + + switch(iterType) + { + case ALL: + nextFound = true; + break; + case SOURCE: + if (V->is_source) + nextFound = true; + break; + case INTERSECTION: + if (V->is_intersection) + nextFound = true; + break; + case CROSSING_INTERSECTION: + if (V->is_intersection && (V->label == CROSSING)) + nextFound = true; + break; + } + } + return(V); + } + }; + + public: + VertexIterator() : root(NULL) {}; + + iterator begin() { return iterator(root, iterType); } + iterator end() { return iterator(NULL, iterType); } + + Vertex* root; + IteratorType iterType; +}; + + +struct Edge +{ + Vertex * v0 = nullptr; + Vertex * v1 = nullptr; + + Edge (Vertex* v, Vertex* w) : v0(v), v1(w) { }; +}; + +struct EdgeIterator +{ + struct iterator + { + iterator(Vertex* root, IteratorType IterType) : + root(root), one(NULL), two(NULL), iterType(IterType) + { + if (root == NULL) + return; + + if (nextEdge() == NULL) // no source edge found + root = one = two = NULL; // -> mark iterator as "end" + } + + const iterator& operator++() { nextEdge(); return *this; } + + Edge operator*() + { + return Edge(one,two); + } + + bool operator!=(const iterator& other) const + { + return (root != other.root) || (one != other.one) || (two != other.two); + } + + private: + Vertex* root; + Vertex* one; + Vertex* two; + IteratorType iterType; + + // + // find the next vertex, starting at curr + // if iterType is ALL, then it is just the next vertex + // if iterType is SOURCE, then it is the next source vertex + // + Vertex* nextVertex(Vertex* curr) + { + if (curr == NULL) + return(NULL); + + switch(iterType) + { + case ALL: + curr = curr->next; + break; + + case SOURCE: + do { + curr = curr->next; + } while (!curr->is_source); + break; + default: + ; + } + + return(curr); + } + + // + // find the next edge + // + Vertex* nextEdge() + { + if (root == NULL) // empty polygon? + return (NULL); + + if (one == NULL) + { // find one (source) vertex + one = root; // note: root is always a (source) vertex + two = nextVertex(one); + if (two == one) // just one (source) vertex + return(NULL); // -> no (source) edges + return(one); + } + + if (two == root) + { // back at the root vertex? + root = one = two = NULL; // -> mark iterator as "end" + return(NULL); + } + + one = two; + two = nextVertex(one); + + return (one); + } + }; + + public: + EdgeIterator() : root(NULL) {}; + + iterator begin() { return iterator(root, iterType); } + iterator end() { return iterator(NULL, iterType); } + + Vertex* root; + IteratorType iterType; +}; + + +inline int CalcSide( const Point<2> & p0, const Point<2> & p1, const Point<2> & r ) +{ + if ( (p0[1] < r[1]) != (p1[1] < r[1]) ) + { + if (p0[0] >= r[0]) + { + if (p1[0] > r[0]) + return 2 * (p1[1] > p0[1]) - 1; + else + if ( (Area(p0,p1,r) > 0) == (p1[1] > p0[1]) ) + return 2 * (p1[1] > p0[1]) - 1; + } + else + { + if (p1[0] > r[0]) + if ( (Area(p0,p1,r) > 0) == (p1[1] > p0[1]) ) + return 2 * (p1[1] > p0[1]) - 1; + } + } + return 0; +} + +struct Loop +{ + unique_ptr first = nullptr; + unique_ptr> bbox = nullptr; + + Loop() = default; + + Loop(const Loop & p) + : first(nullptr) + { + for(auto v : p.Vertices(ALL)) + AppendVertex(*v); + } + + Loop(Loop && p) = default; + + Loop & operator=(Loop && p) = default; + + Loop & operator=(const Loop & p) + { + // static Timer t("Loop::operator="); RegionTimer rt(t); + first = nullptr; + if(p.first) + { + size_t n = p.Size(); + Array> new_verts(n); + { + size_t i = 0; + for(const auto v : p.Vertices(ALL)) + new_verts[i++] = make_unique(*v); + } + + for(auto i : IntRange(n-1)) + { + Vertex * v = new_verts[i].get(); + Vertex * vn = new_verts[i+1].get(); + v->next = vn; + vn->prev = v; + } + Vertex * vfirst = new_verts[0].get(); + Vertex * vlast = new_verts[n-1].get(); + vfirst->prev = vlast; + vlast->next = vfirst; + + for(auto i : IntRange(1,n)) + new_verts[n-1-i]->pnext = std::move(new_verts[n-i]); + + first = std::move(new_verts[0]); + } + bbox = nullptr; + return *this; + } + + void Clear() + { + first = nullptr; + } + + Vertex & AppendVertex(const Vertex & v) + { + auto & vnew = Append( static_cast>(v), true ); + vnew.info = v.info; + vnew.pinfo = v.pinfo; + if(v.spline) + vnew.spline = *v.spline; + if(bbox) + bbox->Add(v); + return vnew; + } + + Vertex & Append(Point<2> p, bool source = false) + { + Vertex * vnew; + if(first==nullptr) + { + first = make_unique(p); + first->next = first.get(); + first->prev = first.get(); + vnew = first.get(); + } + else + { + vnew = first->prev->Insert(p); + } + + vnew->is_source = source; + // cout << "size after " << Size() << endl; + if(bbox) + bbox->Add(p); + return *vnew; + } + + void Remove (Vertex* v) + { + v->prev->next = v->next; + v->next->prev = v->prev; + if(first.get() == v) + first = std::move(v->pnext); + else + v->prev->pnext = std::move(v->pnext); + bbox.reset(); + } + + bool IsInside( Point<2> r ) const; + bool IsLeftInside( const Vertex & p0 ); + bool IsRightInside( const Vertex & p0 ); + + EdgeIterator Edges(IteratorType iterType) const + { + EdgeIterator it; + it.iterType = iterType; + it.root = first.get(); + return it; + } + + VertexIterator Vertices(IteratorType iterType, Vertex* first_ = nullptr) const + { + VertexIterator it; + it.iterType = iterType; + it.root = (first_ == nullptr) ? first.get() : first_; + return it; + } + + // + // check, if all vertices have the ON_ON label + // + bool allOnOn() + { + for (Vertex* v : Vertices(ALL)) + if (v->label != ON_ON) + return(false); + return(true); + } + + // + // check, if the polygon does not contain any crossing intersection vertex + // or crossing intersection chain or (if we want to compute the union instead + // of the intersection) a bouncing vertex or a bouncing intersection chain + // + bool noCrossingVertex(bool union_case = false) + { + for (Vertex* v : Vertices(ALL)) + if (v->is_intersection) + { + if ( (v->label == CROSSING) || (v->label == DELAYED_CROSSING) ) + return(false); + + if (union_case && ( (v->label == BOUNCING) || (v->label == DELAYED_BOUNCING) ) ) + return(false); + } + return(true); + } + + // + // return a non-intersection point + // + Point<2> getNonIntersectionPoint() + { + for (Vertex* v : Vertices(ALL)) + if (!v->is_intersection) + return *v; + + // no non-intersection vertex found -> find suitable edge midpoint + for (Vertex* v : Vertices(ALL)) + // make sure that edge from V to V->next is not collinear with other polygon + if ( (v->next->neighbour != v->neighbour->prev) && (v->next->neighbour != v->neighbour->next) ) + // return edge midpoint + return Center(*v, *v->next); + throw Exception("no point found"); + } + + // + // return and insert a non-intersection vertex + // + Vertex* getNonIntersectionVertex(); + + void SetBC(string bc) + { + for(auto v : Vertices(ALL)) + v->info.bc = bc; + } + + size_t Size() const + { + if(first==nullptr) return 0; + + size_t cnt = 0; + + for(auto v : Vertices(ALL)) + cnt++; + + return cnt; + } + + const Box<2> & GetBoundingBox() + { + if(bbox==nullptr) + { + static Timer tall("Loop::GetBoundingBox"); RegionTimer rt(tall); + bbox = make_unique>(Box<2>::EMPTY_BOX); + for(auto v : Vertices(ALL)) + { + bbox->Add(*v); + if(v->spline) + bbox->Add(v->spline->TangentPoint()); + } + } + return *bbox; + } +}; + + +struct Solid2d +{ + Array polys; + + string name = MAT_DEFAULT; + + Solid2d() = default; + Solid2d(string name_) : name(name_) {} + DLL_HEADER Solid2d(const Array, EdgeInfo, PointInfo>> & points, string name_=MAT_DEFAULT, string bc_=BC_DEFAULT); + Solid2d(Solid2d && other) = default; + Solid2d(const Solid2d & other) = default; + + DLL_HEADER Solid2d operator+(const Solid2d & other) const; + DLL_HEADER Solid2d operator*(const Solid2d & other) const; + DLL_HEADER Solid2d operator-(const Solid2d & other) const; + + Solid2d& operator=(Solid2d && other) = default; + Solid2d& operator=(const Solid2d & other) = default; + DLL_HEADER Solid2d& operator+=(const Solid2d & other); + DLL_HEADER Solid2d& operator*=(const Solid2d & other); + DLL_HEADER Solid2d& operator-=(const Solid2d & other); + + void Append( const Loop & poly ) + { + polys.Append(poly); + } + + bool IsInside( Point<2> r ) const; + bool IsLeftInside( const Vertex & p0 ); + bool IsRightInside( const Vertex & p0 ); + + template + Solid2d & Transform( const TFunc & func ) + { + for(auto & poly : polys) + for(auto v : poly.Vertices(ALL)) + { + auto p = func(*v); + (*v)[0] = p[0]; + (*v)[1] = p[1]; + if(v->spline) + { + auto &s = *v->spline; + auto pmid = func(s.GetPoint(0.5)); + s = Spline(func(s.StartPI()), func(s.TangentPoint()), func(s.EndPI())); + ComputeWeight(s, pmid); + } + } + return *this; + } + + Solid2d & Move( Vec<2> v ); + Solid2d & Scale( double s ); + Solid2d & Scale( Vec<2> s ); + Solid2d & RotateRad( double ang, Point<2> center = {0,0} ); + Solid2d & RotateDeg( double ang, Point<2> center = {0,0} ) + { + return RotateRad( ang/180.*M_PI, center ); + } + + Solid2d & BC(string bc) + { + for(auto & p : polys) + for(auto v : p.Vertices(ALL)) + v->info.bc = bc; + return *this; + } + + Solid2d & Maxh(double maxh) + { + for(auto & p : polys) + for(auto v : p.Vertices(ALL)) + v->info.maxh = maxh; + return *this; + } + + Solid2d & Mat(string mat) + { + name = mat; + return *this; + } + + Box<2> GetBoundingBox() const; +}; + + +class CSG2d +{ + public: + Array solids; + + void Add ( Solid2d s ) + { + solids.Append(s); + } + + DLL_HEADER shared_ptr GenerateSplineGeometry(); + DLL_HEADER shared_ptr GenerateMesh(MeshingParameters & mp); +}; + +DLL_HEADER Solid2d Circle( Point<2> center, double r, string name="", string bc=""); +DLL_HEADER Solid2d Rectangle( Point<2> p0, Point<2> p1, string mat=MAT_DEFAULT, string bc=BC_DEFAULT ); + +DLL_HEADER void AddIntersectionPoints ( Solid2d & s1, Solid2d & s2 ); +DLL_HEADER Solid2d ClipSolids ( const Solid2d & s1, const Solid2d & s2, char op); +DLL_HEADER Solid2d ClipSolids ( const Solid2d & s1, Solid2d && s2, char op); +DLL_HEADER Solid2d ClipSolids ( Solid2d && s1, const Solid2d & s2, char op); +DLL_HEADER Solid2d ClipSolids ( Solid2d && s1, Solid2d && s2, char op); + +DLL_HEADER IntersectionType intersect(const Point<2> P1, const Point<2> P2, const Point<2> Q1, const Point<2> Q2, double& alpha, double& beta); + +} +#endif // NETGEN_CSG2D_HPP_INCLUDED diff --git a/libsrc/geom2d/genmesh2d.cpp b/libsrc/geom2d/genmesh2d.cpp index 09ef0260..2ba08b9c 100644 --- a/libsrc/geom2d/genmesh2d.cpp +++ b/libsrc/geom2d/genmesh2d.cpp @@ -18,29 +18,44 @@ namespace netgen { double fperel, oldf, f; - int n = 10000; - - NgArray > xi(n); - NgArray hi(n); - - for (int i = 0; i < n; i++) + int n = 1; + NgArray > xi; + NgArray hi; + + // do one extra step + int not_fine_enough = 2; + + while(not_fine_enough && n < 10000) + { + not_fine_enough--; + n*=4; + + xi.SetSize(n); + hi.SetSize(n); + + for (int i = 0; i < n; i++) { - xi[i] = spline.GetPoint ( (i+0.5) / n ); - hi[i] = mesh.GetH (Point<3> (xi[i](0), xi[i](1), 0)); + xi[i] = spline.GetPoint ( (i+0.5) / n ); + hi[i] = mesh.GetH (Point<3> (xi[i](0), xi[i](1), 0)); } - // limit slope - double gradh = min(1/elto0,mp.grading); - for (int i = 0; i < n-1; i++) + // limit slope + double gradh = min(1/elto0,mp.grading); + for (int i = 0; i < n-1; i++) { - double hnext = hi[i] + gradh * (xi[i+1]-xi[i]).Length(); - hi[i+1] = min(hi[i+1], hnext); + double hnext = hi[i] + gradh * (xi[i+1]-xi[i]).Length(); + if(hnext > 2*hi[i]) + not_fine_enough = 2; + hi[i+1] = min(hi[i+1], hnext); } - for (int i = n-1; i > 1; i--) + for (int i = n-1; i > 1; i--) { - double hnext = hi[i] + gradh * (xi[i-1]-xi[i]).Length(); - hi[i-1] = min(hi[i-1], hnext); + double hnext = hi[i] + gradh * (xi[i-1]-xi[i]).Length(); + if(hnext > 2*hi[i]) + not_fine_enough = 2; + hi[i-1] = min(hi[i-1], hnext); } + } points.SetSize (0); @@ -233,13 +248,22 @@ namespace netgen double hr = GetDomainMaxh (spline.rightdom); if (hr > 0) hcurve = min2 (hcurve, hr); - int np = 1000; - for (double t = 0.5/np; t < 1; t += 1.0/np) - { - Point<2> x = spline.GetPoint(t); - double hc = 1.0/mp.curvaturesafety / (1e-99+spline.CalcCurvature (t)); - mesh2d.RestrictLocalH (Point<3> (x(0), x(1), 0), min2(hc, hcurve)); - } + // skip curvature restrictions for straight lines + if(spline.MaxCurvature()==0) + { + mesh2d.RestrictLocalHLine (Point<3>(p1(0),p1(1),0), + Point<3>(p2(0),p2(1),0), hcurve); + } + else + { + int np = 1000; + for (double t = 0.5/np; t < 1; t += 1.0/np) + { + Point<2> x = spline.GetPoint(t); + double hc = 1.0/mp.curvaturesafety / (1e-99+spline.CalcCurvature (t)); + mesh2d.RestrictLocalH (Point<3> (x(0), x(1), 0), min2(hc, hcurve)); + } + } } for (auto mspnt : mp.meshsize_points) @@ -278,6 +302,11 @@ namespace netgen { npi = mesh2d.AddPoint (newp, layer); searchtree.Insert (newp, npi); + mesh2d.AddLockedPoint(npi); + Element0d el(npi, npi); + el.name = ""; + mesh2d.SetCD2Name(npi, ""); + mesh2d.pointelements.Append (el); } } } @@ -392,10 +421,16 @@ namespace netgen shared_ptr & mesh, MeshingParameters & mp) { + static Timer tall("MeshFromSpline2D"); RegionTimer rtall(tall); + static Timer t_h("SetH"); + static Timer t_tensor("tensor domain meshing"); + static Timer t_part_boundary("PartitionBoundary"); + static Timer t_hpref("mark hpref points"); PrintMessage (1, "Generate Mesh from spline geometry"); Box<2> bbox = geometry.GetBoundingBox (); + t_h.Start(); if (bbox.Diam() < mp.maxh) mp.maxh = bbox.Diam(); @@ -412,11 +447,14 @@ namespace netgen + t_part_boundary.Start(); geometry.PartitionBoundary (mp, mp.maxh, *mesh); + t_part_boundary.Stop(); PrintMessage (3, "Boundary mesh done, np = ", mesh->GetNP()); + t_hpref.Start(); // marks mesh points for hp-refinement for (int i = 0; i < geometry.GetNP(); i++) if (geometry.GetPoint(i).hpref) @@ -434,6 +472,7 @@ namespace netgen } (*mesh)[mpi].Singularity(geometry.GetPoint(i).hpref); } + t_hpref.Stop(); int maxdomnr = 0; @@ -443,6 +482,16 @@ namespace netgen if ( (*mesh)[si].domout > maxdomnr) maxdomnr = (*mesh)[si].domout; } + TableCreator dom2seg_creator(maxdomnr+1); + for ( ; !dom2seg_creator.Done(); dom2seg_creator++) + for (const Segment & seg : mesh->LineSegments()) + { + dom2seg_creator.Add (seg.domin, &seg); + if (seg.domin != seg.domout) + dom2seg_creator.Add (seg.domout, &seg); + } + auto dom2seg = dom2seg_creator.MoveTable(); + mesh->ClearFaceDescriptors(); for (int i = 1; i <= maxdomnr; i++) mesh->AddFaceDescriptor (FaceDescriptor (i, 0, 0, i)); @@ -458,10 +507,8 @@ namespace netgen for ( int sindex = 0; sindex < maxsegmentindex; sindex++ ) mesh->SetBCName ( sindex, geometry.GetBCName( sindex+1 ) ); - for (SegmentIndex si = 0; si < mesh->GetNSeg(); si++) - (*mesh)[si].SetBCName ( (*mesh).GetBCNamePtr( (*mesh)[si].si-1 ) ); - mesh->CalcLocalH(mp.grading); + t_h.Stop(); int bnp = mesh->GetNP(); // boundary points auto BndPntRange = mesh->Points().Range(); @@ -472,6 +519,7 @@ namespace netgen for (int domnr = 1; domnr <= maxdomnr; domnr++) if (geometry.GetDomainTensorMeshing (domnr)) { // tensor product mesh + RegionTimer rt(t_tensor); NgArray nextpi(bnp); NgArray si1(bnp), si2(bnp); @@ -527,11 +575,16 @@ namespace netgen for (PointIndex pix = nextpi[c1], ix = 0; pix != c2; pix = nextpi[pix], ix++) + { + Point<3> px = (*mesh)[pix]; for (PointIndex piy = nextpi[c2], iy = 0; piy != c3; piy = nextpi[piy], iy++) { - Point<3> p = (*mesh)[pix] + ( (*mesh)[piy] - (*mesh)[c2] ); - pts[(nex+1)*(iy+1) + ix+1] = mesh -> AddPoint (p , 1, FIXEDPOINT); + double lam = Dist((*mesh)[piy],(*mesh)[c2]) / Dist((*mesh)[c3],(*mesh)[c2]); + auto pix1 = pts[(nex+1)*ney+ix+1]; + auto pnew = px + lam*((*mesh)[pix1]-px); + pts[(nex+1)*(iy+1) + ix+1] = mesh -> AddPoint (pnew, 1, FIXEDPOINT); } + } for (int i = 0; i < ney; i++) for (int j = 0; j < nex; j++) @@ -545,13 +598,20 @@ namespace netgen mesh -> AddSurfaceElement (el); } + char* material; + geometry.GetMaterial(domnr, material); + if(material) + mesh->SetMaterial(domnr, material); } + static Timer t_domain("Mesh domain"); + static Timer t_points("Mesh domain - find points"); for (int domnr = 1; domnr <= maxdomnr; domnr++) { + RegionTimer rt(t_domain); if (geometry.GetDomainTensorMeshing (domnr)) continue; double h = mp.maxh; @@ -567,19 +627,43 @@ namespace netgen Meshing2 meshing (geometry, mp, Box<3> (pmin, pmax)); - NgArray compress(bnp); + NgArray compress(mesh->GetNP()); compress = -1; int cnt = 0; - for (PointIndex pi : BndPntRange) - if ( (*mesh)[pi].GetLayer() == geometry.GetDomainLayer(domnr)) - { - meshing.AddPoint ((*mesh)[pi], pi); - cnt++; - compress[pi] = cnt; - } + + t_points.Start(); + /* + for (SegmentIndex si = 0; si < mesh->GetNSeg(); si++) + { + const auto & s = (*mesh)[si]; + if ( s.domin==domnr || s.domout==domnr ) + { + for (auto pi : {s[0], s[1]}) + { + if(compress[pi]==-1) + { + meshing.AddPoint((*mesh)[pi], pi); + cnt++; + compress[pi] = cnt; + } + } + } + } + */ + for (const Segment * seg : dom2seg[domnr]) + if (seg->domin==domnr || seg->domout==domnr ) + for (auto pi : {(*seg)[0], (*seg)[1]}) + if (compress[pi]==-1) + { + meshing.AddPoint((*mesh)[pi], pi); + cnt++; + compress[pi] = cnt; + } + PointGeomInfo gi; gi.trignum = 1; + /* for (SegmentIndex si = 0; si < mesh->GetNSeg(); si++) { if ( (*mesh)[si].domin == domnr) @@ -593,13 +677,31 @@ namespace netgen compress[(*mesh)[si][0]], gi, gi); } } + */ - // not complete, use at own risk ... - // meshing.Delaunay(*mesh, domnr, mp); - mp.checkoverlap = 0; - auto res = meshing.GenerateMesh (*mesh, mp, h, domnr); - if (res != 0) - throw NgException("meshing failed"); + for (const Segment * seg : dom2seg[domnr]) + { + if (seg->domin == domnr) + meshing.AddBoundaryElement (compress[(*seg)[0]], + compress[(*seg)[1]], gi, gi); + + if (seg->domout == domnr) + meshing.AddBoundaryElement (compress[(*seg)[1]], + compress[(*seg)[0]], gi, gi); + } + + + t_points.Stop(); + + if(mp.delaunay2d && cnt>1) + meshing.Delaunay(*mesh, domnr, mp); + else + { + // mp.checkoverlap = 0; + auto res = meshing.GenerateMesh (*mesh, mp, h, domnr); + if (res != 0) + throw NgException("meshing failed"); + } for (SurfaceElementIndex sei = oldnf; sei < mesh->GetNSE(); sei++) (*mesh)[sei].SetIndex (domnr); @@ -611,6 +713,8 @@ namespace netgen mesh->SetMaterial (domnr, material); } + mesh->Compress(); + mp.quad = hquad; diff --git a/libsrc/geom2d/geometry2d.hpp b/libsrc/geom2d/geometry2d.hpp index ff4c459c..daab8d12 100644 --- a/libsrc/geom2d/geometry2d.hpp +++ b/libsrc/geom2d/geometry2d.hpp @@ -9,6 +9,7 @@ #include #include +#include // #include "../gprim/spline.hpp" @@ -133,7 +134,7 @@ namespace netgen NgArray materials; NgArray maxh; NgArray quadmeshing; - NgArray tensormeshing; + Array tensormeshing; NgArray layer; NgArray bcnames; double elto0 = 1.0; @@ -203,8 +204,8 @@ namespace netgen size_t GetNDomains() const { return materials.Size(); } - void GetMaterial (int domnr, char* & material ); - void SetMaterial (int domnr, const string & material); + DLL_HEADER void GetMaterial (int domnr, char* & material ); + DLL_HEADER void SetMaterial (int domnr, const string & material); double GetDomainMaxh ( const int domnr ); void SetDomainMaxh ( const int domnr, double maxh ); @@ -214,11 +215,36 @@ namespace netgen if ( quadmeshing.Size() ) return quadmeshing[domnr-1]; else return false; } + void SetDomainQuadMeshing ( int domnr, bool quad_meshing ) + { + auto oldsize = quadmeshing.Size(); + + if ( oldsize=domnr ) return tensormeshing[domnr-1]; else return false; } + void SetDomainTensorMeshing ( int domnr, bool tm ) + { + if ( tensormeshing.Size() #include +#include using namespace netgen; +using namespace pybind11::literals; namespace netgen { @@ -15,7 +17,7 @@ namespace netgen } -DLL_HEADER void ExportGeom2d(py::module &m) +NGCORE_API_EXPORT void ExportGeom2d(py::module &m) { py::class_> (m, "Spline", "Spline of a SplineGeometry object") @@ -65,24 +67,45 @@ DLL_HEADER void ExportGeom2d(py::module &m) optional> bc, optional copy, double maxh, double hpref, double hprefleft, double hprefright) { - auto segtype = py::cast(segment[0]); - SplineSegExt * seg; - if (segtype == "line") + if(py::isinstance(segment[0])) { - LineSeg<2> * l = new LineSeg<2>(self.GetPoint(py::cast(segment[1])), - self.GetPoint(py::cast(segment[2]))); - seg = new SplineSegExt(*l); - } - else if (segtype == "spline3") - { - SplineSeg3<2> * seg3 = new SplineSeg3<2>(self.GetPoint(py::cast(segment[1])), - self.GetPoint(py::cast(segment[2])), - self.GetPoint(py::cast(segment[3]))); - seg = new SplineSegExt(*seg3); + auto segtype = py::cast(segment[0]); + + if (segtype == "line") + { + LineSeg<2> * l = new LineSeg<2>(self.GetPoint(py::cast(segment[1])), + self.GetPoint(py::cast(segment[2]))); + seg = new SplineSegExt(*l); + } + else if (segtype == "spline3") + { + SplineSeg3<2> * seg3 = new SplineSeg3<2>(self.GetPoint(py::cast(segment[1])), + self.GetPoint(py::cast(segment[2])), + self.GetPoint(py::cast(segment[3]))); + seg = new SplineSegExt(*seg3); + } + else + throw Exception("Appended segment is not a line or a spline3"); } else - throw Exception("Appended segment is not a line or a spline3"); + { + if(py::len(segment) == 2) + { + auto l = new LineSeg<2>(self.GetPoint(py::cast(segment[0])), + self.GetPoint(py::cast(segment[1]))); + seg = new SplineSegExt(*l); + } + else if(py::len(segment) == 3) + { + SplineSeg3<2> * seg3 = new SplineSeg3<2>(self.GetPoint(py::cast(segment[0])), + self.GetPoint(py::cast(segment[1])), + self.GetPoint(py::cast(segment[2]))); + seg = new SplineSegExt(*seg3); + } + else + throw Exception("Appended segment must either have 2 or 3 points"); + } seg->leftdom = leftdomain; seg->rightdom = rightdomain; seg->hmax = maxh; @@ -265,6 +288,7 @@ DLL_HEADER void ExportGeom2d(py::module &m) { double len = self.splines[i]->Length(); int n = floor(len/(0.05*min(xdist,ydist))); + n = max(3, n); lst.push_back(self.splines[i]->StartPI()); for (int j = 1; j < n; j++){ lst.push_back(self.splines[i]->GetPoint(j*1./n)); @@ -371,9 +395,8 @@ DLL_HEADER void ExportGeom2d(py::module &m) }) ) - // If we change to c++17 this can become optional .def("GenerateMesh", [](shared_ptr self, - MeshingParameters* pars, py::kwargs kwargs) + optional pars, py::kwargs kwargs) { MeshingParameters mp; if(pars) mp = *pars; @@ -389,11 +412,89 @@ DLL_HEADER void ExportGeom2d(py::module &m) if(result != 0) throw Exception("Meshing failed!"); return mesh; - }, py::arg("mp") = nullptr, + }, py::arg("mp") = nullopt, + py::call_guard(), + meshingparameter_description.c_str()) + .def("_SetDomainTensorMeshing", &SplineGeometry2d::SetDomainTensorMeshing) + ; + + py::class_(m, "Solid2d") + .def(py::init<>()) + .def(py::init, EdgeInfo, PointInfo>>, std::string, std::string>(), py::arg("points"), py::arg("mat")=MAT_DEFAULT, py::arg("bc")=BC_DEFAULT) + + .def(py::self+py::self) + .def(py::self-py::self) + .def(py::self*py::self) + .def(py::self+=py::self) + .def(py::self-=py::self) + .def(py::self*=py::self) + + .def("Mat", &Solid2d::Mat) + .def("BC", &Solid2d::BC) + .def("Maxh", &Solid2d::Maxh) + + .def("Copy", [](Solid2d & self) -> Solid2d { return self; }) + .def("Move", &Solid2d::Move) + .def("Scale", static_cast(&Solid2d::Scale)) + .def("Scale", static_cast)>(&Solid2d::Scale)) + .def("Rotate", &Solid2d::RotateDeg, py::arg("angle"), py::arg("center")=Point<2>{0,0}) + ; + + + m.def("Rectangle", [](Point<2> p0, Point<2> p1, string mat, string bc, optional bottom, optional right, optional top, optional left) -> Solid2d + { + using P = Point<2>; + return { { + p0, EdgeInfo{bottom ? *bottom : bc}, + P{p1[0],p0[1]}, EdgeInfo {right ? *right : bc}, + p1, EdgeInfo {top ? *top : bc}, + P{p0[0],p1[1]}, EdgeInfo {left ? *left : bc}, + }, mat}; + }, + "pmin"_a, "pmax"_a, "mat"_a=MAT_DEFAULT, "bc"_a=BC_DEFAULT, + "bottom"_a=nullopt, "right"_a=nullopt, "top"_a=nullopt, "left"_a=nullopt + ); + m.def("Circle", Circle, py::arg("center"), py::arg("radius"), py::arg("mat")=MAT_DEFAULT, py::arg("bc")=BC_DEFAULT); + + py::class_(m, "CSG2d") + .def(py::init<>()) + .def("GenerateSplineGeometry", &CSG2d::GenerateSplineGeometry) + .def("Add", &CSG2d::Add) + .def("GenerateMesh", [](CSG2d & self, optional pars, py::kwargs kwargs) + { + MeshingParameters mp; + if(pars) mp = *pars; + { + py::gil_scoped_acquire aq; + CreateMPfromKwargs(mp, kwargs); + } + auto mesh = make_shared(); + auto geo = self.GenerateSplineGeometry(); + mesh->SetGeometry(geo); + SetGlobalMesh (mesh); + ng_geometry = geo; + auto result = geo->GenerateMesh(mesh, mp); + if(result != 0) + throw Exception("Meshing failed!"); + return mesh; + }, py::arg("mp") = nullopt, py::call_guard(), meshingparameter_description.c_str()) ; - + + py::class_(m, "EdgeInfo") + .def(py::init<>()) + .def(py::init&>(), py::arg("control_point")) + .def(py::init(), py::arg("maxh")) + .def(py::init(), py::arg("bc")) + .def(py::init>, double, string>(), py::arg("control_point")=nullopt, py::arg("maxh")=MAXH_DEFAULT, py::arg("bc")=BC_DEFAULT) + ; + py::class_(m, "PointInfo") + .def(py::init<>()) + .def(py::init(), "maxh"_a) + .def(py::init(), "name"_a) + .def(py::init(), "maxh"_a, "name"_a) + ; } PYBIND11_MODULE(libgeom2d, m) { diff --git a/libsrc/gprim/adtree.hpp b/libsrc/gprim/adtree.hpp index bff29157..f05f7170 100644 --- a/libsrc/gprim/adtree.hpp +++ b/libsrc/gprim/adtree.hpp @@ -1099,6 +1099,284 @@ public: // auto & Tree() { return *tree; }; // }; + template + class DelaunayTree + { + public: + // Number of entries per leaf + static constexpr int N = 100; + + struct Node; + + struct Leaf + { + Point<2*dim, TSCAL> p[N]; + T index[N]; + int n_elements; + int nr; + + Leaf() : n_elements(0) + { } + + + void Add( Array &leaves, Array &leaf_index, const Point<2*dim> &ap, T aindex ) + { + p[n_elements] = ap; + index[n_elements] = aindex; + n_elements++; + if(leaf_index.Size() leaves; + Array leaf_index; + + Point global_min, global_max; + double tol; + size_t n_leaves; + size_t n_nodes; + BlockAllocator ball_nodes; + BlockAllocator ball_leaves; + + public: + + DelaunayTree (const Point & pmin, const Point & pmax) + : global_min(pmin), global_max(pmax), n_leaves(1), n_nodes(1), ball_nodes(sizeof(Node)), ball_leaves(sizeof(Leaf)) + { + root.leaf = (Leaf*) ball_leaves.Alloc(); new (root.leaf) Leaf(); + root.leaf->nr = 0; + leaves.Append(root.leaf); + root.level = 0; + tol = 1e-7 * Dist(pmax, pmin); + } + + DelaunayTree (const Box & box) + : DelaunayTree(box.PMin(), box.PMax()) + { } + + size_t GetNLeaves() + { + return n_leaves; + } + + size_t GetNNodes() + { + return n_nodes; + } + + template + void GetFirstIntersecting (const Point & pmin, const Point & pmax, + TFunc func=[](auto pi){return false;}) const + { + // static Timer timer("DelaunayTree::GetIntersecting"); RegionTimer rt(timer); + // static Timer timer1("DelaunayTree::GetIntersecting-LinearSearch"); + ArrayMem stack; + ArrayMem dir_stack; + + + Point<2*dim> tpmin, tpmax; + + for (size_t i : IntRange(dim)) + { + tpmin(i) = global_min(i); + tpmax(i) = pmax(i)+tol; + + tpmin(i+dim) = pmin(i)-tol; + tpmax(i+dim) = global_max(i); + } + + stack.SetSize(0); + stack.Append(&root); + dir_stack.SetSize(0); + dir_stack.Append(0); + + while(stack.Size()) + { + const Node *node = stack.Last(); + stack.DeleteLast(); + + int dir = dir_stack.Last(); + dir_stack.DeleteLast(); + + if(Leaf *leaf = node->GetLeaf()) + { + // RegionTimer rt1(timer1); + for (auto i : IntRange(leaf->n_elements)) + { + bool intersect = true; + const auto p = leaf->p[i]; + + for (int d = 0; d < dim; d++) + if (p[d] > tpmax[d]) + intersect = false; + for (int d = dim; d < 2*dim; d++) + if (p[d] < tpmin[d]) + intersect = false; + if(intersect) + if(func(leaf->index[i])) return; + } + } + else + { + int newdir = dir+1; + if(newdir==2*dim) newdir = 0; + if (tpmin[dir] <= node->sep) + { + stack.Append(node->children[0]); + dir_stack.Append(newdir); + } + if (tpmax[dir] >= node->sep) + { + stack.Append(node->children[1]); + dir_stack.Append(newdir); + } + } + } + } + + void GetIntersecting (const Point & pmin, const Point & pmax, + NgArray & pis) const + { + pis.SetSize(0); + GetFirstIntersecting(pmin, pmax, [&pis](auto pi) { pis.Append(pi); return false;}); + } + + void Insert (const Box & box, T pi) + { + Insert (box.PMin(), box.PMax(), pi); + } + + void Insert (const Point & pmin, const Point & pmax, T pi) + { + // static Timer timer("DelaunayTree::Insert"); RegionTimer rt(timer); + int dir = 0; + Point<2*dim> p; + for (auto i : IntRange(dim)) + { + p(i) = pmin[i]; + p(i+dim) = pmax[i]; + } + + Node * node = &root; + Leaf * leaf = node->GetLeaf(); + + // search correct leaf to add point + while(!leaf) + { + node = p[dir] < node->sep ? node->children[0] : node->children[1]; + dir++; + if(dir==2*dim) dir = 0; + leaf = node->GetLeaf(); + } + + // add point to leaf + if(leaf->n_elements < N) + leaf->Add(leaves, leaf_index, p,pi); + else // assume leaf->n_elements == N + { + // add two new nodes and one new leaf + int n_elements = leaf->n_elements; + ArrayMem coords(n_elements); + ArrayMem order(n_elements); + + // separate points in two halves, first sort all coordinates in direction dir + for (auto i : IntRange(n_elements)) + { + order[i] = i; + coords[i] = leaf->p[i][dir]; + } + + QuickSortI(coords, order); + int isplit = N/2; + Leaf *leaf1 = (Leaf*) ball_leaves.Alloc(); new (leaf1) Leaf(); + Leaf *leaf2 = (Leaf*) ball_leaves.Alloc(); new (leaf2) Leaf(); + + leaf1->nr = leaf->nr; + leaf2->nr = leaves.Size(); + leaves.Append(leaf2); + leaves[leaf1->nr] = leaf1; + + for (auto i : order.Range(isplit)) + leaf1->Add(leaves, leaf_index, leaf->p[i], leaf->index[i] ); + for (auto i : order.Range(isplit, N)) + leaf2->Add(leaves, leaf_index, leaf->p[i], leaf->index[i] ); + + Node *node1 = (Node*) ball_nodes.Alloc(); new (node1) Node(); + node1->leaf = leaf1; + node1->level = node->level+1; + + Node *node2 = (Node*) ball_nodes.Alloc(); new (node2) Node(); + node2->leaf = leaf2; + node2->level = node->level+1; + + node->children[0] = node1; + node->children[1] = node2; + node->sep = 0.5 * (leaf->p[order[isplit-1]][dir] + leaf->p[order[isplit]][dir]); + + // add new point to one of the new leaves + if (p[dir] < node->sep) + leaf1->Add( leaves, leaf_index, p, pi ); + else + leaf2->Add( leaves, leaf_index, p, pi ); + + ball_leaves.Free(leaf); + n_leaves++; + n_nodes+=2; + } + } + + void DeleteElement (T pi) + { + // static Timer timer("DelaunayTree::DeleteElement"); RegionTimer rt(timer); + Leaf *leaf = leaves[leaf_index[pi]]; + leaf_index[pi] = -1; + auto & n_elements = leaf->n_elements; + auto & index = leaf->index; + auto & p = leaf->p; + + for (auto i : IntRange(n_elements)) + { + if(index[i] == pi) + { + n_elements--; + if(i!=n_elements) + { + index[i] = index[n_elements]; + p[i] = p[n_elements]; + } + return; + } + } + } + }; } #endif diff --git a/libsrc/gprim/geom2d.cpp b/libsrc/gprim/geom2d.cpp index 34263348..f50131aa 100644 --- a/libsrc/gprim/geom2d.cpp +++ b/libsrc/gprim/geom2d.cpp @@ -3,10 +3,6 @@ #include #include -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - namespace netgen { @@ -251,239 +247,4 @@ int PTRIANGLE2D :: IsIn (const Point2d & p) const } #endif - - - - - - -Polygon2d :: Polygon2d () -{ - ; -} - -Polygon2d :: ~Polygon2d () -{ - ; -} - -void Polygon2d :: AddPoint (const Point2d & p) -{ - points.Append(p); -} - - -double Polygon2d :: HArea () const -{ - int i; - double ar = 0; - for (i = 1; i <= points.Size(); i++) - { - const Point2d & p1 = points.Get(i); - const Point2d & p2 = points.Get(i%points.Size()+1); - ar += - (p2.X()-p1.X()) * p1.Y() - - (p2.Y()-p1.Y()) * p1.X(); - } - return ar/2; - /* - CURSOR c; - double ar = 0; - Point2d * p1, * p2, p0 = Point2d(0, 0); - Vec2d v1, v2 = Vec2d(1, 0); - - p2 = points[points.Last()]; - for (c = points.First(); c != points.Head(); c++) - { - p1 = p2; - p2 = points[c]; - ar += Cross ( (*p2-*p1), (*p1 - p0)); - } - return ar / 2; - */ -} - - -int Polygon2d :: IsOn (const Point2d & p) const -{ - int i; - for (i = 1; i <= points.Size(); i++) - { - const Point2d & p1 = points.Get(i); - const Point2d & p2 = points.Get(i%points.Size()+1); - if (IsOnLine (Line2d(p1, p2), p)) return 1; - } - return 0; - /* - CURSOR c; - Point2d * p1, * p2; - - p2 = points[points.Last()]; - for (c = points.First(); c != points.Head(); c++) - { - p1 = p2; - p2 = points[c]; - if (IsOnLine (Line2d(*p1, *p2), p)) return 1; - } - return 0; - */ -} - - -int Polygon2d :: IsIn (const Point2d & p) const -{ - int i; - double sum = 0, ang; - for (i = 1; i <= points.Size(); i++) - { - const Point2d & p1 = points.Get(i); - const Point2d & p2 = points.Get(i%points.Size()+1); - ang = Angle ( (p1 - p), (p2 - p) ); - if (ang > M_PI) ang -= 2 * M_PI; - sum += ang; - } - return fabs(sum) > M_PI; - /* - CURSOR c; - Point2d * p1, * p2; - double sum = 0, ang; - - p2 = points[points.Last()]; - for (c = points.First(); c != points.Head(); c++) - { - p1 = p2; - p2 = points[c]; - ang = Angle ( (*p1 - p), (*p2 - p) ); - if (ang > M_PI) ang -= 2 * M_PI; - sum += ang; - } - - return fabs(sum) > M_PI; - */ -} - -int Polygon2d :: IsConvex () const - { - /* - Point2d *p, *pold, *pnew; - char cw; - CURSOR c; - - if (points.Length() < 3) return 0; - - c = points.Last(); - p = points[c]; - c--; - pold = points[c]; - pnew = points[points.First()]; - cw = ::CW (*pold, *p, *pnew); - - for (c = points.First(); c != points.Head(); c++) - { - pnew = points[c]; - if (cw != ::CW (*pold, *p, *pnew)) - return 0; - pold = p; - p = pnew; - } - */ - return 0; - } - - -int Polygon2d :: IsStarPoint (const Point2d & p) const - { - /* - Point2d *pnew, *pold; - char cw; - CURSOR c; - - if (points.Length() < 3) return 0; - - pold = points[points.Last()]; - pnew = points[points.First()]; - - cw = ::CW (p, *pold, *pnew); - - for (c = points.First(); c != points.Head(); c++) - { - pnew = points[c]; - if (cw != ::CW (p, *pold, *pnew)) - return 0; - pold = pnew; - } - return 1; - */ - return 0; - } - - -Point2d Polygon2d :: Center () const - { - /* - double ai, a = 0, x = 0, y = 0; - Point2d * p, *p2; - Point2d p0 = Point2d(0, 0); - CURSOR c; - - p2 = points[points.Last()]; - - for (c = points.First(); c != points.Head(); c++) - { - p = points[c]; - ai = Cross (*p2 - p0, *p - p0); - x += ai / 3 * (p2->X() + p->X()); - y += ai / 3 * (p2->Y() + p->Y()); - a+= ai; - p2 = p; - } - if (a != 0) - return Point2d (x / a, y / a); - else - return Point2d (0, 0); - */ - return Point2d (0, 0); - } - - - -Point2d Polygon2d :: EqualAreaPoint () const - { - /* - double a11 = 0, a12 = 0, a21= 0, a22 = 0; - double b1 = 0, b2 = 0, dx, dy; - double det; - Point2d * p, *p2; - CURSOR c; - - p = points[points.Last()]; - - for (c = points.First(); c != points.Head(); c++) - { - p2 = p; - p = points[c]; - - dx = p->X() - p2->X(); - dy = p->Y() - p2->Y(); - - a11 += sqr (dy); - a12 -= dx * dy; - a21 -= dx * dy; - a22 += sqr (dx); - b1 -= dy * (p->X() * p2->Y() - p2->X() * p->Y()); - b2 -= dx * (p->Y() * p2->X() - p2->Y() * p->X()); - } - - det = a11 * a22 - a21 * a12; - - if (det != 0) - return Point2d ( (b1 * a22 - b2 * a12) / det, - (a11 * b2 - a21 * b1) / det); - else - return Point2d (0, 0); -*/ - return Point2d (0, 0); - } - - } diff --git a/libsrc/gprim/geom2d.hpp b/libsrc/gprim/geom2d.hpp index b86c74b7..890a456b 100644 --- a/libsrc/gprim/geom2d.hpp +++ b/libsrc/gprim/geom2d.hpp @@ -609,40 +609,6 @@ namespace netgen #endif - - class Polygon2d - { - protected: - NgArray points; - - public: - Polygon2d (); - ~Polygon2d (); - - void AddPoint (const Point2d & p); - int GetNP() const { return points.Size(); } - void GetPoint (int i, Point2d & p) const - { p = points.Get(i); } - void GetLine (int i, Point2d & p1, Point2d & p2) const - { p1 = points.Get(i); p2 = points.Get(i%points.Size()+1); } - - double Area () const { return fabs (HArea()); } - int CW () const { return HArea() > 0; } - int CCW () const { return HArea() < 0; } - - int IsOn (const Point2d & p) const; - int IsIn (const Point2d & p) const; - - int IsConvex () const; - - int IsStarPoint (const Point2d & p) const; - Point2d Center() const; - Point2d EqualAreaPoint () const; - private: - double HArea () const; - }; - - /** Cheap approximation to atan2. A monotone function of atan2(x,y) is computed. */ diff --git a/libsrc/gprim/geomfuncs.cpp b/libsrc/gprim/geomfuncs.cpp index b2ac8382..6608c072 100644 --- a/libsrc/gprim/geomfuncs.cpp +++ b/libsrc/gprim/geomfuncs.cpp @@ -83,7 +83,7 @@ double Det (const Mat<3,3> & m) void EigenValues (const Mat<3,3> & m, Vec<3> & ev) { - const double pi = 3.141592; + const double pi = M_PI; double a, b, c, d; double p, q; double arg; diff --git a/libsrc/gprim/geomfuncs.hpp b/libsrc/gprim/geomfuncs.hpp index ea0070df..c85e0dc6 100644 --- a/libsrc/gprim/geomfuncs.hpp +++ b/libsrc/gprim/geomfuncs.hpp @@ -142,10 +142,24 @@ namespace netgen inline void CalcInverse (const Mat<2,3> & m, Mat<3,2> & inv) { + Vec<3> a0 = m.Row(0); + Vec<3> a1 = m.Row(1); + Vec<3> n = Cross(a0, a1); + Vec<3> d0 = Cross(a1, n); + Vec<3> d1 = Cross(a0, n); + double s0 = 1.0/(a0*d0); + double s1 = 1.0/(a1*d1); + for (int i = 0; i < 3; i++) + { + inv(i,0) = s0*d0(i); + inv(i,1) = s1*d1(i); + } + /* Mat<2,2> a = m * Trans (m); Mat<2,2> ainv; CalcInverse (a, ainv); inv = Trans (m) * ainv; + */ } void CalcInverse (const Mat<3,2> & m, Mat<2,3> & inv); @@ -166,6 +180,31 @@ namespace netgen void EigenValues (const Mat<3,3> & m, Vec<3> & ev); void EigenValues (const Mat<2,2> & m, Vec<3> & ev); + + template + Vec<3,T> StableSolve (Mat<2,3,T> mat, Vec<2,T> rhs) + { + Vec<3> a0 = mat.Row(0); + Vec<3> a1 = mat.Row(1); + /* + Vec<3> d = Cross ( Cross (a0, a1), a0); + + double alpha = rhs(0) / a0.Length2(); + double beta = (rhs(1)-alpha* (a0*a1)) / (d*a1); + return alpha * a0 + beta * d; + */ + Vec<3> n = Cross(a0, a1); + Vec<3> d0 = Cross(a1, n); + Vec<3> d1 = Cross(a0, n); + double alpha = rhs(0) / (a0*d0); + double beta = rhs(1) / (a1*d1); + return alpha * d0 + beta * d1; + } + + + + + } #endif diff --git a/libsrc/gprim/geomobjects.hpp b/libsrc/gprim/geomobjects.hpp index fe8171bf..765565f0 100644 --- a/libsrc/gprim/geomobjects.hpp +++ b/libsrc/gprim/geomobjects.hpp @@ -384,8 +384,16 @@ namespace netgen bool IsIn (const Point & p) const { for (int i = 0; i < D; i++) - if (p(i) < pmin(i) || p(i) > pmax(i)) return 0; - return 1; + if (p(i) < pmin(i) || p(i) > pmax(i)) return false; + return true; + } + + // is point in eps-increased box + bool IsIn (const Point & p, double eps) const + { + for (int i = 0; i < D; i++) + if (p(i) < pmin(i)-eps || p(i) > pmax(i)+eps) return false; + return true; } @@ -478,7 +486,7 @@ namespace netgen }; -#ifdef PARALLEL +#ifdef PARALLEL_OLD template <> inline MPI_Datatype MyGetMPIType > () { diff --git a/libsrc/gprim/geomtest3d.cpp b/libsrc/gprim/geomtest3d.cpp index 0c117a10..5c3a827a 100644 --- a/libsrc/gprim/geomtest3d.cpp +++ b/libsrc/gprim/geomtest3d.cpp @@ -1035,6 +1035,39 @@ double MinDistLP2 (const Point3d & lp1, const Point3d & lp2, const Point3d & p) } +double MinDistLP2 (const Point3d & lp1, const Point3d & lp2, const Point3d & p, double & lam) +{ + Vec3d v(lp1, lp2); + Vec3d vlp(lp1, p); + + // dist(lam) = \| vlp \|^2 - 2 lam (v1p, v) + lam^2 \| v \|^2 + + // lam = (v * vlp) / (v * v); + // if (lam < 0) lam = 0; + // if (lam > 1) lam = 1; + + double num = v*vlp; + double den = v*v; + + if (num <= 0) + { + lam = 0.0; + return Dist2 (lp1, p); + } + + if (num >= den) + { + lam = 1.0; + return Dist2 (lp2, p); + } + + lam = num/den; + if (den > 0) + return vlp.Length2() - num * num /den; + else + return vlp.Length2(); +} + double MinDistTP2 (const Point3d & tp1, const Point3d & tp2, const Point3d & tp3, const Point3d & p) @@ -1102,7 +1135,7 @@ double MinDistTP2 (const Point3d & tp1, const Point3d & tp2, // 0 checks !!! double MinDistLL2 (const Point3d & l1p1, const Point3d & l1p2, - const Point3d & l2p1, const Point3d & l2p2) + const Point3d & l2p1, const Point3d & l2p2, double & lam1, double & lam2 ) { // dist(lam1,lam2) = \| l2p1+lam2v2 - (l1p1+lam1 v1) \| // min ! @@ -1112,7 +1145,7 @@ double MinDistLL2 (const Point3d & l1p1, const Point3d & l1p2, Vec3d v2 (l2p1, l2p2); double a11, a12, a22, rs1, rs2; - double lam1, lam2, det; + double det; a11 = v1*v1; a12 = -(v1*v2); @@ -1138,14 +1171,27 @@ double MinDistLL2 (const Point3d & l1p1, const Point3d & l1p2, } double minv, hv; - minv = MinDistLP2 (l1p1, l1p2, l2p1); - hv = MinDistLP2 (l1p1, l1p2, l2p2); - if (hv < minv) minv = hv; + minv = MinDistLP2 (l1p1, l1p2, l2p1, lam1); + lam2 = 0.; + hv = MinDistLP2 (l1p1, l1p2, l2p2, lam1); + if (hv < minv) + { + lam2 = 1.; + minv = hv; + } - hv = MinDistLP2 (l2p1, l2p2, l1p1); - if (hv < minv) minv = hv; - hv = MinDistLP2 (l2p1, l2p2, l1p2); - if (hv < minv) minv = hv; + hv = MinDistLP2 (l2p1, l2p2, l1p1, lam2); + if (hv < minv) + { + lam1 = 0.; + minv = hv; + } + hv = MinDistLP2 (l2p1, l2p2, l1p2, lam2); + if (hv < minv) + { + lam1 = 1.; + minv = hv; + } return minv; } diff --git a/libsrc/gprim/geomtest3d.hpp b/libsrc/gprim/geomtest3d.hpp index a8bb0266..b26c9b41 100644 --- a/libsrc/gprim/geomtest3d.hpp +++ b/libsrc/gprim/geomtest3d.hpp @@ -75,7 +75,7 @@ extern double MinDistLP2 (const Point2d & lp1, const Point2d & lp2, const Point2 extern double MinDistLP2 (const Point3d & lp1, const Point3d & lp2, const Point3d & p); /// Minimal distance of point p to the triangle segment [tp1,tp2,pt3] -extern double MinDistTP2 (const Point3d & tp1, const Point3d & tp2, +DLL_HEADER double MinDistTP2 (const Point3d & tp1, const Point3d & tp2, const Point3d & tp3, const Point3d & p); inline double MinDistTP2 (const Point<2> & tp1, const Point<2> & tp2, @@ -91,6 +91,9 @@ extern double MinDistTP2 (const Point3d & tp1, const Point3d & tp2, extern double MinDistLL2 (const Point3d & l1p1, const Point3d & l1p2, const Point3d & l2p1, const Point3d & l2p2); +extern double MinDistLL2 (const Point3d & l1p1, const Point3d & l1p2, + const Point3d & l2p1, const Point3d & l2p2, double & lam1, double & lam2 ); + } #endif diff --git a/libsrc/gprim/spline.cpp b/libsrc/gprim/spline.cpp index a2778d91..4a17da71 100644 --- a/libsrc/gprim/spline.cpp +++ b/libsrc/gprim/spline.cpp @@ -98,6 +98,16 @@ namespace netgen proj_latest_t = 0.5; } + template + SplineSeg3 :: SplineSeg3 (const GeomPoint & ap1, + const GeomPoint & ap2, + const GeomPoint & ap3, + double aweight) + : p1(ap1), p2(ap2), p3(ap3), weight(aweight) + { + proj_latest_t = 0.5; + } + template Point SplineSeg3 :: GetPoint (double t) const { diff --git a/libsrc/gprim/spline.hpp b/libsrc/gprim/spline.hpp index 6549587d..94a96f60 100644 --- a/libsrc/gprim/spline.hpp +++ b/libsrc/gprim/spline.hpp @@ -185,6 +185,10 @@ namespace netgen SplineSeg3 (const GeomPoint & ap1, const GeomPoint & ap2, const GeomPoint & ap3); + SplineSeg3 (const GeomPoint & ap1, + const GeomPoint & ap2, + const GeomPoint & ap3, + double aweight); // default constructor for archive SplineSeg3() {} /// @@ -192,9 +196,13 @@ namespace netgen { ar & p1 & p2 & p3 & weight & proj_latest_t; } - virtual Point GetPoint (double t) const; /// - virtual Vec GetTangent (const double t) const; + double GetWeight () const { return weight; } + void SetWeight (double w) { weight = w; } + /// + DLL_HEADER virtual Point GetPoint (double t) const; + /// + DLL_HEADER virtual Vec GetTangent (const double t) const; DLL_HEADER virtual void GetDerivatives (const double t, @@ -202,12 +210,12 @@ namespace netgen Vec & first, Vec & second) const; /// - virtual const GeomPoint & StartPI () const { return p1; }; + DLL_HEADER virtual const GeomPoint & StartPI () const { return p1; }; /// - virtual const GeomPoint & EndPI () const { return p3; } + DLL_HEADER virtual const GeomPoint & EndPI () const { return p3; } /// - virtual void GetCoeff (Vector & coeffs) const; - virtual void GetCoeff (Vector & coeffs, Point p0) const; + DLL_HEADER virtual void GetCoeff (Vector & coeffs) const; + DLL_HEADER virtual void GetCoeff (Vector & coeffs, Point p0) const; virtual string GetType(void) const {return "spline3";} diff --git a/libsrc/include/incopengl.hpp b/libsrc/include/incopengl.hpp index 8cff36af..1f42f12f 100644 --- a/libsrc/include/incopengl.hpp +++ b/libsrc/include/incopengl.hpp @@ -2,9 +2,10 @@ #define INCOPENGL_HPP___ #define GL_GLEXT_PROTOTYPES +#include #include -# if defined(TOGL_AGL) || defined(TOGL_AGL_CLASSIC) || defined(TOGL_NSOPENGL) +# ifdef __APPLE__ #define GL_SILENCE_DEPRECATION #define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED # include diff --git a/libsrc/include/mydefs.hpp b/libsrc/include/mydefs.hpp index f2368ce7..41369e6a 100644 --- a/libsrc/include/mydefs.hpp +++ b/libsrc/include/mydefs.hpp @@ -11,22 +11,15 @@ defines for graphics, testmodes, ... */ +#include #define PACKAGE_VERSION "6.2-dev" // #define DEBUG -#ifdef WIN32 - #if NGINTERFACE_EXPORTS || NGLIB_EXPORTS || nglib_EXPORTS - #define DLL_HEADER __declspec(dllexport) - #else - #define DLL_HEADER __declspec(dllimport) - #endif +#if defined(NGINTERFACE_EXPORTS) || ( defined(WIN32) && (defined(NGLIB_EXPORTS) || defined(nglib_EXPORTS)) ) + #define DLL_HEADER NGCORE_API_EXPORT #else - #if __GNUC__ >= 4 - #define DLL_HEADER __attribute__ ((visibility ("default"))) - #else - #define DLL_HEADER - #endif + #define DLL_HEADER NGCORE_API_IMPORT #endif diff --git a/libsrc/include/nginterface.h b/libsrc/include/nginterface.h index c09f18f0..cea9558b 100644 --- a/libsrc/include/nginterface.h +++ b/libsrc/include/nginterface.h @@ -11,22 +11,13 @@ /* Date: 20. Nov. 99 */ /**************************************************************************/ -#include +#include "mydefs.hpp" /* Application program interface to Netgen */ -#ifndef DLL_HEADER - #if NGINTERFACE_EXPORTS || NGLIB_EXPORTS || nglib_EXPORTS - #define DLL_HEADER NGCORE_API_EXPORT - #else - #define DLL_HEADER NGCORE_API_IMPORT - #endif -#endif - - // max number of nodes per element #define NG_ELEMENT_MAXPOINTS 20 diff --git a/libsrc/include/nginterface_v2.hpp b/libsrc/include/nginterface_v2.hpp index 081422d3..3cf00fc8 100644 --- a/libsrc/include/nginterface_v2.hpp +++ b/libsrc/include/nginterface_v2.hpp @@ -8,7 +8,7 @@ /* Date: May 09 */ /**************************************************************************/ -#include +#include "mydefs.hpp" /* C++ interface to Netgen @@ -276,7 +276,7 @@ namespace netgen void UpdateTopology (); void DoArchive (Archive & archive); - NgMPI_Comm GetCommunicator() const; + const NgMPI_Comm & GetCommunicator() const; virtual ~Ngx_Mesh(); @@ -343,8 +343,11 @@ namespace netgen void SetRefinementFlag (size_t elnr, bool flag); void Curve (int order); + int GetCurveOrder (); - void Refine (NG_REFINEMENT_TYPE reftype, + void EnableTable (string name, bool set); + + void Refine (NG_REFINEMENT_TYPE reftype, bool onlyonce, void (*taskmanager)(function) = &DummyTaskManager2, void (*tracer)(string, bool) = &DummyTracer2); @@ -354,6 +357,10 @@ namespace netgen int GetParentElement (int ei) const; int GetParentSElement (int ei) const; + bool HasParentEdges() const; + tuple> GetParentEdges (int enr) const; + tuple> GetParentFaces (int fnr) const; + int GetNIdentifications() const; int GetIdentificationType(int idnr) const; Ng_Buffer GetPeriodicVertices(int idnr) const; @@ -367,8 +374,9 @@ namespace netgen // for MPI-parallel - std::tuple GetDistantProcs (int nodetype, int locnum) const; - + FlatArray GetDistantProcs (int nodetype, int locnum) const; + size_t GetGlobalVertexNum (int locnum) const; + shared_ptr GetMesh () const { return mesh; } shared_ptr SelectMesh () const; inline auto GetTimeStamp() const; diff --git a/libsrc/include/nginterface_v2_impl.hpp b/libsrc/include/nginterface_v2_impl.hpp index 6d6d1b85..cd9a6c4b 100644 --- a/libsrc/include/nginterface_v2_impl.hpp +++ b/libsrc/include/nginterface_v2_impl.hpp @@ -111,7 +111,13 @@ NGX_INLINE DLL_HEADER Ng_Element Ngx_Mesh :: GetElement<1> (size_t nr) const ret.faces.num = 0; ret.faces.ptr = NULL; - if (mesh->GetDimension() == 2) + if (mesh->GetDimension() == 3) + { + ret.facets.num = 0; + ret.facets.base = 0; + ret.facets.ptr = nullptr; + } + else if (mesh->GetDimension() == 2) { ret.facets.num = 1; ret.facets.base = 0; @@ -330,6 +336,20 @@ NGX_INLINE void Ngx_Mesh :: GetParentNodes (int ni, int * parents) const parents[0] = parents[1] = -1; } +inline bool Ngx_Mesh :: HasParentEdges() const +{ + return mesh->GetTopology().HasParentEdges(); +} + +inline tuple> Ngx_Mesh :: GetParentEdges (int enr) const +{ + return mesh->GetTopology().GetParentEdges(enr); +} + +inline tuple> Ngx_Mesh :: GetParentFaces (int fnr) const +{ + return mesh->GetTopology().GetParentFaces(fnr); +} inline auto Ngx_Mesh :: GetTimeStamp() const { return mesh->GetTimeStamp(); } diff --git a/libsrc/interface/CMakeLists.txt b/libsrc/interface/CMakeLists.txt index bc1bc2f3..47661a0c 100644 --- a/libsrc/interface/CMakeLists.txt +++ b/libsrc/interface/CMakeLists.txt @@ -4,10 +4,10 @@ add_library(interface ${NG_LIB_TYPE} read_fnf_mesh.cpp readtetmesh.cpp readuser.cpp writeabaqus.cpp writediffpack.cpp writedolfin.cpp writeelmer.cpp writefeap.cpp writefluent.cpp writegmsh.cpp writejcm.cpp writepermas.cpp writetecplot.cpp writetet.cpp writetochnog.cpp writeuser.cpp - wuchemnitz.cpp writegmsh2.cpp writeOpenFOAM15x.cpp + wuchemnitz.cpp writegmsh2.cpp writeOpenFOAM15x.cpp rw_cgns.cpp ) -target_link_libraries(interface mesh csg geom2d stl visual) +target_link_libraries(interface PUBLIC mesh csg geom2d stl visual PRIVATE netgen_cgns) if(NOT WIN32) install( TARGETS interface ${NG_INSTALL_DIR}) diff --git a/libsrc/interface/nginterface.cpp b/libsrc/interface/nginterface.cpp index 7c647119..c6bdd3fc 100644 --- a/libsrc/interface/nginterface.cpp +++ b/libsrc/interface/nginterface.cpp @@ -560,7 +560,7 @@ char * Ng_GetSurfaceElementBCName (int ei) if ( mesh->GetDimension() == 3 ) return const_cast(mesh->GetFaceDescriptor(mesh->SurfaceElement(ei).GetIndex()).GetBCName().c_str()); else - return const_cast(mesh->LineSegment(ei).GetBCName().c_str()); + return const_cast(mesh->GetBCName(mesh->LineSegment(ei).si).c_str()); } @@ -905,6 +905,7 @@ void Ng_GetSurfaceElementNeighbouringDomains(const int selnr, int & in, int & ou // gibt anzahl an distant pnums zurueck // * pnums entspricht ARRAY +[[deprecated("Use GetDistantNodeNums(locnum) -> FlatArray instead!")]] int NgPar_GetDistantNodeNums ( int nodetype, int locnum, int * distnums ) { int size = NgPar_GetNDistantNodeNums (nodetype, locnum); @@ -931,6 +932,7 @@ int NgPar_GetDistantNodeNums ( int nodetype, int locnum, int * distnums ) return size; } +[[deprecated("Use GetDistantNodeNums(locnum) -> FlatArray instead!")]] int NgPar_GetNDistantNodeNums ( int nodetype, int locnum ) { locnum++; @@ -944,6 +946,7 @@ int NgPar_GetNDistantNodeNums ( int nodetype, int locnum ) return -1; } +[[deprecated("Use GetDistantNodeNums(locnum) -> FlatArray instead!")]] int NgPar_GetGlobalNodeNum (int nodetype, int locnum) { locnum++; diff --git a/libsrc/interface/nginterface_v2.cpp b/libsrc/interface/nginterface_v2.cpp index a961e999..10e8c498 100644 --- a/libsrc/interface/nginterface_v2.cpp +++ b/libsrc/interface/nginterface_v2.cpp @@ -61,8 +61,12 @@ namespace netgen SetGlobalMesh (mesh); } - NgMPI_Comm Ngx_Mesh :: GetCommunicator() const - { return Valid() ? mesh->GetCommunicator() : NgMPI_Comm{}; } + const NgMPI_Comm & Ngx_Mesh :: GetCommunicator() const + { + // return Valid() ? mesh->GetCommunicator() : NgMPI_Comm{}; + if (!Valid()) throw Exception("Ngx_mesh::GetCommunicator: don't have a valid mesh"); + return mesh->GetCommunicator(); + } void Ngx_Mesh :: SaveMesh (ostream & ost) const { @@ -775,29 +779,23 @@ namespace netgen -#ifdef __SSE__ -#include - template<> DLL_HEADER void Ngx_Mesh :: MultiElementTransformation<1,1> (int elnr, int npts, - const tAVXd * xi, size_t sxi, - tAVXd * x, size_t sx, - tAVXd * dxdxi, size_t sdxdxi) const + const SIMD * xi, size_t sxi, + SIMD * x, size_t sx, + SIMD * dxdxi, size_t sdxdxi) const { cout << "multi-eltrafo simd called, 1,1,simd" << endl; } template<> DLL_HEADER void Ngx_Mesh :: MultiElementTransformation<2,2> (int elnr, int npts, - const tAVXd * xi, size_t sxi, - tAVXd * x, size_t sx, - tAVXd * dxdxi, size_t sdxdxi) const + const SIMD * xi, size_t sxi, + SIMD * x, size_t sx, + SIMD * dxdxi, size_t sdxdxi) const { mesh->GetCurvedElements().CalcMultiPointSurfaceTransformation<2> - (elnr, npts, - reinterpret_cast*> (xi), sxi, - reinterpret_cast*> (x), sx, - reinterpret_cast*> (dxdxi), sdxdxi); + (elnr, npts, xi, sxi, x, sx, dxdxi, sdxdxi); /* for (int i = 0; i < npts; i++) { @@ -824,15 +822,15 @@ namespace netgen template<> DLL_HEADER void Ngx_Mesh :: MultiElementTransformation<3,3> (int elnr, int npts, - const tAVXd * xi, size_t sxi, - tAVXd * x, size_t sx, - tAVXd * dxdxi, size_t sdxdxi) const + const SIMD * xi, size_t sxi, + SIMD * x, size_t sx, + SIMD * dxdxi, size_t sdxdxi) const { mesh->GetCurvedElements().CalcMultiPointElementTransformation (elnr, npts, - reinterpret_cast*> (xi), sxi, - reinterpret_cast*> (x), sx, - reinterpret_cast*> (dxdxi), sdxdxi); + xi, sxi, + x, sx, + dxdxi, sdxdxi); /* for (int i = 0; i < npts; i++) { @@ -859,33 +857,43 @@ namespace netgen template<> DLL_HEADER void Ngx_Mesh :: MultiElementTransformation<0,2> (int elnr, int npts, - const tAVXd *xi, size_t sxi, - tAVXd * x, size_t sx, - tAVXd * dxdxi, size_t sdxdxi) const + const SIMD *xi, size_t sxi, + SIMD * x, size_t sx, + SIMD * dxdxi, size_t sdxdxi) const { - cout << "MultiElementtransformation<0,2> simd not implemented" << endl; + //cout << "MultiElementtransformation<0,2> simd not implemented" << endl; + + PointIndex pi = mesh->pointelements[elnr].pnum; + Point<3> xg = mesh->Point(pi); + if (x) + for (int j = 0; j < npts; j++) + for (int i = 0; i < 2; i++) + x[j*sx+i] = xg(i); } template<> DLL_HEADER void Ngx_Mesh :: MultiElementTransformation<0,1> (int elnr, int npts, - const tAVXd * xi, size_t sxi, - tAVXd * x, size_t sx, - tAVXd * dxdxi, size_t sdxdxi) const + const SIMD * xi, size_t sxi, + SIMD * x, size_t sx, + SIMD * dxdxi, size_t sdxdxi) const { - cout << "multi-eltrafo simd called, 0,1,simd" << endl; + //cout << "multi-eltrafo simd called, 0,1,simd" << endl; + PointIndex pi = mesh->pointelements[elnr].pnum; + Point<3> xg = mesh->Point(pi); + if (x) + for (int j = 0; j < npts; j++) + for (int i = 0; i < 1; i++) + x[j*sx+i] = xg(i); } template<> DLL_HEADER void Ngx_Mesh :: MultiElementTransformation<1,3> (int elnr, int npts, - const tAVXd * xi, size_t sxi, - tAVXd * x, size_t sx, - tAVXd * dxdxi, size_t sdxdxi) const + const SIMD * xi, size_t sxi, + SIMD * x, size_t sx, + SIMD * dxdxi, size_t sdxdxi) const { mesh->GetCurvedElements().CalcMultiPointSegmentTransformation<3> - (elnr, npts, - reinterpret_cast*> (xi), sxi, - reinterpret_cast*> (x), sx, - reinterpret_cast*> (dxdxi), sdxdxi); + (elnr, npts, xi, sxi, x, sx, dxdxi, sdxdxi); /* double hxi[4][1]; double hx[4][3]; @@ -908,15 +916,12 @@ namespace netgen template<> DLL_HEADER void Ngx_Mesh :: MultiElementTransformation<1,2> (int elnr, int npts, - const tAVXd * xi, size_t sxi, - tAVXd * x, size_t sx, - tAVXd * dxdxi, size_t sdxdxi) const + const SIMD * xi, size_t sxi, + SIMD * x, size_t sx, + SIMD * dxdxi, size_t sdxdxi) const { mesh->GetCurvedElements().CalcMultiPointSegmentTransformation<2> - (elnr, npts, - reinterpret_cast*> (xi), sxi, - reinterpret_cast*> (x), sx, - reinterpret_cast*> (dxdxi), sdxdxi); + (elnr, npts, xi, sxi, x, sx, dxdxi, sdxdxi); /* for (int i = 0; i < npts; i++) { @@ -943,15 +948,12 @@ namespace netgen template<> DLL_HEADER void Ngx_Mesh :: MultiElementTransformation<2,3> (int elnr, int npts, - const tAVXd * xi, size_t sxi, - tAVXd * x, size_t sx, - tAVXd * dxdxi, size_t sdxdxi) const + const SIMD * xi, size_t sxi, + SIMD * x, size_t sx, + SIMD * dxdxi, size_t sdxdxi) const { mesh->GetCurvedElements().CalcMultiPointSurfaceTransformation<3> - (elnr, npts, - reinterpret_cast*> (xi), sxi, - reinterpret_cast*> (x), sx, - reinterpret_cast*> (dxdxi), sdxdxi); + (elnr, npts, xi, sxi, x, sx, dxdxi, sdxdxi); /* for (int i = 0; i < npts; i++) { @@ -978,9 +980,9 @@ namespace netgen template<> DLL_HEADER void Ngx_Mesh :: MultiElementTransformation<0,3> (int elnr, int npts, - const tAVXd * xi, size_t sxi, - tAVXd * x, size_t sx, - tAVXd * dxdxi, size_t sdxdxi) const + const SIMD * xi, size_t sxi, + SIMD * x, size_t sx, + SIMD * dxdxi, size_t sdxdxi) const { for (int i = 0; i < npts; i++) { @@ -999,10 +1001,6 @@ namespace netgen } } - -#endif - - @@ -1037,6 +1035,14 @@ namespace netgen case 2: { Point<3> p(hp[0], hp[1],0); + try + { + auto ind = mesh->GetSurfaceElementOfPoint(p, lami, nullptr, + build_searchtree); + return ind - 1; + } + catch(NgException e) // quads not implemented curved yet + { for (SegmentIndex si = 0; si < mesh->GetNSeg(); si++) { auto & seg = (*mesh)[si]; @@ -1060,6 +1066,7 @@ namespace netgen return si; } } + } } break; case 3: @@ -1134,7 +1141,18 @@ namespace netgen mesh->BuildCurvedElements(order); } + int Ngx_Mesh :: GetCurveOrder () + { + return mesh->GetCurvedElements().GetOrder(); + } + void Ngx_Mesh :: EnableTable (string name, bool set) + { + mesh->GetTopology().EnableTable (name, set); + } + + + template <> DLL_HEADER void Ngx_Mesh :: SetRefinementFlag<2> (size_t elnr, bool flag) { @@ -1147,9 +1165,9 @@ namespace netgen mesh->VolumeElement(elnr+1).SetRefinementFlag(flag); } - void Ngx_Mesh :: Refine (NG_REFINEMENT_TYPE reftype, + void Ngx_Mesh :: Refine (NG_REFINEMENT_TYPE reftype, bool onlyonce, void (*task_manager)(function), - Tracer tracer) + NgTracer tracer) { NgLock meshlock (mesh->MajorMutex(), 1); @@ -1157,6 +1175,7 @@ namespace netgen biopt.usemarkedelements = 1; biopt.refine_p = 0; biopt.refine_hp = 0; + biopt.onlyonce = onlyonce; if (reftype == NG_REFINE_P) biopt.refine_p = 1; if (reftype == NG_REFINE_HP) @@ -1288,33 +1307,35 @@ void Ngx_Mesh::SetSurfaceElementOrders (int enr, int ox, int oy) +size_t Ngx_Mesh :: GetGlobalVertexNum (int locnum) const +{ +#ifdef PARALLEL + return mesh->GetParallelTopology().GetGlobalPNum (locnum+1)-1; +#else + return locnum; +#endif +} - std::tuple Ngx_Mesh :: GetDistantProcs (int nodetype, int locnum) const +FlatArray Ngx_Mesh :: GetDistantProcs (int nodetype, int locnum) const { #ifdef PARALLEL + if (mesh->GetCommunicator().Size() == 1) + return FlatArray(0,nullptr); + switch (nodetype) { case 0: - { - NgFlatArray dn = mesh->GetParallelTopology().GetDistantPNums(locnum); - return std::tuple(dn.Size(), &dn[0]); - } + return mesh->GetParallelTopology().GetDistantPNums(locnum); case 1: - { - NgFlatArray dn = mesh->GetParallelTopology().GetDistantEdgeNums(locnum); - return std::tuple(dn.Size(), &dn[0]); - } + return mesh->GetParallelTopology().GetDistantEdgeNums(locnum); case 2: - { - NgFlatArray dn = mesh->GetParallelTopology().GetDistantFaceNums(locnum); - return std::tuple(dn.Size(), &dn[0]); - } + return mesh->GetParallelTopology().GetDistantFaceNums(locnum); default: - return std::tuple(0,nullptr); + return FlatArray(0, nullptr); } #else - return std::tuple(0,nullptr); + return FlatArray(0,nullptr); #endif } } diff --git a/libsrc/interface/read_fnf_mesh.cpp b/libsrc/interface/read_fnf_mesh.cpp index 955a1356..2394f510 100644 --- a/libsrc/interface/read_fnf_mesh.cpp +++ b/libsrc/interface/read_fnf_mesh.cpp @@ -208,7 +208,9 @@ namespace netgen sbuf >> nr >> prop >> ch; if (prop == "DEF") { - ; + string name; + sbuf >> name; + mesh.SetMaterial(nr, name); } else { diff --git a/libsrc/interface/readtetmesh.cpp b/libsrc/interface/readtetmesh.cpp index e061c722..46607c59 100644 --- a/libsrc/interface/readtetmesh.cpp +++ b/libsrc/interface/readtetmesh.cpp @@ -154,7 +154,7 @@ namespace netgen break; case 7: - // NodeID, X, Y, Z, Type (0=Reg 1=PMaster 2=PSlave 3=CPMaster 4=CPSlave), PID: + // NodeID, X, Y, Z, Type (0=Reg 1=PMaster 2=PMinion 3=CPMaster 4=CPMinion), PID: { cout << "read nodes" << endl; for(int i=0; i> dummyint >> dummyint >> dummyint; break; @@ -254,7 +254,7 @@ namespace netgen break; case 18: - // MasterEdgeID, 3 SlaveEdgeID's, 3 TranslCode (1=dS1 2=dS2 3=dS1+dS2) + // MasterEdgeID, 3 MinionEdgeID's, 3 TranslCode (1=dS1 2=dS2 3=dS1+dS2) for(int i=0; i> dummyint; @@ -266,7 +266,7 @@ namespace netgen break; case 19: - // FaceID, EdgeID0, EdgeID1, EdgeID2, FaceType (0=Reg 1=PMaster 2=PSlave), PID + // FaceID, EdgeID0, EdgeID1, EdgeID2, FaceType (0=Reg 1=PMaster 2=PMinion), PID { //Segment seg; int segnum_ng[3]; @@ -343,7 +343,7 @@ namespace netgen break; case 21: - // MasterFaceID, SlaveFaceID, TranslCode (1=dS1 2=dS2) + // MasterFaceID, MinionFaceID, TranslCode (1=dS1 2=dS2) { Vec<3> randomvec(-1.32834,3.82399,0.5429151); int maxtransl = -1; diff --git a/libsrc/interface/readuser.cpp b/libsrc/interface/readuser.cpp index 472592ac..f211f38e 100644 --- a/libsrc/interface/readuser.cpp +++ b/libsrc/interface/readuser.cpp @@ -42,12 +42,11 @@ namespace netgen { Point3d p; in >> p.X() >> p.Y() >> p.Z(); - p.Z() *= 10; mesh.AddPoint (p); } mesh.ClearFaceDescriptors(); - mesh.AddFaceDescriptor (FaceDescriptor(0,1,0,0)); + mesh.AddFaceDescriptor (FaceDescriptor(1,1,0,0)); in >> nbe; // int invert = globflags.GetDefineFlag ("invertsurfacemesh"); @@ -313,7 +312,6 @@ namespace netgen ednr = mesh.AddEdgeDescriptor(ed); mesh.SetCD2Name(bcpr, name); auto nr = mesh.AddSegment(tmp_segments[get<0>(element_map[index])-1]); - mesh[nr].SetBCName(mesh.GetCD2NamePtr(mesh.GetNCD2Names())); mesh[nr].edgenr = ednr+1; } else if(dim == 2) @@ -321,7 +319,6 @@ namespace netgen Segment & seg = mesh.LineSegment(get<0>(element_map[index])); seg.si = bccounter + 1; mesh.SetBCName(bccounter, name); - seg.SetBCName(mesh.GetBCNamePtr(bccounter)); bccounter++; } break; @@ -353,13 +350,11 @@ namespace netgen { auto nr = mesh.AddSegment(tmp_segments[get<0>(element_map[index])-1]); mesh[nr].edgenr = ednr+1; - mesh[nr].SetBCName(mesh.GetCD2NamePtr(mesh.GetNCD2Names())); } else if(dim == 2) { Segment & seg = mesh.LineSegment(get<0>(element_map[index])); seg.si = bccounter; - seg.SetBCName(mesh.GetBCNamePtr(bccounter-1)); } break; default: @@ -388,7 +383,6 @@ namespace netgen if(seg.si == -1){ seg.si = bccounter + 1; if(bccounter_tmp == bccounter) mesh.SetBCName(bccounter, "default"); // could be more efficient - seg.SetBCName(mesh.GetBCNamePtr(bccounter)); bccounter_tmp++; } } @@ -649,6 +643,13 @@ namespace netgen ReadFNFFormat (mesh, filename); } + // .cgns file - CFD General Notation System + if ( (strlen (filename) > 5) && + strcmp (&filename[strlen (filename)-5], ".cgns") == 0 ) + { + ReadCGNSMesh (mesh, filename); + } + if ( ( (strlen (filename) > 4) && strcmp (&filename[strlen (filename)-4], ".stl") == 0 ) || ( (strlen (filename) > 5) && strcmp (&filename[strlen (filename)-5], ".stlb") == 0 ) ) { diff --git a/libsrc/interface/rw_cgns.cpp b/libsrc/interface/rw_cgns.cpp new file mode 100644 index 00000000..13010fae --- /dev/null +++ b/libsrc/interface/rw_cgns.cpp @@ -0,0 +1,839 @@ +#include +#include "writeuser.hpp" + +#ifdef NG_CGNS +#include + +#include + +namespace netgen::cg +{ + typedef ngcore::ClosedHashTable, size_t> PointTable; + + int getDim(ElementType_t type) + { + switch(type) + { + case BAR_2: + case BAR_3: + return 1; + case TRI_3: + case TRI_6: + case QUAD_4: + case QUAD_8: + return 2; + case TETRA_4: + case TETRA_10: + case PYRA_5: + case PYRA_13: + case HEXA_8: + case HEXA_20: + case PENTA_6: + case PENTA_15: + return 3; + default: + throw Exception("Read CGNS: unknown element type " + string(cg_ElementTypeName(type))); + } + } + + Segment ReadCGNSElement1D( ElementType_t type, FlatArray verts ) + { + int np; + cg_npe(type, &np); + + Segment s; + for (auto i : Range(np)) + s[i] = verts[i]; + return s; + } + + Element2d ReadCGNSElement2D( ElementType_t type, FlatArray verts ) + { +// static constexpr int map_tri3[] = {0,2,1}; + static constexpr int map_tri6[] = {0,2,1,3,5,4}; // untested +// static constexpr int map_quad4[] = {0,3,2,1}; + static constexpr int map_quad8[] = {0,3,2,1,4,7,6,5}; // untested + + const int * map = nullptr; + switch(type) + { + case TRI_3: +// map = map_tri3; + break; + case QUAD_4: +// map = map_quad4; + break; + case TRI_6: + map = map_tri6; + break; + case QUAD_8: + map = map_quad8; + break; + default: + throw Exception("Read CGNS: unknown element type " + string(cg_ElementTypeName(type))); + } + + int np; + cg_npe(type, &np); + + Element2d el(np); + for (auto i : Range(np)) + el[i] = verts[i]; + return el; + } + + Element ReadCGNSElement3D( ElementType_t type, FlatArray verts ) + { + static constexpr int map_tet4[] = {0,2,1,3}; + static constexpr int map_prism6[] = {0,2,1,3,5,4}; + static constexpr int map_pyra5[] = {0,3,2,1,4}; + static constexpr int map_hexa8[] = {0,3,2,1,4,7,6,5}; + int np; + cg_npe(type, &np); + + const int * map = nullptr; + switch(type) + { + case TETRA_4: + map = map_tet4; break; + case PYRA_5: + map = map_pyra5; break; + case PENTA_6: + map = map_prism6; break; + case HEXA_8: + map = map_hexa8; break; + // TODO: Second order elements + case TETRA_10: + case PYRA_13: + case HEXA_20: + case PENTA_15: + default: + throw Exception("Read CGNS: unknown element type " + string(cg_ElementTypeName(type))); + } + + Element el(np); + for (auto i : Range(np)) + el[i] = verts[map[i]]; + return el; + } + + void WriteCGNSElement( const Segment & el, Array & verts ) + { + verts.Append(BAR_2); + verts.Append(el[0]); + verts.Append(el[1]); + } + + void WriteCGNSElement( const Element2d & el, Array & verts ) + { + static constexpr int map_tri6[] = {0,2,1,3,5,4}; // untested + static constexpr int map_quad8[] = {0,3,2,1,4,7,6,5}; // untested + + ElementType_t type; + + const int * map = nullptr; + switch(el.GetType()) + { + case TRIG: + type = TRI_3; + break; + case QUAD: + type = QUAD_4; + break; + case TRIG6: + type = TRI_6; + map = map_tri6; + break; + case QUAD8: + type = QUAD_8; + map = map_quad8; + break; + // TODO: Second order elements + default: + throw Exception("Write CGNS: unknown element type " + ToString(el.GetType())); + } + + verts.Append(type); + + for (auto i : Range(el.GetNP())) + verts.Append(el[i]); + } + + void WriteCGNSElement( const Element & el, Array & verts ) + { + static constexpr int map_tet4[] = {0,2,1,3}; + static constexpr int map_prism6[] = {0,2,1,3,5,4}; + static constexpr int map_pyra5[] = {0,3,2,1,4}; + static constexpr int map_hexa8[] = {0,3,2,1,4,7,6,5}; + + ElementType_t type; + + const int * map = nullptr; + switch(el.GetType()) + { + case TET: + map = map_tet4; + type = TETRA_4; + break; + case PYRAMID: + type = PYRA_5; + map = map_pyra5; + break; + case PRISM: + type = PENTA_6; + map = map_prism6; + break; + case HEX: + type = HEXA_8; + map = map_hexa8; + break; + // TODO: Second order elements + default: + throw Exception("Write CGNS: unknown element type " + ToString(el.GetType())); + } + + verts.Append(type); + + for (auto i : Range(el.GetNP())) + verts.Append(el[map[i]]); + } + + int WriteCGNSRegion( const Mesh & mesh, int dim, int index, int fn, int base, int zone, int ne_before ) + { + int meshdim = mesh.GetDimension(); + int codim = meshdim-dim; + + if(codim < 0 || codim > 2) + return 0; + + // make sure that each material/boundary name is unique + string prefix[] = { "dom_", "bnd_", "bbnd_" }; + string name = prefix[meshdim-dim] + ToString(index) + "_"; + + if(codim==0) name += mesh.GetMaterial(index+1); + if(codim==1) name += *mesh.GetBCNamePtr(index); + if(codim==2) name += mesh.GetCD2Name(index); + + int ne = 0; + Array data; + + if(dim==3) + for(const auto el : mesh.VolumeElements()) + if(el.GetIndex()==index) + { + ne++; + WriteCGNSElement(el, data); + } + + if(dim==2) + for(const auto el : mesh.SurfaceElements()) + if(el.GetIndex()==index) + { + ne++; + WriteCGNSElement(el, data); + } + + if(dim==1) + for(const auto el : mesh.LineSegments()) + if(el.si==index) + { + ne++; + WriteCGNSElement(el, data); + } + + if(ne==0) + return 0; + + int section; + int start = 1; + int end = ne; +#if CGNS_VERSION < 3400 + cg_section_write(fn,base,zone, name.c_str(), MIXED, ne_before+1, ne_before+ne, 0, &data[0], §ion); +#else + cg_poly_section_write(fn,base,zone, name.c_str(), MIXED, ne_before+1, ne_before+ne, 0, &data[0], nullptr, §ion); +#endif + + return ne; + } + + // maps cgns node type to ngsolve node type + // enum NODE_TYPE { NT_VERTEX = 0, NT_EDGE = 1, NT_FACE = 2, NT_CELL = 3, NT_ELEMENT = 4, NT_FACET = 5 }; + int getNodeType( GridLocation_t location ) + { + switch(location) + { + case Vertex: + return 0; + case CellCenter: + return 3; + case FaceCenter: + return 2; + case EdgeCenter: + return 1; + default: + throw Exception("Read CGNS: unknown grid location " + string(cg_GridLocationName(location))); + } + } + + GridLocation_t getCGNodeType( int node_type ) + { + switch(node_type) + { + case 0: + return Vertex; + case 1: + return EdgeCenter; + case 2: + return FaceCenter; + case 3: + return CellCenter; + default: + throw Exception("Write CGNS: unknown node type " + ToString(node_type)); + } + } + + + struct Solution + { + int fn, base, zone, solution; + string name; + GridLocation_t location; // solution is defined on either cells, faces, edges or vertices + PointSetType_t point_type; + cgsize_t n_points; + + Array field_names; + Array field_datatypes; + + Solution() = default; + + Solution(int fn_, int base_, int zone_, int solution_) + : fn(fn_), base(base_), zone(zone_), solution(solution_) + { + char solname[100]; + cg_sol_info(fn, base, zone, solution, solname, &location); + name = solname; + cg_sol_ptset_info(fn, base, zone, solution, &point_type, &n_points); + + int n_fields = 0; + cg_nfields(fn, base, zone, solution, &n_fields); + + field_names.SetSize(n_fields); + field_datatypes.SetSize(n_fields); + for(auto fi : Range(n_fields)) + { + char buf[100]; + cg_field_info(fn, base, zone, solution, fi+1, &field_datatypes[fi], buf); + field_names[fi] = buf; + } + } + }; + + struct Zone + { + ZoneType_t zone_type; + int fn, base, zone; + int first_index_1d, first_index_2d, first_index_3d; + int nv=0, ne_1d=0, ne_2d=0, ne_3d=0; + + Array names_1d, names_2d, names_3d; + + string name; + cgsize_t size[3]; + + Array solutions; + + Zone(int fn_, int base_, int zone_) + : fn(fn_), base(base_), zone(zone_) + { + cg_zone_type(fn, base, zone, &zone_type); + char zone_name[100]; + cg_zone_read(fn,base,zone, zone_name, size); + nv = size[0]; + + int n_solutions; + cg_nsols(fn, base, zone, &n_solutions); + + solutions.SetSize(n_solutions); + for(auto si : Range(n_solutions)) + solutions[si] = Solution{fn, base, zone, si+1}; + } + + void ReadSolutions( int meshdim, std::vector & sol_names, std::vector> & sol_values, std::vector & sol_locations ) + { + static Timer tall("CGNS::ReadSolutions"); RegionTimer rtall(tall); + for (auto & sol : solutions) + { + for (auto fi : Range(sol.field_names.Size())) + { + cgsize_t size = sol.n_points; + size=0; // TODO: check if sol.point_type is a list or range, and handle appropriately + if(size==0) + { + switch(sol.location) + { + case Vertex: + size = nv; + break; + case CellCenter: + size = (meshdim == 3 ? ne_3d : ne_2d); + break; + case FaceCenter: + case IFaceCenter: + case JFaceCenter: + case KFaceCenter: + case EdgeCenter: + default: + throw Exception("Read CGNS: unknown grid location " + string(cg_GridLocationName(sol.location))); + } + } + + auto values = Array(size); + + cgsize_t imin = 1UL; + cg_field_read(fn, base, zone, sol.solution, sol.field_names[fi].c_str(), RealDouble, &imin, &size, &values[0]); + sol_names.push_back(sol.field_names[fi]); + sol_values.emplace_back(std::move(values)); + sol_locations.push_back(getNodeType(sol.location)); + } + } + } + + void ReadMesh( Mesh & mesh, PointTable & point_table ) + { + static Timer tall("CGNS::ReadMesh-Zone"); RegionTimer rtall(tall); + static Timer tsection("CGNS::ReadMesh-Section"); + first_index_1d = mesh.GetRegionNamesCD(2).Size(); + first_index_2d = mesh.GetRegionNamesCD(1).Size(); + first_index_3d = mesh.GetRegionNamesCD(0).Size(); + + Array x(nv), y(nv), z(nv); + cgsize_t imin=1; + cg_coord_read(fn,base,zone, "CoordinateX", RealDouble, &imin, &nv, &x[0]); + cg_coord_read(fn,base,zone, "CoordinateY", RealDouble, &imin, &nv, &y[0]); + cg_coord_read(fn,base,zone, "CoordinateZ", RealDouble, &imin, &nv, &z[0]); + + Array point_map(nv); + + for(auto i : Range(nv)) + { + ngcore::INT<3,size_t> hash = {*reinterpret_cast(&x[i]), *reinterpret_cast(&y[i]), *reinterpret_cast(&z[i])}; + size_t pi_ng; + size_t pos; + // check if this point is new + if( point_table.PositionCreate (hash, pos) ) + { + pi_ng = mesh.AddPoint( {x[i], y[i], z[i]} ); + point_table.SetData(pos, pi_ng); + } + else + point_table.GetData(pos, pi_ng); + + point_map[i] = pi_ng; + } + + int nsections; + cg_nsections(fn, base, zone, &nsections); + + int index_1d = first_index_1d; + int index_2d = first_index_2d; + int index_3d = first_index_3d; + + for (auto section : Range(1,nsections+1)) + { + RegionTimer rtsection(tsection); + char sec_name[100]; + ElementType_t type; + cgsize_t start, end; + int nbndry, parent_flag; + + cg_section_read(fn, base, zone, section, sec_name, &type, &start, &end, &nbndry, &parent_flag); + PrintMessage(4, "Read section ", section, " with name ", sec_name, " and element type ", cg_ElementTypeName(type)); + + string ngname{sec_name}; + + for (char & c : ngname) + if(c==' ') + c = '_'; + + + if(type==MIXED) + { + bool have_1d_elements = false; + bool have_2d_elements = false; + bool have_3d_elements = false; + + cgsize_t nv; + cg_ElementDataSize(fn, base, zone, section, &nv); + + Array vertices(nv); +#if CGNS_VERSION < 3400 + cg_elements_read(fn, base, zone, section, &vertices[0], nullptr); +#else + cg_poly_elements_read(fn, base, zone, section, &vertices[0], nullptr, nullptr); +#endif + + size_t vi = 0; + while(vi(vertices[vi++]); + int dim = getDim(type); + + int np; + cg_npe(type, &np); + + for (auto & v : vertices.Range(vi, vi+np)) + v = point_map[v-1]; + + if(dim==1) + { + if(!have_1d_elements) + { + index_1d++; + have_1d_elements = true; + mesh.AddEdgeDescriptor(EdgeDescriptor{}); + names_1d.Append(ngname); + } + auto el = ReadCGNSElement1D(type, vertices.Range(vi, vertices.Size())); + el.si = index_1d; + mesh.AddSegment(el); + vi += el.GetNP(); + ne_1d++; + } + + if(dim==2) + { + if(!have_2d_elements) + { + index_2d++; + have_2d_elements = true; + mesh.AddFaceDescriptor(FaceDescriptor(index_2d, 1, 0, 1)); + names_2d.Append(ngname); + } + auto el = ReadCGNSElement2D(type, vertices.Range(vi, vertices.Size())); + el.SetIndex(index_2d); + mesh.AddSurfaceElement(el); + vi += el.GetNP(); + ne_2d++; + } + + if(dim==3) + { + if(!have_3d_elements) + { + index_3d++; + have_3d_elements = true; + names_3d.Append(ngname); + } + + auto el = ReadCGNSElement3D(type, vertices.Range(vi, vertices.Size())); + el.SetIndex(index_3d); + mesh.AddVolumeElement(el); + vi += el.GetNP(); + ne_3d++; + } + } + } + else + { + int dim = getDim(type); + + cgsize_t nv; + cg_ElementDataSize(fn, base, zone, section, &nv); + int np=0; + cg_npe(type, &np); + + Array vertices(nv); + cg_elements_read(fn, base, zone, section, &vertices[0], nullptr); + for (auto & v : vertices) + v = point_map[v-1]; + int ne_section = nv/np; + + if(dim==1) + { + index_1d++; + mesh.AddEdgeDescriptor(EdgeDescriptor{}); + names_1d.Append(ngname); + for(auto i : Range(ne_section)) + { + auto el = ReadCGNSElement1D(type, vertices.Range(np*i, np*(i+1))); + el.si = index_1d; + mesh.AddSegment(el); + } + ne_1d += ne_section; + } + + if(dim==2) + { + index_2d++; + mesh.AddFaceDescriptor(FaceDescriptor(index_2d, 1, 0, 1)); + names_2d.Append(ngname); + for(auto i : Range(ne_section)) + { + auto el = ReadCGNSElement2D(type, vertices.Range(np*i, np*(i+1))); + el.SetIndex(index_2d); + mesh.AddSurfaceElement(el); + } + ne_2d += ne_section; + } + + if(dim==3) + { + index_3d++; + names_3d.Append(ngname); + for(auto i : Range(ne_section)) + { + auto el = ReadCGNSElement3D(type, vertices.Range(np*i, np*(i+1))); + el.SetIndex(index_3d); + mesh.AddVolumeElement(el); + } + ne_3d += ne_section; + } + } + } + + mesh.GetRegionNamesCD(2).SetSize(index_1d); + mesh.GetRegionNamesCD(1).SetSize(index_2d); + mesh.GetRegionNamesCD(0).SetSize(index_3d); + mesh.GetRegionNamesCD(2) = nullptr; + mesh.GetRegionNamesCD(1) = nullptr; + mesh.GetRegionNamesCD(0) = nullptr; + } + + void SetNames( Mesh & mesh ) + { + if(mesh.GetDimension() == 2) + { + for (auto i : Range(names_1d.Size())) + mesh.SetBCName(first_index_1d + i, names_1d[i]); + + for (auto i : Range(names_2d.Size())) + mesh.SetMaterial(first_index_2d + i +1, names_2d[i]); + } + else + { + for (auto i : Range(names_1d.Size())) + mesh.SetCD2Name(first_index_1d + i +1, names_1d[i]); + + for (auto i : Range(names_2d.Size())) + { + mesh.SetBCName(first_index_2d + i, names_2d[i]); + mesh.GetFaceDescriptor(first_index_2d + i +1).SetDomainIn(first_index_3d+1); + } + + for (auto i : Range(names_3d.Size())) + mesh.SetMaterial(first_index_3d + i +1, names_3d[i]); + } + } + }; +} + +namespace netgen +{ + int ReadCGNSMesh (Mesh & mesh, const string & filename, Array> & zones) + { + mesh.SetDimension(3); + static Timer tall("CGNS::ReadMesh"); RegionTimer rtall(tall); + int fn; + cg_open(filename.c_str(),CG_MODE_READ,&fn); + + int base = 1; + int nzones; + cg_nzones(fn, base, &nzones); + + int n_vertices = 0; + for (auto zi : Range(1, nzones+1)) + { + int size[3]; + char name[100]; + cg_zone_read(fn,base,zi, name, size); + n_vertices += size[0]; + } + + cg::PointTable points(2*n_vertices); + + for (auto zi : Range(1, nzones+1)) + { + ZoneType_t zone_type; + cg_zone_type(fn, base, zi, &zone_type); + if(zone_type != Unstructured ) + { + PrintMessage(2, "skipping zone with type ", cg_ZoneTypeName(zone_type) ); + continue; + } + auto zone = make_unique(fn, base, zi); + zone->ReadMesh( mesh, points ); + zones.Append(std::move(zone)); + } + + if(mesh.GetNE() == 0) + mesh.SetDimension(2); + + for (auto & zone : zones) + zone->SetNames(mesh); + + mesh.UpdateTopology(); + const auto & topo = mesh.GetTopology(); + + for (auto sei : Range(mesh.SurfaceElements())) + { + int ei0, ei1; + topo.GetSurface2VolumeElement (sei+1, ei0, ei1); + auto si = mesh.SurfaceElement(sei).GetIndex(); + auto & fd = mesh.GetFaceDescriptor(si); + + if(ei0>0) + { + int i0 = mesh.VolumeElement(ei0).GetIndex(); + if(fd.DomainIn()!=i0) + fd.SetDomainOut(i0); + } + + if(ei1>0) + { + int i1 = mesh.VolumeElement(ei1).GetIndex(); + if(fd.DomainIn()!=i1) + fd.SetDomainOut(i1); + } + } + return fn; + } + + void ReadCGNSMesh (Mesh & mesh, const string & filename) + { + Array> zones; + int fn = ReadCGNSMesh(mesh, filename, zones); + cg_close(fn); + } + + // Reads mesh and solutions of .csns file + tuple, vector, vector>, vector> ReadCGNSFile(string filename, int base) + { + static Timer tall("CGNS::ReadFile"); RegionTimer rtall(tall); + + auto mesh = make_shared(); + Array> zones; + int fn = ReadCGNSMesh(*mesh, filename, zones); + + std::vector names; + std::vector> values; + std::vector locations; + + for (auto & zone : zones) + zone->ReadSolutions( mesh->GetDimension(), names, values, locations ); + + cg_close(fn); + return std::make_tuple(mesh, names, values, locations); + } + + void WriteCGNSMesh (const Mesh & mesh, int fn, int & base, int & zone) + { + int dim = mesh.GetDimension(); + cg_base_write(fn, "mesh", dim, dim, &base); + + int nv = static_cast(mesh.GetNV()); + int ne = mesh.GetNE(); + + Array x, y, z; + for(auto & p : mesh.Points()) + { + x.Append(p[0]); + y.Append(p[1]); + z.Append(p[2]); + } + + cgsize_t isize[3] = { nv, ne, 0 }; + cg_zone_write(fn,base, "mesh", isize, Unstructured, &zone); + + int coord; + cg_coord_write(fn,base,zone, RealDouble, "CoordinateX", &x[0], &coord); + cg_coord_write(fn,base,zone, RealDouble, "CoordinateY", &y[0], &coord); + cg_coord_write(fn,base,zone, RealDouble, "CoordinateZ", &z[0], &coord); + + int imax3 = 0; + for(const auto & el : mesh.VolumeElements()) + imax3 = max(imax3, el.GetIndex()); + + int imax2 = 0; + for(const auto & el : mesh.SurfaceElements()) + imax2 = max(imax2, el.GetIndex()); + + int imax1 = 0; + for(const auto & el : mesh.LineSegments()) + imax1 = max(imax1, el.si); + + int ne_written = 0; + int meshdim = mesh.GetDimension(); + + for(const auto i : IntRange(imax3)) + ne_written += cg::WriteCGNSRegion(mesh, 3, i+1, fn, base, zone, ne_written); + + for(const auto i : IntRange(imax2)) + ne_written += cg::WriteCGNSRegion(mesh, 2, i+1, fn, base, zone, ne_written); + + for(const auto i : IntRange(imax1)) + ne_written += cg::WriteCGNSRegion(mesh, 1, i+1, fn, base, zone, ne_written); + + } + + void WriteCGNSMesh (const Mesh & mesh, const string & filename) + { + static Timer tall("CGNS::WriteMesh"); RegionTimer rtall(tall); + int fn, base, zone; + cg_open(filename.c_str(),CG_MODE_WRITE,&fn); + + WriteCGNSMesh(mesh, fn, base, zone); + + cg_close(fn); + } + + + void WriteCGNSFile(shared_ptr mesh, string filename, vector fields, vector> values, vector locations) + { + static Timer tall("CGNS::WriteFile"); RegionTimer rtall(tall); + int fn, base, zone; + cg_open(filename.c_str(),CG_MODE_WRITE,&fn); + + WriteCGNSMesh(*mesh, fn, base, zone); + + for(auto i : IntRange(fields.size())) + { + int section, field; + string name = "solution_" + ToString(i); + + + cg_sol_write(fn, base, zone, name.c_str(), cg::getCGNodeType(locations[i]), §ion); + cg_field_write(fn, base, zone, section, RealDouble, fields[i].c_str(), &values[i][0], &field); + } + + cg_close(fn); + } + +} + +#else // NG_CGNS + +namespace netgen +{ + void ReadCGNSMesh (Mesh & mesh, const string & filename) + { + PrintMessage(1, "Could not import CGNS mesh: Netgen was built without CGNS support"); + } + + tuple, vector, vector>, vector> ReadCGNSFile(string filename, int base) + { + throw Exception("Netgen was built without CGNS support"); + } + + void WriteCGNSMesh (const Mesh & mesh, const string & filename) + { + PrintMessage(1, "Could not write CGNS mesh: Netgen was built without CGNS support"); + } + + void WriteCGNSFile(shared_ptr mesh, string filename, vector fields, vector> values, vector locations) + { + throw Exception("Netgen was built without CGNS support"); + } + +} + +#endif // NG_CGNS diff --git a/libsrc/interface/writeabaqus.cpp b/libsrc/interface/writeabaqus.cpp index 916600af..209ac89a 100644 --- a/libsrc/interface/writeabaqus.cpp +++ b/libsrc/interface/writeabaqus.cpp @@ -158,25 +158,25 @@ void WriteAbaqusFormat (const Mesh & mesh, cout << "masternode = " << masternode << " = " << mesh.Point(masternode) << endl; - NgArray slaves(3); + NgArray minions(3); for (i = 1; i <= 3; i++) { mesh.GetIdentifications().GetPairs (i, pairs); for (j = 1; j <= pairs.Size(); j++) { if (pairs.Get(j).I1() == masternode) - slaves.Elem(i) = pairs.Get(j).I2(); + minions.Elem(i) = pairs.Get(j).I2(); } - cout << "slave(" << i << ") = " << slaves.Get(i) - << " = " << mesh.Point(slaves.Get(i)) << endl; + cout << "minion(" << i << ") = " << minions.Get(i) + << " = " << mesh.Point(minions.Get(i)) << endl; } outfile << "**\n" << "*NSET,NSET=CTENODS\n" - << slaves.Get(1) << ", " - << slaves.Get(2) << ", " - << slaves.Get(3) << endl; + << minions.Get(1) << ", " + << minions.Get(2) << ", " + << minions.Get(3) << endl; outfile << "**\n" @@ -190,7 +190,7 @@ void WriteAbaqusFormat (const Mesh & mesh, << "*BOUNDARY, OP=NEW\n"; for (j = 1; j <= 3; j++) { - Vec3d v(mesh.Point(masternode), mesh.Point(slaves.Get(j))); + Vec3d v(mesh.Point(masternode), mesh.Point(minions.Get(j))); double vlen = v.Length(); int dir = 0; if (fabs (v.X()) > 0.9 * vlen) dir = 2; @@ -198,7 +198,7 @@ void WriteAbaqusFormat (const Mesh & mesh, if (fabs (v.Z()) > 0.9 * vlen) dir = 1; if (!dir) cout << "ERROR: Problem with rigid body constraints" << endl; - outfile << slaves.Get(j) << ", " << dir << ",, 0.\n"; + outfile << minions.Get(j) << ", " << dir << ",, 0.\n"; } outfile << "**\n" @@ -223,7 +223,7 @@ void WriteAbaqusFormat (const Mesh & mesh, mpc << "4" << "\n"; mpc << pairs.Get(j).I2() << "," << k << ", -1.0, "; mpc << pairs.Get(j).I1() << "," << k << ", 1.0, "; - mpc << slaves.Get(i) << "," << k << ", 1.0, "; + mpc << minions.Get(i) << "," << k << ", 1.0, "; mpc << masternode << "," << k << ", -1.0 \n"; } } diff --git a/libsrc/interface/writefeap.cpp b/libsrc/interface/writefeap.cpp index dc2574a9..84f71eef 100644 --- a/libsrc/interface/writefeap.cpp +++ b/libsrc/interface/writefeap.cpp @@ -144,11 +144,11 @@ void WriteFEAPFormat (const Mesh & mesh, // BEGIN CONTACT OUTPUT /* - int masterindex, slaveindex; + int masterindex, minionindex; cout << "Master Surface index = "; cin >> masterindex; - cout << "Slave Surface index = "; - cin >> slaveindex; + cout << "Minion Surface index = "; + cin >> minionindex; // CONTACT SURFACE 1 @@ -196,7 +196,7 @@ void WriteFEAPFormat (const Mesh & mesh, Element2d sel = mesh.SurfaceElement(i); if (invertsurf) sel.Invert(); - if (mesh.GetFaceDescriptor(sel.GetIndex ()).BCProperty() == slaveindex) + if (mesh.GetFaceDescriptor(sel.GetIndex ()).BCProperty() == minionindex) { zz++; outfile.width(14); diff --git a/libsrc/interface/writetet.cpp b/libsrc/interface/writetet.cpp index 3bc0ba5a..2dbfa440 100644 --- a/libsrc/interface/writetet.cpp +++ b/libsrc/interface/writetet.cpp @@ -427,7 +427,7 @@ namespace netgen << numedges << " " << numnodes << endl << endl; - outfile << "// NodeID, X, Y, Z, Type (0=Reg 1=PMaster 2=PSlave 3=CPMaster 4=CPSlave), "<< uidpid <<":\n" \ + outfile << "// NodeID, X, Y, Z, Type (0=Reg 1=PMaster 2=PMinion 3=CPMaster 4=CPMinion), "<< uidpid <<":\n" \ << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"; @@ -515,7 +515,7 @@ namespace netgen << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" \ << n2 << "\n" \ << "\n" \ - << "// MasterNodeID, SlaveNodeID, TranslCode (1=dS1 2=dS2 3=dS1+dS2):\n" \ + << "// MasterNodeID, MinionNodeID, TranslCode (1=dS1 2=dS2 3=dS1+dS2):\n" \ << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"; for(int i=0; i, vector, vector>, vector> +DLL_HEADER ReadCGNSFile(string filename, int base); + +extern void DLL_HEADER WriteCGNSFile(shared_ptr mesh,string filename, vector fields, + vector> values, vector locations); + + void WriteDolfinFormat (const Mesh & mesh, const string & filename); diff --git a/libsrc/meshing/CMakeLists.txt b/libsrc/meshing/CMakeLists.txt index 47b99e79..342dd1e0 100644 --- a/libsrc/meshing/CMakeLists.txt +++ b/libsrc/meshing/CMakeLists.txt @@ -12,7 +12,7 @@ add_library(mesh ${NG_LIB_TYPE} smoothing2.cpp smoothing3.cpp specials.cpp tetrarls.cpp topology.cpp triarls.cpp validate.cpp bcfunctions.cpp parallelmesh.cpp paralleltop.cpp paralleltop.hpp basegeom.cpp - python_mesh.cpp hexarls.cpp + python_mesh.cpp hexarls.cpp surfacegeom.cpp ../../ng/onetcl.cpp ${mesh_object_libs} ) @@ -23,7 +23,7 @@ endif(APPLE) target_link_libraries( mesh PUBLIC ngcore PRIVATE gprim la gen ) -target_link_libraries( mesh PRIVATE netgen_metis netgen_python ${ZLIB_LIBRARIES} ) +target_link_libraries( mesh PRIVATE netgen_metis "$" ${ZLIB_LIBRARIES} ) if(NOT WIN32) install( TARGETS mesh ${NG_INSTALL_DIR}) endif(NOT WIN32) @@ -37,6 +37,6 @@ install(FILES localh.hpp meshclass.hpp meshfunc.hpp meshing2.hpp meshing3.hpp meshing.hpp meshtool.hpp meshtype.hpp msghandler.hpp paralleltop.hpp ruler2.hpp ruler3.hpp specials.hpp topology.hpp validate.hpp - python_mesh.hpp + python_mesh.hpp surfacegeom.hpp DESTINATION ${NG_INSTALL_DIR_INCLUDE}/meshing COMPONENT netgen_devel ) diff --git a/libsrc/meshing/adfront2.cpp b/libsrc/meshing/adfront2.cpp index fc858ca4..29643699 100644 --- a/libsrc/meshing/adfront2.cpp +++ b/libsrc/meshing/adfront2.cpp @@ -503,7 +503,7 @@ namespace netgen } bool AdFront2 :: SameSide (const Point<2> & lp1, const Point<2> & lp2, - const NgArray * testfaces) const + const FlatArray * testfaces) const { int cnt = 0; diff --git a/libsrc/meshing/adfront2.hpp b/libsrc/meshing/adfront2.hpp index 5758c184..95914535 100644 --- a/libsrc/meshing/adfront2.hpp +++ b/libsrc/meshing/adfront2.hpp @@ -206,7 +206,7 @@ public: const FrontLine & GetLine (int nr) { return lines[nr]; } const FrontPoint2 & GetPoint (int nr) { return points[nr]; } - + const auto & GetLines () const { return lines; } /// int SelectBaseLine (Point<3> & p1, Point<3> & p2, @@ -262,7 +262,7 @@ public: bool Inside (const Point<2> & p) const; bool SameSide (const Point<2> & lp1, const Point<2> & lp2, - const NgArray * /* testfaces */ = NULL) const; + const FlatArray * /* testfaces */ = NULL) const; /* { return Inside (lp1) == Inside (lp2); diff --git a/libsrc/meshing/bcfunctions.cpp b/libsrc/meshing/bcfunctions.cpp index d430a3a7..a7214954 100644 --- a/libsrc/meshing/bcfunctions.cpp +++ b/libsrc/meshing/bcfunctions.cpp @@ -322,7 +322,7 @@ namespace netgen colours_sorted.SetSize(all_colours.Size()+1); faces_sorted = 0; - // Slave NgArray to identify the colours the faces were assigned to, + // Index NgArray to identify the colours the faces were assigned to, // after the bubble sort routine to sort the automatic boundary // identifiers according to the number of surface mesh elements // of a given colour diff --git a/libsrc/meshing/bcfunctions.hpp b/libsrc/meshing/bcfunctions.hpp index 55aca571..967dd0d8 100644 --- a/libsrc/meshing/bcfunctions.hpp +++ b/libsrc/meshing/bcfunctions.hpp @@ -26,7 +26,7 @@ namespace netgen - Use colour index 0 (zero) for all faces with no colour defined - Calculate the number of faces of the surface mesh for each colour - Sort the number of surface elements in ascending order, with the - colour indices as a slave + colour indices as a index - Use the indices of the sorted array as the BC property number Example: If there are 3 colours, present in the mesh and the number diff --git a/libsrc/meshing/bisect.cpp b/libsrc/meshing/bisect.cpp index 9b5c770f..0205cc81 100644 --- a/libsrc/meshing/bisect.cpp +++ b/libsrc/meshing/bisect.cpp @@ -1082,8 +1082,12 @@ namespace netgen MarkedQuad & mq) { for (int i = 0; i < 4; i++) - mq.pnums[i] = el[i]; - Swap (mq.pnums[2], mq.pnums[3]); + { + mq.pnums[i] = el[i]; + mq.pgeominfo[i] = el.GeomInfoPi (i+1); + } + Swap (mq.pnums[2], mq.pnums[3]); + Swap (mq.pgeominfo[2], mq.pgeominfo[3]); mq.marked = 0; mq.markededge = 0; @@ -1681,7 +1685,7 @@ namespace netgen int MarkHangingTets (T_MTETS & mtets, const INDEX_2_CLOSED_HASHTABLE & cutedges, - TaskManager tm) + NgTaskManager tm) { static int timer = NgProfiler::CreateTimer ("MarkHangingTets"); NgProfiler::RegionTimer reg (timer); @@ -1759,7 +1763,7 @@ namespace netgen bool MarkHangingTris (T_MTRIS & mtris, const INDEX_2_CLOSED_HASHTABLE & cutedges, - TaskManager tm) + NgTaskManager tm) { bool hanging = false; // for (int i = 1; i <= mtris.Size(); i++) @@ -3015,7 +3019,7 @@ namespace netgen { cnttet++; mtets.Elem(cnttet).marked = - 3 * mesh.VolumeElement(i).TestRefinementFlag(); + (opt.onlyonce ? 3 : 1) * mesh.VolumeElement(i).TestRefinementFlag(); if (mtets.Elem(cnttet).marked) cntm++; } @@ -3034,7 +3038,7 @@ namespace netgen for (int i = 1; i <= mtets.Size(); i++) { mtets.Elem(i).marked = - 3 * mesh.VolumeElement(i).TestRefinementFlag(); + (opt.onlyonce ? 1 : 3) * mesh.VolumeElement(i).TestRefinementFlag(); if (mtets.Elem(i).marked) cntm++; } @@ -3064,7 +3068,7 @@ namespace netgen { cnttrig++; mtris.Elem(cnttrig).marked = - mesh.SurfaceElement(i).TestRefinementFlag() ? 2 : 0; + mesh.SurfaceElement(i).TestRefinementFlag() ? (opt.onlyonce ? 1 : 2) : 0; // mtris.Elem(cnttrig).marked = 0; if (mtris.Elem(cnttrig).marked) cntm++; diff --git a/libsrc/meshing/bisect.hpp b/libsrc/meshing/bisect.hpp index 6b96bd07..9cab3ef3 100644 --- a/libsrc/meshing/bisect.hpp +++ b/libsrc/meshing/bisect.hpp @@ -12,8 +12,9 @@ public: int usemarkedelements; bool refine_hp; bool refine_p; - TaskManager task_manager = &DummyTaskManager; - Tracer tracer = &DummyTracer; + bool onlyonce = false; + NgTaskManager task_manager = &DummyTaskManager; + NgTracer tracer = &DummyTracer; DLL_HEADER BisectionOptions (); }; diff --git a/libsrc/meshing/boundarylayer.cpp b/libsrc/meshing/boundarylayer.cpp index 4d132615..3c4b3951 100644 --- a/libsrc/meshing/boundarylayer.cpp +++ b/libsrc/meshing/boundarylayer.cpp @@ -1,5 +1,8 @@ #include #include "meshing.hpp" +#include "meshing2.hpp" +#include "global.hpp" +#include "../geom2d/csg2d.hpp" namespace netgen { @@ -88,560 +91,1250 @@ namespace netgen cout << "Quads: " << nq << endl; } + void GenerateBoundaryLayer(Mesh& mesh, const BoundaryLayerParameters& blp) + { + static Timer timer("Create Boundarylayers"); + RegionTimer regt(timer); + int max_edge_nr = -1; + for(const auto& seg : mesh.LineSegments()) + if(seg.edgenr > max_edge_nr) + max_edge_nr = seg.edgenr; + int new_mat_nr = mesh.GetNDomains() +1; + mesh.SetMaterial(new_mat_nr, blp.new_mat); + auto domains = blp.domains; + if(!blp.outside) + domains.Invert(); -/* - Philippose Rajan - 11 June 2009 + mesh.UpdateTopology(); + auto& meshtopo = mesh.GetTopology(); - Function to calculate the surface normal at a given - vertex of a surface element, with respect to that - surface element. + int np = mesh.GetNP(); + int ne = mesh.GetNE(); + int nse = mesh.GetNSE(); + int nseg = mesh.GetNSeg(); - This function is used by the boundary layer generation - function, in order to calculate the effective direction - in which the prismatic layer should grow -*/ - void GetSurfaceNormal(Mesh & mesh, const Element2d & el, int Vertex, Vec3d & SurfaceNormal) - { - int Vertex_A; - int Vertex_B; + Array, PointIndex> mapto(np); - Vertex_A = Vertex + 1; - if(Vertex_A > el.GetNP()) Vertex_A = 1; + Array, PointIndex> growthvectors(np); + growthvectors = 0.; - Vertex_B = Vertex - 1; - if(Vertex_B <= 0) Vertex_B = el.GetNP(); + Array surfacefacs(mesh.GetNFD()+1); + surfacefacs = 0.; - Vec3d Vect_A,Vect_B; - - Vect_A = mesh[el.PNum(Vertex_A)] - mesh[el.PNum(Vertex)]; - Vect_B = mesh[el.PNum(Vertex_B)] - mesh[el.PNum(Vertex)]; + auto getSurfaceNormal = [&mesh] (const Element2d& el) + { + auto v0 = mesh[el[0]]; + return Cross(mesh[el[1]]-v0, mesh[el[2]]-v0).Normalize(); + }; - SurfaceNormal = Cross(Vect_A,Vect_B); - SurfaceNormal.Normalize(); - } + // surface index map + Array si_map(mesh.GetNFD()+1); + si_map = -1; + int fd_old = mesh.GetNFD(); - - - -/* - Philippose Rajan - 11 June 2009 - - Added an initial experimental function for - generating prismatic boundary layers on - a given set of surfaces. - - The number of layers, height of the first layer - and the growth / shrink factor can be specified - by the user - - Currently, the layer height is calculated using: - height = h_first_layer * (growth_factor^(num_layers - 1)) -*/ - void GenerateBoundaryLayer (Mesh & mesh, BoundaryLayerParameters & blp) - { - ofstream dbg("BndLayerDebug.log"); - - // Angle between a surface element and a growth-vector below which - // a prism is project onto that surface as a quad - // (in degrees) - double angleThreshold = 5.0; - - - NgArray surfid (blp.surfid); - int prismlayers = blp.prismlayers; - double hfirst = blp.hfirst; - double growthfactor = blp.growthfactor; - NgArray heights (blp.heights); - - bool grow_edges = false; // grow layer at edges - - - // Monitor and print out the number of prism and quad elements - // added to the mesh - int numprisms = 0; - int numquads = 0; - - - cout << "Old NP: " << mesh.GetNP() << endl; - cout << "Old NSE: " << mesh.GetNSE() << endl; - - for(int layer = prismlayers; layer >= 1; layer--) - { - cout << "Generating layer: " << layer << endl; - - const MeshTopology& meshtopo = mesh.GetTopology(); - const_cast (meshtopo).SetBuildEdges(true); - const_cast (meshtopo).SetBuildFaces(true); - const_cast (meshtopo).Update(); - - double layerht = hfirst; - - if(heights.Size()>0) - { - layerht = heights[layer-1]; - } - else - { - if(growthfactor == 1) - { - layerht = layer * hfirst; - } - else - { - layerht = hfirst*(pow(growthfactor,(layer+1)) - 1)/(growthfactor - 1); - } - } - - cout << "Layer Height = " << layerht << endl; - - // Need to store the old number of points and - // surface elements because there are new points and - // surface elements being added during the process - int np = mesh.GetNP(); - int nse = mesh.GetNSE(); - int ne = mesh.GetNE(); - - // Safety measure to ensure no issues with mesh - // consistency - int nseg = mesh.GetNSeg(); - - // Indicate which points need to be remapped - NgBitArray bndnodes(np+1); // big enough for 1-based array - - // Map of the old points to the new points - NgArray mapto(np); - - // Growth vectors for the prismatic layer based on - // the effective surface normal at a given point - NgArray growthvectors(np); - - // Bit array to identify all the points belonging - // to the surface of interest - bndnodes.Clear(); - - // Run through all the surface elements and mark the points - // belonging to those where a boundary layer has to be created. - // In addition, also calculate the effective surface normal - // vectors at each of those points to determine the mesh motion - // direction - cout << "Marking points for remapping...." << endl; - - for (SurfaceElementIndex si = 0; si < nse; si++) - if (surfid.Contains(mesh[si].GetIndex())) - { - const Element2d & sel = mesh[si]; - for(int j = 0; j < sel.GetNP(); j++) - { - // Set the bitarray to indicate that the - // point is part of the required set - bndnodes.Set(sel[j]); - Vec3d surfacenormal; - - // Calculate the surface normal at the current point - // with respect to the current surface element - GetSurfaceNormal(mesh,sel,j+1,surfacenormal); - - // Add the surface normal to the already existent one - // (This gives the effective normal direction at corners - // and curved areas) - growthvectors[sel[j]] += surfacenormal; - } - } - - if (!grow_edges) - for (SegmentIndex sei = 0; sei <= nseg; sei++) - { - bndnodes.Clear (mesh[sei][0]); - bndnodes.Clear (mesh[sei][1]); - } - - // Add additional points into the mesh structure in order to - // clone the surface elements. - // Also invert the growth vectors so that they point inwards, - // and normalize them - cout << "Cloning points and calculating growth vectors...." << endl; - - for (PointIndex pi = 1; pi <= np; pi++) - { - if (bndnodes.Test(pi)) - { - mapto[pi] = mesh.AddPoint (mesh[pi]); - - growthvectors[pi].Normalize(); - growthvectors[pi] *= -1.0; - } - else - { - mapto[pi] = 0; - growthvectors[pi] = Vec3d(0,0,0); - } - } - - - // Add quad surface elements at edges for surfaces which - // don't have boundary layers - - // Bit array to keep track of segments already processed - NgBitArray segsel(nseg); - - // Set them all to "1" to initially activate all segments - segsel.Set(); - - cout << "Adding 2D Quad elements on required surfaces...." << endl; - - if (grow_edges) - for (SegmentIndex sei = 0; sei <= nseg; sei++) - { - PointIndex seg_p1 = mesh[sei][0]; - PointIndex seg_p2 = mesh[sei][1]; - - // Only go in if the segment is still active, and if both its - // surface index is part of the "hit-list" - if(segsel.Test(sei) && surfid.Contains(mesh[sei].si)) - { - // clear the bit to indicate that this segment has been processed - segsel.Clear(sei); - - // Find matching segment pair on other surface - for (SegmentIndex sej = 0; sej < nseg; sej++) - { - PointIndex segpair_p1 = mesh[sej][1]; - PointIndex segpair_p2 = mesh[sej][0]; - - // Find the segment pair on the neighbouring surface element - // Identified by: seg1[0] = seg_pair[1] and seg1[1] = seg_pair[0] - if(segsel.Test(sej) && ((segpair_p1 == seg_p1) && (segpair_p2 == seg_p2))) - { - // clear bit to indicate that processing of this segment is done - segsel.Clear(sej); - - // Only worry about those surfaces which are not in the - // boundary layer list - if(!surfid.Contains(mesh[sej].si)) - { - SurfaceElementIndex pnt_commelem = 0; - NgArray pnt1_elems; - NgArray pnt2_elems; - - - meshtopo.GetVertexSurfaceElements(segpair_p1,pnt1_elems); - meshtopo.GetVertexSurfaceElements(segpair_p2,pnt2_elems); - - for(int k = 0; k < pnt1_elems.Size(); k++) - { - const Element2d & pnt1_sel = mesh.SurfaceElement(pnt1_elems[k]); - for(int l = 0; l < pnt2_elems.Size(); l++) - { - const Element2d & pnt2_sel = mesh.SurfaceElement(pnt2_elems[l]); - if((pnt1_sel.GetIndex() == mesh[sej].si) - && (pnt2_sel.GetIndex() == mesh[sej].si) - && (pnt1_elems[k] == pnt2_elems[l])) - { - pnt_commelem = pnt1_elems[k]; - } - } - } - - /* - int pnum_commelem = 0; - for(int k = 1; k <= mesh.SurfaceElement(pnt_commelem).GetNP(); k++) - { - if((mesh.SurfaceElement(pnt_commelem).PNum(k) != segpair_p1) - && (mesh.SurfaceElement(pnt_commelem).PNum(k) != segpair_p2)) - { - pnum_commelem = mesh.SurfaceElement(pnt_commelem).PNum(k); - } - } - */ - - Vec3d surfelem_vect, surfelem_vect1; - - const Element2d & commsel = mesh.SurfaceElement(pnt_commelem); - - dbg << "NP= " << commsel.GetNP() << " : "; - - for(int k = 1; k <= commsel.GetNP(); k++) - { - GetSurfaceNormal(mesh,commsel,k,surfelem_vect1); - surfelem_vect += surfelem_vect1; - } - - surfelem_vect.Normalize(); - - double surfangle = Angle(growthvectors.Elem(segpair_p1),surfelem_vect); - - dbg << "V1= " << surfelem_vect1 - << " : V2= " << surfelem_vect1 - << " : V= " << surfelem_vect - << " : GV= " << growthvectors.Elem(segpair_p1) - << " : Angle= " << surfangle * 180 / 3.141592; - - - // remap the segments to the new points - mesh[sei][0] = mapto[seg_p1]; - mesh[sei][1] = mapto[seg_p2]; - mesh[sej][1] = mapto[seg_p1]; - mesh[sej][0] = mapto[seg_p2]; - - if((surfangle < (90 + angleThreshold) * 3.141592 / 180.0) - && (surfangle > (90 - angleThreshold) * 3.141592 / 180.0)) - { - dbg << " : quad\n"; - // Since the surface is lower than the threshold, change the effective - // prism growth vector to match with the surface vector, so that - // the Quad which is created lies on the original surface - //growthvectors.Elem(segpair_p1) = surfelem_vect; - - // Add a quad element to account for the prism volume - // element which is going to be added - Element2d sel(QUAD); - sel.PNum(4) = mapto[seg_p1]; - sel.PNum(3) = mapto[seg_p2]; - sel.PNum(2) = segpair_p2; - sel.PNum(1) = segpair_p1; - sel.SetIndex(mesh[sej].si); - mesh.AddSurfaceElement(sel); - numquads++; - } - else - { - dbg << "\n"; - for (int k = 0; k < pnt1_elems.Size(); k++) - { - Element2d & pnt_sel = mesh.SurfaceElement(pnt1_elems[k]); - if(pnt_sel.GetIndex() == mesh[sej].si) - { - for(int l = 0; l < pnt_sel.GetNP(); l++) - { - if(pnt_sel[l] == segpair_p1) - pnt_sel[l] = mapto[seg_p1]; - else if (pnt_sel[l] == segpair_p2) - pnt_sel[l] = mapto[seg_p2]; - } - } - } - - for (int k = 0; k < pnt2_elems.Size(); k++) - { - Element2d & pnt_sel = mesh.SurfaceElement(pnt2_elems[k]); - if(pnt_sel.GetIndex() == mesh[sej].si) - { - for(int l = 0; l < pnt_sel.GetNP(); l++) - { - if(pnt_sel[l] == segpair_p1) - pnt_sel[l] = mapto.Get(seg_p1); - else if (pnt_sel[l] == segpair_p2) - pnt_sel[l] = mapto.Get(seg_p2); - } - } - } - } - // } - } - else - { - // If the code comes here, it indicates that we are at - // a line segment pair which is at the intersection - // of two surfaces, both of which have to grow boundary - // layers.... here too, remapping the segments to the - // new points is required - mesh[sei][0] = mapto.Get(seg_p1); - mesh[sei][1] = mapto.Get(seg_p2); - mesh[sej][1] = mapto.Get(seg_p1); - mesh[sej][0] = mapto.Get(seg_p2); - } - } - } - } - } - - // Add prismatic cells at the boundaries - cout << "Generating prism boundary layer volume elements...." << endl; - - for (SurfaceElementIndex si = 0; si < nse; si++) - { - Element2d & sel = mesh.SurfaceElement(si); - if(surfid.Contains(sel.GetIndex())) - { - /* - Element el(PRISM); - for (int j = 0; j < sel.GetNP(); j++) - { - // Check (Doublecheck) if the corresponding point has a - // copy available for remapping - if (mapto.Get(sel[j])) - { - // Define the points of the newly added Prism cell - el[j+3] = mapto[sel[j]]; - el[j] = sel[j]; - } - else - { - el[j+3] = sel[j]; - el[j] = sel[j]; - } - } - - el.SetIndex(1); - el.Invert(); - mesh.AddVolumeElement(el); - numprisms++; - */ - // cout << "add element: " << endl; - int classify = 0; - for (int j = 0; j < 3; j++) - if (mapto[sel[j]]) - classify += (1 << j); - - // cout << "classify = " << classify << endl; - - ELEMENT_TYPE types[] = { PRISM, TET, TET, PYRAMID, - TET, PYRAMID, PYRAMID, PRISM }; - int nums[] = { sel[0], sel[1], sel[2], mapto[sel[0]], mapto[sel[1]], mapto[sel[2]] }; - int vertices[][6] = - { - { 0, 1, 2, 0, 1, 2 }, // should not occur - { 0, 2, 1, 3, 0, 0 }, - { 0, 2, 1, 4, 0, 0 }, - { 0, 1, 4, 3, 2, 0 }, - - { 0, 2, 1, 5, 0, 0 }, - { 2, 0, 3, 5, 1, 0 }, - { 1, 2, 5, 4, 0, 0 }, - { 0, 2, 1, 3, 5, 4 } - }; - - Element el(types[classify]); - for (int i = 0; i < 6; i++) - el[i] = nums[vertices[classify][i]]; - if(blp.new_matnrs.Size() > 0) - el.SetIndex(blp.new_matnrs[layer-1]); - else - el.SetIndex(blp.new_matnr); - // cout << "el = " << el << endl; - if (classify != 0) - mesh.AddVolumeElement(el); - } - } - - // Finally switch the point indices of the surface elements - // to the newly added ones - cout << "Transferring boundary layer surface elements to new vertex references...." << endl; - - for (int i = 1; i <= nse; i++) - { - Element2d & sel = mesh.SurfaceElement(i); - if(surfid.Contains(sel.GetIndex())) + // create new FaceDescriptors + for(auto i : Range(1, fd_old+1)) + { + const auto& fd = mesh.GetFaceDescriptor(i); + string name = fd.GetBCName(); + if(blp.surfid.Contains(i)) + { + if(auto isIn = domains.Test(fd.DomainIn()); isIn != domains.Test(fd.DomainOut())) { - for (int j = 1; j <= sel.GetNP(); j++) - { - // Check (Doublecheck) if the corresponding point has a - // copy available for remapping - if (mapto.Get(sel.PNum(j))) - { - // Map the surface elements to the new points - sel.PNum(j) = mapto.Get(sel.PNum(j)); - } - } + int new_si = mesh.GetNFD()+1; + surfacefacs[i] = isIn ? 1. : -1.; + // -1 surf nr is so that curving does not do anything + FaceDescriptor new_fd(-1, isIn ? new_mat_nr : fd.DomainIn(), + isIn ? fd.DomainOut() : new_mat_nr, -1); + new_fd.SetBCProperty(new_si); + mesh.AddFaceDescriptor(new_fd); + si_map[i] = new_si; + mesh.SetBCName(new_si-1, "mapped_" + name); } - } - for (int i = 1; i <= ne; i++) - { - Element & el = mesh.VolumeElement(i); - if(el.GetIndex() != blp.bulk_matnr) + } + } + + // mark points for remapping + for(const auto& sel : mesh.SurfaceElements()) + { + auto n = surfacefacs[sel.GetIndex()] * getSurfaceNormal(sel); + if(n.Length2() != 0.) + { + for(auto pi : sel.PNums()) { - for (int j = 1; j <= el.GetNP(); j++) - { - // Check (Doublecheck) if the corresponding point has a - // copy available for remapping - if (mapto.Get(el.PNum(j))) - { - // Map the surface elements to the new points - el.PNum(j) = mapto.Get(el.PNum(j)); - } - } + auto & np = growthvectors[pi]; + if(np.Length() == 0) { np = n; continue; } + auto npn = np * n; + auto npnp = np * np; + auto nn = n * n; + if(nn-npn*npn/npnp == 0) { np = n; continue; } + np += (nn - npn)/(nn - npn*npn/npnp) * (n - npn/npnp * np); } - } + } + } + // Bit array to keep track of segments already processed + BitArray segs_done(nseg); + segs_done.Clear(); + // map for all segments with same points + // points to pair of SegmentIndex, int + // int is type of other segment, either: + // 0 == adjacent surface grows layer + // 1 == adjacent surface doesn't grow layer, but layer ends on it + // 2 == adjacent surface is interior surface that ends on layer + // 3 == adjacent surface is exterior surface that ends on layer (not allowed yet) + Array>, SegmentIndex> segmap(mesh.GetNSeg()); - - // Lock all the prism points so that the rest of the mesh can be - // optimised without invalidating the entire mesh - // for (PointIndex pi = mesh.Points().Begin(); pi < mesh.Points().End(); pi++) - for (PointIndex pi : mesh.Points().Range()) - { - if(bndnodes.Test(pi)) mesh.AddLockedPoint(pi); - } + // moved segments + Array moved_segs; - // Now, actually pull back the old surface points to create - // the actual boundary layers - cout << "Moving and optimising boundary layer points...." << endl; - - for (int i = 1; i <= np; i++) - { - NgArray vertelems; + // boundaries to project endings to + BitArray project_boundaries(fd_old+1); + BitArray move_boundaries(fd_old+1); + project_boundaries.Clear(); + move_boundaries.Clear(); - if(bndnodes.Test(i)) + Array seg2surfel(mesh.GetNSeg()); + for(auto si : Range(mesh.SurfaceElements())) + { + NgArray surfeledges; + meshtopo.GetSurfaceElementEdges(si+1, surfeledges); + for(auto edgenr : surfeledges) + for(auto sei : Range(mesh.LineSegments())) + if(meshtopo.GetEdge(sei)+1 == edgenr && + mesh[sei].si == mesh[si].GetIndex()) + seg2surfel[sei] = si; + } + + for(auto si : Range(mesh.LineSegments())) + { + if(segs_done[si]) continue; + const auto& segi = mesh[si]; + if(si_map[segi.si] == -1) continue; + segs_done.SetBit(si); + segmap[si].Append(make_pair(si, 0)); + moved_segs.Append(si); + for(auto sj : Range(mesh.LineSegments())) + { + if(segs_done.Test(sj)) continue; + const auto& segj = mesh[sj]; + if((segi[0] == segj[0] && segi[1] == segj[1]) || + (segi[0] == segj[1] && segi[1] == segj[0])) { - MeshPoint pointtomove; - - pointtomove = mesh.Point(i); - - if(layer == prismlayers) + segs_done.SetBit(sj); + int type; + if(si_map[segj.si] != -1) + type = 0; + else if(const auto& fd = mesh.GetFaceDescriptor(segj.si); domains.Test(fd.DomainIn()) && domains.Test(fd.DomainOut())) { - mesh.Point(i).SetPoint(pointtomove + layerht * growthvectors.Elem(i)); - - meshtopo.GetVertexElements(i,vertelems); - - for(int j = 1; j <= vertelems.Size(); j++) - { - // double sfact = 0.9; - Element volel = mesh.VolumeElement(vertelems.Elem(j)); - if(((volel.GetType() == TET) || (volel.GetType() == TET10)) && (!volel.IsDeleted())) - { - //while((volel.Volume(mesh.Points()) <= 0.0) && (sfact >= 0.0)) - //{ - // mesh.Point(i).SetPoint(pointtomove + (sfact * layerht * growthvectors.Elem(i))); - // mesh.ImproveMesh(); - - // // Try to move the point back by one step but - // // if the volume drops to below zero, double back - // mesh.Point(i).SetPoint(pointtomove + ((sfact + 0.1) * layerht * growthvectors.Elem(i))); - // if(volel.Volume(mesh.Points()) <= 0.0) - // { - // mesh.Point(i).SetPoint(pointtomove + (sfact * layerht * growthvectors.Elem(i))); - // } - // sfact -= 0.1; - //} - volel.Delete(); - } - } - } + type = 2; + if(fd.DomainIn() == 0 || fd.DomainOut() == 0) + project_boundaries.SetBit(segj.si); + } + else if(const auto& fd = mesh.GetFaceDescriptor(segj.si); !domains.Test(fd.DomainIn()) && !domains.Test(fd.DomainOut())) + { + type = 3; + if(fd.DomainIn() == 0 || fd.DomainOut() == 0) + project_boundaries.SetBit(segj.si); + move_boundaries.SetBit(segj.si); + } else { - mesh.Point(i).SetPoint(pointtomove + layerht * growthvectors.Elem(i)); + type = 1; + // in case 1 we project the growthvector onto the surface + project_boundaries.SetBit(segj.si); + } + segmap[si].Append(make_pair(sj, type)); + } + } + } + + BitArray in_surface_direction(fd_old+1); + in_surface_direction.Clear(); + + // project growthvector on surface for inner angles + if(blp.grow_edges) + { + for(const auto& sel : mesh.SurfaceElements()) + if(project_boundaries.Test(sel.GetIndex())) + { + auto n = getSurfaceNormal(sel); + for(auto i : Range(sel.PNums())) + { + auto pi = sel.PNums()[i]; + if(growthvectors[pi].Length2() == 0.) + continue; + auto next = sel.PNums()[(i+1)%sel.GetNV()]; + auto prev = sel.PNums()[i == 0 ? sel.GetNV()-1 : i-1]; + auto v1 = (mesh[next] - mesh[pi]).Normalize(); + auto v2 = (mesh[prev] - mesh[pi]).Normalize(); + auto v3 = growthvectors[pi]; + v3.Normalize(); + if((v1 * v3 > 1e-12) || (v2 * v3 > 1e-12)) + in_surface_direction.SetBit(sel.GetIndex()); + + auto& g = growthvectors[pi]; + auto ng = n * g; + auto gg = g * g; + auto nn = n * n; + // if(fabs(ng*ng-nn*gg) < 1e-12 || fabs(ng) < 1e-12) continue; + auto a = -ng*ng/(ng*ng-nn * gg); + auto b = ng*gg/(ng*ng-nn*gg); + g += a*g + b*n; + } + } + } + else + { + for(const auto& seg : mesh.LineSegments()) + { + int count = 0; + for(const auto& seg2 : mesh.LineSegments()) + if(((seg[0] == seg2[0] && seg[1] == seg2[1]) || (seg[0] == seg2[1] && seg[1] == seg2[0])) && blp.surfid.Contains(seg2.si)) + count++; + if(count == 1) + { + growthvectors[seg[0]] = {0., 0., 0.}; + growthvectors[seg[1]] = {0., 0., 0.}; + } + } + } + + // insert new points + for (PointIndex pi = 1; pi <= np; pi++) + if (growthvectors[pi].Length2() != 0) + { + Point<3> p = mesh[pi]; + for(auto i : Range(blp.heights)) + { + p += blp.heights[i] * growthvectors[pi]; + mapto[pi].Append(mesh.AddPoint(p)); + } + } + + // add 2d quads on required surfaces + map, int> seg2edge; + if(blp.grow_edges) + { + for(auto sei : moved_segs) + { + // copy here since we will add segments and this would + // invalidate a reference! + auto segi = mesh[sei]; + for(auto [sej, type] : segmap[sei]) + { + auto segj = mesh[sej]; + if(type == 0) + { + Segment s; + s[0] = mapto[segj[0]].Last(); + s[1] = mapto[segj[1]].Last(); + s[2] = PointIndex::INVALID; + auto pair = s[0] < s[1] ? make_pair(s[0], s[1]) : make_pair(s[1], s[0]); + if(seg2edge.find(pair) == seg2edge.end()) + seg2edge[pair] = ++max_edge_nr; + s.edgenr = seg2edge[pair]; + s.si = si_map[segj.si]; + mesh.AddSegment(s); + } + // here we need to grow the quad elements + else if(type == 1) + { + PointIndex pp1 = segj[1]; + PointIndex pp2 = segj[0]; + if(in_surface_direction.Test(segj.si)) + { + Swap(pp1, pp2); + move_boundaries.SetBit(segj.si); + } + PointIndex p1 = pp1; + PointIndex p2 = pp2; + PointIndex p3, p4; + Segment s0; + s0[0] = p1; + s0[1] = p2; + s0[2] = PointIndex::INVALID; + s0.edgenr = segj.edgenr; + s0.si = segj.si; + mesh.AddSegment(s0); + + for(auto i : Range(blp.heights)) + { + Element2d sel(QUAD); + p3 = mapto[pp2][i]; + p4 = mapto[pp1][i]; + sel[0] = p1; + sel[1] = p2; + sel[2] = p3; + sel[3] = p4; + sel.SetIndex(segj.si); + mesh.AddSurfaceElement(sel); + + // TODO: Too many, would be enough to only add outermost ones + Segment s1; + s1[0] = p2; + s1[1] = p3; + s1[2] = PointIndex::INVALID; + auto pair = make_pair(p2, p3); + if(seg2edge.find(pair) == seg2edge.end()) + seg2edge[pair] = ++max_edge_nr; + s1.edgenr = seg2edge[pair]; + s1.si = segj.si; + mesh.AddSegment(s1); + Segment s2; + s2[0] = p4; + s2[1] = p1; + s2[2] = PointIndex::INVALID; + pair = make_pair(p1, p4); + if(seg2edge.find(pair) == seg2edge.end()) + seg2edge[pair] = ++max_edge_nr; + s2.edgenr = seg2edge[pair]; + s2.si = segj.si; + mesh.AddSegment(s2); + p1 = p4; + p2 = p3; + } + Segment s3; + s3[0] = p3; + s3[1] = p4; + s3[2] = PointIndex::INVALID; + auto pair = p3 < p4 ? make_pair(p3, p4) : make_pair(p4, p3); + if(seg2edge.find(pair) == seg2edge.end()) + seg2edge[pair] = ++max_edge_nr; + s3.edgenr = seg2edge[pair]; + s3.si = segj.si; + mesh.AddSegment(s3); } } - } - mesh.Compress(); + } } - - // Optimise the tet part of the volume mesh after all the modifications - // to the system are completed - //OptimizeVolume(mparam,mesh); - cout << "New NP: " << mesh.GetNP() << endl; - cout << "Num of Quads: " << numquads << endl; - cout << "Num of Prisms: " << numprisms << endl; - cout << "Boundary Layer Generation....Done!" << endl; + BitArray fixed_points(np+1); + fixed_points.Clear(); + BitArray moveboundarypoint(np+1); + moveboundarypoint.Clear(); + for(SurfaceElementIndex si = 0; si < nse; si++) + { + // copy because surfaceels array will be resized! + auto sel = mesh[si]; + if(si_map[sel.GetIndex()] != -1) + { + Array points(sel.PNums()); + if(surfacefacs[sel.GetIndex()] > 0) Swap(points[0], points[2]); + for(auto j : Range(blp.heights)) + { + auto eltype = points.Size() == 3 ? PRISM : HEX; + Element el(eltype); + for(auto i : Range(points)) + el[i] = points[i]; + for(auto i : Range(points)) + points[i] = mapto[sel.PNums()[i]][j]; + if(surfacefacs[sel.GetIndex()] > 0) Swap(points[0], points[2]); + for(auto i : Range(points)) + el[sel.PNums().Size() + i] = points[i]; + el.SetIndex(new_mat_nr); + mesh.AddVolumeElement(el); + } + Element2d newel = sel; + for(auto& p : newel.PNums()) + p = mapto[p].Last(); + newel.SetIndex(si_map[sel.GetIndex()]); + mesh.AddSurfaceElement(newel); + } + else + { + bool has_moved = false; + for(auto p : sel.PNums()) + if(mapto[p].Size()) + has_moved = true; + if(has_moved) + for(auto p : sel.PNums()) + { + if(!mapto[p].Size()) + { + fixed_points.SetBit(p); + if(move_boundaries.Test(sel.GetIndex())) + moveboundarypoint.SetBit(p); + } + } + } + if(move_boundaries.Test(sel.GetIndex())) + { + for(auto& p : mesh[si].PNums()) + if(mapto[p].Size()) + p = mapto[p].Last(); + } + } - dbg.close(); + for(SegmentIndex sei = 0; sei < nseg; sei++) + { + auto& seg = mesh[sei]; + if(move_boundaries.Test(seg.si)) + for(auto& p : seg.PNums()) + if(mapto[p].Size()) + p = mapto[p].Last(); + } + + for(ElementIndex ei = 0; ei < ne; ei++) + { + auto el = mesh[ei]; + ArrayMem fixed; + ArrayMem moved; + bool moved_bnd = false; + for(const auto& p : el.PNums()) + { + if(fixed_points.Test(p)) + fixed.Append(p); + if(mapto[p].Size()) + moved.Append(p); + if(moveboundarypoint.Test(p)) + moved_bnd = true; + } + + bool do_move, do_insert; + if(domains.Test(el.GetIndex())) + { + do_move = fixed.Size() && moved_bnd; + do_insert = do_move; + } + else + { + do_move = !fixed.Size() || moved_bnd; + do_insert = !do_move; + } + + if(do_move) + { + for(auto& p : mesh[ei].PNums()) + if(mapto[p].Size()) + p = mapto[p].Last(); + } + if(do_insert) + { + if(el.GetType() == TET) + { + if(moved.Size() == 3) // inner corner + { + PointIndex p1 = moved[0]; + PointIndex p2 = moved[1]; + PointIndex p3 = moved[2]; + auto v1 = mesh[p1]; + auto n = Cross(mesh[p2]-v1, mesh[p3]-v1); + auto d = mesh[mapto[p1][0]] - v1; + if(n*d > 0) + Swap(p2,p3); + PointIndex p4 = p1; + PointIndex p5 = p2; + PointIndex p6 = p3; + for(auto i : Range(blp.heights)) + { + Element nel(PRISM); + nel[0] = p4; nel[1] = p5; nel[2] = p6; + p4 = mapto[p1][i]; p5 = mapto[p2][i]; p6 = mapto[p3][i]; + nel[3] = p4; nel[4] = p5; nel[5] = p6; + nel.SetIndex(el.GetIndex()); + mesh.AddVolumeElement(nel); + } + } + if(moved.Size() == 2) + { + if(fixed.Size() == 1) + { + PointIndex p1 = moved[0]; + PointIndex p2 = moved[1]; + for(auto i : Range(blp.heights)) + { + PointIndex p3 = mapto[moved[1]][i]; + PointIndex p4 = mapto[moved[0]][i]; + Element nel(PYRAMID); + nel[0] = p1; + nel[1] = p2; + nel[2] = p3; + nel[3] = p4; + nel[4] = el[0] + el[1] + el[2] + el[3] - fixed[0] - moved[0] - moved[1]; + if(Cross(mesh[p2]-mesh[p1], mesh[p4]-mesh[p1]) * (mesh[nel[4]]-mesh[nel[1]]) > 0) + Swap(nel[1], nel[3]); + nel.SetIndex(el.GetIndex()); + mesh.AddVolumeElement(nel); + p1 = p4; + p2 = p3; + } + } + } + if(moved.Size() == 1 && fixed.Size() == 1) + { + PointIndex p1 = moved[0]; + for(auto i : Range(blp.heights)) + { + Element nel = el; + PointIndex p2 = mapto[moved[0]][i]; + for(auto& p : nel.PNums()) + { + if(p == moved[0]) + p = p1; + else if(p == fixed[0]) + p = p2; + } + p1 = p2; + mesh.AddVolumeElement(nel); + } + } + } + else if(el.GetType() == PYRAMID) + { + if(moved.Size() == 2) + { + if(fixed.Size() != 2) + throw Exception("This case is not implemented yet! Fixed size = " + ToString(fixed.Size())); + PointIndex p1 = moved[0]; + PointIndex p2 = moved[1]; + for(auto i : Range(blp.heights)) + { + PointIndex p3 = mapto[moved[1]][i]; + PointIndex p4 = mapto[moved[0]][i]; + Element nel(PYRAMID); + nel[0] = p1; + nel[1] = p2; + nel[2] = p3; + nel[3] = p4; + nel[4] = el[0] + el[1] + el[2] + el[3] + el[4] - fixed[0] - fixed[1] - moved[0] - moved[1]; + if(Cross(mesh[p2] - mesh[p1], mesh[p4]-mesh[p1]) * (mesh[nel[4]]-mesh[nel[1]]) > 0) + Swap(nel[1], nel[3]); + nel.SetIndex(el.GetIndex()); + mesh.AddVolumeElement(nel); + p1 = p4; + p2 = p3; + } + } + else if(moved.Size() == 1) + throw Exception("This case is not implemented yet!"); + } + else + throw Exception("Boundarylayer only implemented for tets and pyramids outside yet!"); + } + } + + for(auto i : Range(1, fd_old+1)) + if(si_map[i] != -1) + { + if(mesh.GetFaceDescriptor(mesh.GetNFD()).DomainIn() == new_mat_nr) + mesh.GetFaceDescriptor(i).SetDomainOut(new_mat_nr); + else + mesh.GetFaceDescriptor(i).SetDomainIn(new_mat_nr); + } + } + + void AddDirection( Vec<3> & a, Vec<3> b ) + { + if(a.Length2()==0.) + { + a = b; + return; + } + + if(b.Length2()==0.) + return; + + auto ab = a * b; + if(fabs(ab)>1-1e-8) + return; + + Mat<2> m; + m(0,0) = a[0]; + m(0,1) = a[1]; + m(1,0) = b[0]; + m(1,1) = b[1]; + Vec<2> lam; + Vec<2> rhs; + rhs[0] = a[0]-b[0]; + rhs[1] = a[1]-b[1]; + + const auto Dot = [](Vec<3> a, Vec<3> b) + { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; }; + + rhs[0] = Dot(a,a); + rhs[1] = Dot(b,b); + + m.Solve(rhs, lam); + a[0] = lam[0]; + a[1] = lam[1]; + a[2] = 0.0; + return; + } + + static void Generate2dMesh( Mesh & mesh, int domain ) + { + Box<3> box{Box<3>::EMPTY_BOX}; + for(const auto & seg : mesh.LineSegments()) + if (seg.si == domain) + for (auto pi : {seg[0], seg[1]}) + box.Add(mesh[pi]); + + MeshingParameters mp; + Meshing2 meshing (*mesh.GetGeometry(), mp, box); + + Array compress(mesh.GetNP()); + compress = PointIndex::INVALID; + + PointIndex cnt = PointIndex::BASE; + for(const auto & seg : mesh.LineSegments()) + if (seg.si == domain) + for (auto pi : {seg[0], seg[1]}) + if (compress[pi]==PointIndex{PointIndex::INVALID}) + { + meshing.AddPoint(mesh[pi], pi); + compress[pi] = cnt++; + } + + PointGeomInfo gi; + gi.trignum = domain; + for(const auto & seg : mesh.LineSegments()) + if (seg.si == domain) + meshing.AddBoundaryElement (compress[seg[0]], compress[seg[1]], gi, gi); + + auto oldnf = mesh.GetNSE(); + auto res = meshing.GenerateMesh (mesh, mp, mp.maxh, domain); + for (SurfaceElementIndex sei : Range(oldnf, mesh.GetNSE())) + mesh[sei].SetIndex (domain); + + int hsteps = mp.optsteps2d; + + const char * optstr = mp.optimize2d.c_str(); + MeshOptimize2d meshopt(mesh); + meshopt.SetFaceIndex(domain); + meshopt.SetMetricWeight (mp.elsizeweight); + for (size_t j = 1; j <= strlen(optstr); j++) + { + switch (optstr[j-1]) + { + case 's': + { // topological swap + meshopt.EdgeSwapping (0); + break; + } + case 'S': + { // metric swap + meshopt.EdgeSwapping (1); + break; + } + case 'm': + { + meshopt.ImproveMesh(mp); + break; + } + case 'c': + { + meshopt.CombineImprove(); + break; + } + default: + cerr << "Optimization code " << optstr[j-1] << " not defined" << endl; + } + } + + mesh.Compress(); + mesh.OrderElements(); + mesh.SetNextMajorTimeStamp(); + + } + + int GenerateBoundaryLayer2 (Mesh & mesh, int domain, const Array & thicknesses, bool should_make_new_domain, const Array & boundaries) + { + SegmentIndex first_new_seg = mesh.LineSegments().Range().Next(); + + int np = mesh.GetNP(); + int nseg = mesh.GetNSeg(); + int ne = mesh.GetNSE(); + mesh.UpdateTopology(); + + double total_thickness = 0.0; + for(auto thickness : thicknesses) + total_thickness += thickness; + + Array, PointIndex> mapto(np); + + // Bit array to keep track of segments already processed + BitArray segs_done(nseg); + segs_done.Clear(); + + // moved segments + Array moved_segs; + + Array, PointIndex> growthvectors(np); + growthvectors = 0.; + + auto & meshtopo = mesh.GetTopology(); + + Array seg2surfel(mesh.GetNSeg()); + seg2surfel = -1; + NgArray temp_els; + for(auto si : Range(mesh.LineSegments())) + { + meshtopo.GetSegmentSurfaceElements ( si+1, temp_els ); + // NgArray surfeledges; + // meshtopo.GetSurfaceElementEdges(si+1, surfeledges); + for(auto seli : temp_els) + if(mesh[seli].GetIndex() == mesh[si].si) + seg2surfel[si] = seli; + } + + Array segments; + + // surface index map + Array si_map(mesh.GetNFD()+1); + si_map = -1; + + int fd_old = mesh.GetNFD(); + + int max_edge_nr = -1; + int max_domain = -1; + + for(const auto& seg : mesh.LineSegments()) + { + if(seg.epgeominfo[0].edgenr > max_edge_nr) + max_edge_nr = seg.epgeominfo[0].edgenr; + if(seg.si > max_domain) + max_domain = seg.si; + } + + int new_domain = max_domain+1; + + BitArray active_boundaries(max_edge_nr+1); + BitArray active_segments(nseg); + active_boundaries.Clear(); + active_segments.Clear(); + + if(boundaries.Size() == 0) + active_boundaries.Set(); + else + for(auto edgenr : boundaries) + active_boundaries.SetBit(edgenr); + + for(auto segi : Range(mesh.LineSegments())) + { + const auto seg = mesh[segi]; + if(active_boundaries.Test(seg.epgeominfo[0].edgenr) && seg.si==domain) + active_segments.SetBit(segi); + } + + for(auto segi : Range(mesh.LineSegments())) + { + const auto& seg = mesh[segi]; + auto si = seg.si; + + if(si_map[si]!=-1) + continue; + + if(!active_segments.Test(segi)) + continue; + + FaceDescriptor new_fd(0, 0, 0, -1); + new_fd.SetBCProperty(new_domain); + int new_fd_index = mesh.AddFaceDescriptor(new_fd); + si_map[si] = new_domain; + if(should_make_new_domain) + mesh.SetBCName(new_domain-1, "mapped_" + mesh.GetBCName(si-1)); + } + + for(auto si : Range(mesh.LineSegments())) + { + if(segs_done[si]) continue; + segs_done.SetBit(si); + const auto& segi = mesh[si]; + if(si_map[segi.si] == -1) continue; + if(!active_boundaries.Test(segi.epgeominfo[0].edgenr)) + continue; + moved_segs.Append(si); + } + + // calculate growth vectors (average normal vectors of adjacent segments at each point) + for (auto si : moved_segs) + { + auto & seg = mesh[si]; + + meshtopo.GetSegmentSurfaceElements ( si+1, temp_els ); + ArrayMem seg_domains; + + temp_els.SetSize(0); + if(seg2surfel[si]!=-1) + temp_els.Append(seg2surfel[si]); + + int n_temp_els = temp_els.Size(); + if(n_temp_els==0) + continue; + + int dom0 = mesh[temp_els[0]].GetIndex(); + int dom1 = n_temp_els==2 ? mesh[temp_els[1]].GetIndex() : 0; + + bool in_dom0 = dom0 == domain; + bool in_dom1 = dom1 == domain; + + if(!in_dom0 && !in_dom1) + continue; + + int side = in_dom0 ? 0 : 1; + + auto & sel = mesh[ temp_els[side] ]; + + int domain = sel.GetIndex(); + Vec<3> pcenter = 0.0; + for(auto i : IntRange(sel.GetNP())) + { + for(auto d : IntRange(3)) + pcenter[d] += mesh[sel[i]][d]; + } + pcenter = 1.0/sel.GetNP() * pcenter; + + auto n = mesh[seg[1]] - mesh[seg[0]]; + n = {-n[1], n[0], 0}; + n.Normalize(); + + Vec<3> p0{mesh[seg[0]]}; + Vec<3> p1{mesh[seg[0]]}; + + + auto v = pcenter -0.5*(p0+p1); + + const auto Dot = [](Vec<3> a, Vec<3> b) + { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; }; + if(Dot(n, v)<0) + n = -1*n; + + AddDirection(growthvectors[seg[0]], n); + AddDirection(growthvectors[seg[1]], n); + } + + ////////////////////////////////////////////////////////////////////////// + // average growthvectors along straight lines to avoid overlaps in corners + BitArray points_done(np+1); + points_done.Clear(); + + for(auto si : moved_segs) + { + auto current_seg = mesh[si]; + auto current_si = si; + + auto first = current_seg[0]; + auto current = -1; + auto next = current_seg[1]; + + if(points_done.Test(first)) + continue; + + Array chain; + chain.Append(first); + + // first find closed loops of segments + while(next != current && next != first) + { + current = next; + points_done.SetBit(current); + chain.Append(current); + for(auto sj : meshtopo.GetVertexSegments( current )) + { + if(!active_segments.Test(sj)) + continue; + + if(sj!=current_si) + { + current_si = sj; + current_seg = mesh[sj]; + + next = current_seg[0] + current_seg[1] - current; + break; + } + } + } + + auto ifirst = 0; + auto n = chain.Size(); + + // angle of adjacent segments at points a[i-1], a[i], a[i+1] + auto getAngle = [&mesh, &growthvectors] (FlatArray a, size_t i) + { + auto n = a.Size(); + auto v0 = growthvectors[a[(i+n-1)%n]]; + auto v1 = growthvectors[a[i]]; + auto v2 = growthvectors[a[(i+1)%n]]; + + auto p0 = mesh[a[(i+n-1)%n]]; + auto p1 = mesh[a[i]]; + auto p2 = mesh[a[(i+1)%n]]; + + v0 = p1-p0; + v1 = p2-p1; + + auto angle = abs(atan2(v1[0], v1[1]) - atan2(v0[0], v0[1])); + if(angle>M_PI) + angle = 2*M_PI-angle; + + return angle; + }; + + // find first corner point + while(getAngle(chain, ifirst) < 1e-5 ) + ifirst = (ifirst+1)%n; + + // Copy points of closed loop in correct order, starting with a corner + Array pis(n+1); + pis.Range(0, n-ifirst) = chain.Range(ifirst, n); + pis.Range(n-ifirst, n) = chain.Range(0, n-ifirst); + pis[n] = pis[0]; + + Array lengths(n); + + for(auto i : Range(n)) + lengths[i] = (mesh[pis[(i+1)%n]] - mesh[pis[i]]).Length(); + + auto averageGrowthVectors = [&] (size_t first, size_t last) + { + if(first+1 >= last) + return; + + double total_len = 0.0; + for(auto l : lengths.Range(first, last)) + total_len += l; + + double len = lengths[first]; + auto v0 = growthvectors[pis[first]]; + auto v1 = growthvectors[pis[last]]; + + for(auto i : Range(first+1, last)) + { + auto pi = pis[i]; + growthvectors[pi] = (len/total_len)*v1 + (1.0-len/total_len)*v0; + len += lengths[i]; + } + }; + + auto icurrent = 0; + + while(icurrent average growth vectors between end points + if(icurrent!=ilast) + averageGrowthVectors(icurrent, ilast); + + icurrent = ilast; + } + } + + ////////////////////////////////////////////////////////////////////// + // reduce growthvectors where necessary to avoid overlaps/slim regions + const auto getSegmentBox = [&] (SegmentIndex segi) + { + PointIndex pi0=mesh[segi][0], pi1=mesh[segi][1]; + Box<3> box( mesh[pi0], mesh[pi1] ); + box.Add( mesh[pi0]+growthvectors[pi0] ); + box.Add( mesh[pi1]+growthvectors[pi1] ); + return box; + }; + + Array growth(np); + growth = 1.0; + + const auto Dot = [](auto a, auto b) + { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; }; + + const auto restrictGrowthVectors = [&] (SegmentIndex segi0, SegmentIndex segi1) + { + if(!active_segments.Test(segi0)) + return; + + const auto & seg0 = mesh[segi0]; + const auto & seg1 = mesh[segi1]; + + if(seg0.si != seg1.si) + return; + + if(segi0 == segi1) + return; + + if(seg0[0]==seg1[0] || seg0[0]==seg1[1] || seg0[1]==seg1[0] || seg0[1] == seg1[1]) + return; + + auto n = mesh[seg0[0]] - mesh[seg0[1]]; + n = {-n[1], n[0], 0}; + n.Normalize(); + if(Dot(n, growthvectors[seg0[0]])<0) n = -n; + if(Dot(n, growthvectors[seg0[1]])<0) n = -n; + + auto n1 = mesh[seg1[0]] - mesh[seg1[1]]; + n1 = {-n1[1], n1[0], 0}; + n1.Normalize(); + if(Dot(n1, growthvectors[seg1[0]])<0) n1 = -n; + if(Dot(n1, growthvectors[seg1[1]])<0) n1 = -n; + + auto p10 = mesh[seg1[0]]; + auto p11 = mesh[seg1[1]]; + + for ( auto pi : {seg0[0], seg0[1]} ) + { + if(growthvectors[pi] == 0.0) + continue; + + PointIndex pi1 = seg0[0] + seg0[1] - pi; + auto p1 = mesh[pi1]; + auto p = mesh[pi]; + + Point<3> points[] = { p10, p11, p10+total_thickness*growthvectors[seg1[0]], p11+total_thickness*growthvectors[seg1[1]], p1+total_thickness*growthvectors[pi1] }; + + Vec<3> gn{ growthvectors[pi][1], -growthvectors[pi][0], 0.0 }; + if(Dot(gn, p1-p) < 0) + gn = -gn; + + double d0 = Dot(gn, p); + double d1 = Dot(gn, p1); + if(d0>d1) + Swap(d0,d1); + + bool all_left=true, all_right=true; + + for (auto i: Range(4)) + { + auto p_other = points[i]; + auto dot = Dot(gn,p_other); + if(dot>d0) all_left = false; + if(dot points[] = { p10, p10+t*growthvectors[seg1[0]], p11, p11+t*growthvectors[seg1[1]] }; + auto p0 = mesh[pi]; + auto p1 = p0 + t*growthvectors[pi]; + auto P2 = [](Point<3> p) { return Point<2>{p[0], p[1]}; }; + ArrayMem, 4> intersections; + + double alpha, beta; + + if(X_INTERSECTION == intersect( P2(p0), P2(p1), P2(points[0]), P2(points[2]), alpha, beta )) + intersections.Append({alpha, 0.0}); + + if(X_INTERSECTION == intersect( P2(p0), P2(p1), P2(points[1]), P2(points[3]), alpha, beta )) + intersections.Append({alpha, 1.0}); + + if(X_INTERSECTION == intersect( P2(p0), P2(p1), P2(points[0]), P2(points[1]), alpha, beta )) + intersections.Append({alpha, beta}); + + if(X_INTERSECTION == intersect( P2(p0), P2(p1), P2(points[2]), P2(points[3]), alpha, beta )) + intersections.Append({alpha, beta}); + + QuickSort(intersections); + for(auto [alpha,beta] : intersections) + { + if(!active_segments.Test(segi1)) + growth[pi] = min(growth[pi], alpha); + else + { + double mean = 0.5*(alpha+beta); + growth[pi] = min(growth[pi], mean); + growth[seg1[0]] = min(growth[seg1[0]], mean); + growth[seg1[1]] = min(growth[seg1[1]], mean); + } + } + } + } + }; + + Box<3> box(Box<3>::EMPTY_BOX); + for (auto segi : Range(mesh.LineSegments())) + { + auto segbox = getSegmentBox( segi ); + box.Add(segbox.PMin()); + box.Add(segbox.PMax()); + } + BoxTree<3> segtree(box); + + for (auto segi : Range(mesh.LineSegments())) + { + auto p2 = [](Point<3> p) { return Point<2>{p[0], p[1]}; }; + + auto seg = mesh[segi]; + double alpha,beta; + intersect( p2(mesh[seg[0]]), p2(mesh[seg[0]]+total_thickness*growthvectors[seg[0]]), p2(mesh[seg[1]]), p2(mesh[seg[1]]+total_thickness*growthvectors[seg[1]]), alpha, beta ); + + if(beta>0 && alpha>0 && alpha<1.1) + growth[seg[0]] = min(growth[seg[0]], 0.8*alpha); + if(alpha>0 && beta>0 && beta<1.1) + growth[seg[1]] = min(growth[seg[1]], 0.8*beta); + + for (auto segj : Range(mesh.LineSegments())) + if(segi!=segj) + restrictGrowthVectors(segi, segj); + } + + for( auto pi : Range(growthvectors)) + growthvectors[pi] *= growth[pi]; + + + // insert new points + for(PointIndex pi : Range(mesh.Points())) + if(growthvectors[pi].Length2()!=0) + { + + auto & pnew = mapto[pi]; + auto dist = 0.0; + for(auto t : thicknesses) + { + dist+=t; + pnew.Append( mesh.AddPoint( mesh[pi] + dist*growthvectors[pi] ) ); + mesh[pnew.Last()].SetType(FIXEDPOINT); + } + } + + map, int> seg2edge; + + // insert new elements ( and move old ones ) + for(auto si : moved_segs) + { + auto seg = mesh[si]; + + bool swap = false; + auto & pm0 = mapto[seg[0]]; + auto & pm1 = mapto[seg[1]]; + + auto newindex = si_map[seg.si]; + + Segment s = seg; + s.geominfo[0] = {}; + s.geominfo[1] = {}; + s[0] = pm0.Last(); + s[1] = pm1.Last(); + s[2] = PointIndex::INVALID; + auto pair = s[0] < s[1] ? make_pair(s[0], s[1]) : make_pair(s[1], s[0]); + if(seg2edge.find(pair) == seg2edge.end()) + seg2edge[pair] = ++max_edge_nr; + s.edgenr = seg2edge[pair]; + s.si = seg.si; + mesh.AddSegment(s); + + Swap(s[0], s[1]); + s.si = newindex; + mesh.AddSegment(s); + + for ( auto i : Range(thicknesses)) + { + PointIndex pi0, pi1, pi2, pi3; + + if(i==0) + { + pi0 = seg[0]; + pi1 = seg[1]; + } + else + { + pi0 = pm0[i-1]; + pi1 = pm1[i-1]; + } + + pi2 = pm1[i]; + pi3 = pm0[i]; + + if(i==0) + { + auto p0 = mesh[pi0]; + auto p1 = mesh[pi1]; + auto q0 = mesh[pi2]; + auto q1 = mesh[pi3]; + + Vec<2> n = {-p1[1]+p0[1], p1[0]-p0[0]}; + Vec<2> v = { q0[0]-p0[0], q0[1]-p0[1]}; + if(n[0]*v[0]+n[1]*v[1]<0) + swap = true; + } + + Element2d newel; + newel.SetType(QUAD); + newel[0] = pi0; + newel[1] = pi1; + newel[2] = pi2; + newel[3] = pi3; + newel.SetIndex(si_map[seg.si]); + newel.GeomInfo() = PointGeomInfo{}; + +// if(swap) +// { +// Swap(newel[0], newel[1]); +// Swap(newel[2], newel[3]); +// } + + mesh.AddSurfaceElement(newel); + + } + // segment now adjacent to new 2d-domain! + mesh[si].si = si_map[seg.si]; + } + + for(auto pi : Range(mapto)) + { + if(mapto[pi].Size() == 0) + continue; + auto pnew = mapto[pi].Last(); + NgArray old_els; + meshtopo.GetVertexSurfaceElements( pi, old_els); + for(auto old_sei : old_els) + { + if(mesh[old_sei].GetIndex() == domain) + { + auto & old_el = mesh[old_sei]; + for(auto i : IntRange(old_el.GetNP())) + if(old_el[i]==pi) + old_el[i] = pnew; + } + } + } + + for(auto & sel : mesh.SurfaceElements()) + if(sel.GetIndex() == domain) + sel.Delete(); + + mesh.Compress(); + mesh.CalcSurfacesOfNode(); + + Generate2dMesh(mesh, domain); + + // even without new domain, we need temporarily a new domain to mesh the remaining area, without confusing the meshes with quads -> add segments temporarily and reset domain number and segments afterwards + if(!should_make_new_domain) + { + // map new domain back to old one + for(auto & sel : mesh.SurfaceElements()) + if(sel.GetIndex()==new_domain) + sel.SetIndex(domain); + + // remove (temporary) inner segments + for(auto segi : Range(first_new_seg, mesh.LineSegments().Range().Next())) + { + mesh[segi][0].Invalidate(); + mesh[segi][1].Invalidate(); + } + + for(auto segi : moved_segs) + mesh[segi].si = domain; + + mesh.Compress(); + mesh.CalcSurfacesOfNode(); + } + + return new_domain; } } - diff --git a/libsrc/meshing/boundarylayer.hpp b/libsrc/meshing/boundarylayer.hpp index a174a55a..4d61276b 100644 --- a/libsrc/meshing/boundarylayer.hpp +++ b/libsrc/meshing/boundarylayer.hpp @@ -12,18 +12,18 @@ class BoundaryLayerParameters { public: // parameters by Philippose .. - NgArray surfid; - NgArray heights; - NgArray new_matnrs; - int prismlayers = 1; - int bulk_matnr = 1; - int new_matnr = 1; - double hfirst = 0.01; - double growthfactor = 1; - bool optimize = true; + Array surfid; + Array heights; + string new_mat; + BitArray domains; + bool outside = false; // set the boundary layer on the outside + bool grow_edges = false; + Array project_boundaries; }; -DLL_HEADER extern void GenerateBoundaryLayer (Mesh & mesh, BoundaryLayerParameters & blp); +DLL_HEADER void GenerateBoundaryLayer (Mesh & mesh, + const BoundaryLayerParameters & blp); +DLL_HEADER int /* new_domain_number */ GenerateBoundaryLayer2 (Mesh & mesh, int domain, const Array & thicknesses, bool should_make_new_domain=true, const Array & boundaries=Array{}); #endif diff --git a/libsrc/meshing/clusters.cpp b/libsrc/meshing/clusters.cpp index dafe81f8..dae820df 100644 --- a/libsrc/meshing/clusters.cpp +++ b/libsrc/meshing/clusters.cpp @@ -16,7 +16,7 @@ namespace netgen ; } - void AnisotropicClusters :: Update(TaskManager tm, Tracer tracer) + void AnisotropicClusters :: Update(NgTaskManager tm, NgTracer tracer) { static int timer = NgProfiler::CreateTimer ("clusters"); // static int timer1 = NgProfiler::CreateTimer ("clusters1"); diff --git a/libsrc/meshing/clusters.hpp b/libsrc/meshing/clusters.hpp index bf9a5a56..21e122fb 100644 --- a/libsrc/meshing/clusters.hpp +++ b/libsrc/meshing/clusters.hpp @@ -27,7 +27,7 @@ public: AnisotropicClusters (const Mesh & amesh); ~AnisotropicClusters(); - void Update(TaskManager tm = &DummyTaskManager, Tracer trace = &DummyTracer); + void Update(NgTaskManager tm = &DummyTaskManager, NgTracer trace = &DummyTracer); int GetVertexRepresentant (int vnr) const { return cluster_reps.Get(vnr); } diff --git a/libsrc/meshing/curvedelems.cpp b/libsrc/meshing/curvedelems.cpp index 6523c056..70031bdd 100644 --- a/libsrc/meshing/curvedelems.cpp +++ b/libsrc/meshing/curvedelems.cpp @@ -560,21 +560,13 @@ namespace netgen ishighorder = 0; order = 1; - // MPI_Comm curve_comm; - const auto & curve_comm = mesh.GetCommunicator(); + auto comm = mesh.GetCommunicator(); #ifdef PARALLEL enum { MPI_TAG_CURVE = MPI_TAG_MESH+20 }; - const ParallelMeshTopology & partop = mesh.GetParallelTopology (); - // MPI_Comm_dup (mesh.GetCommunicator(), &curve_comm); - NgArray procs; -#else - // curve_comm = mesh.GetCommunicator(); #endif - int id = curve_comm.Rank(); - int ntasks = curve_comm.Size(); - - bool working = (ntasks == 1) || (id > 0); + int ntasks = comm.Size(); + bool working = (ntasks == 1) || (comm.Rank() > 0); if (working) order = aorder; @@ -653,38 +645,26 @@ namespace netgen if (ntasks > 1 && working) { for (int e = 0; e < edgeorder.Size(); e++) - { - partop.GetDistantEdgeNums (e+1, procs); - for (int j = 0; j < procs.Size(); j++) - send_orders.Add (procs[j], edgeorder[e]); - } + for (int proc : partop.GetDistantEdgeProcs(e)) + send_orders.Add (proc, edgeorder[e]); for (int f = 0; f < faceorder.Size(); f++) - { - partop.GetDistantFaceNums (f+1, procs); - for (int j = 0; j < procs.Size(); j++) - send_orders.Add (procs[j], faceorder[f]); - } + for (int proc : partop.GetDistantFaceProcs(f)) + send_orders.Add (proc, faceorder[f]); } if (ntasks > 1) - MyMPI_ExchangeTable (send_orders, recv_orders, MPI_TAG_CURVE, curve_comm); + MyMPI_ExchangeTable (send_orders, recv_orders, MPI_TAG_CURVE, comm); if (ntasks > 1 && working) { NgArray cnt(ntasks); cnt = 0; for (int e = 0; e < edgeorder.Size(); e++) - { - partop.GetDistantEdgeNums (e+1, procs); - for (int j = 0; j < procs.Size(); j++) - edgeorder[e] = max(edgeorder[e], recv_orders[procs[j]][cnt[procs[j]]++]); - } + for (auto proc : partop.GetDistantEdgeProcs(e)) + edgeorder[e] = max(edgeorder[e], recv_orders[proc][cnt[proc]++]); for (int f = 0; f < faceorder.Size(); f++) - { - partop.GetDistantFaceNums (f+1, procs); - for (int j = 0; j < procs.Size(); j++) - faceorder[f] = max(faceorder[f], recv_orders[procs[j]][cnt[procs[j]]++]); - } + for (auto proc : partop.GetDistantFaceProcs(f)) + faceorder[f] = max(faceorder[f], recv_orders[proc][cnt[proc]++]); } #endif @@ -748,21 +728,19 @@ namespace netgen for (int i2 = 0; i2 < edgenrs.Size(); i2++) { - // PointIndex pi1 = el[edges[i2][0]]; - // PointIndex pi2 = el[edges[i2][1]]; - - // bool swap = pi1 > pi2; - - // Point<3> p1 = mesh[pi1]; - // Point<3> p2 = mesh[pi2]; - - // int order1 = edgeorder[edgenrs[i2]]; - // int ndof = max (0, order1-1); - - surfnr[edgenrs[i2]] = mesh.GetFaceDescriptor(el.GetIndex()).SurfNr(); - gi0[edgenrs[i2]] = el.GeomInfoPi(edges[i2][0]+1); - gi1[edgenrs[i2]] = el.GeomInfoPi(edges[i2][1]+1); - } + auto enr = edgenrs[i2]; + surfnr[enr] = mesh.GetFaceDescriptor(el.GetIndex()).SurfNr(); + if (el[edges[i2][0]] < el[edges[i2][1]]) + { + gi0[enr] = el.GeomInfoPi(edges[i2][0]+1); + gi1[enr] = el.GeomInfoPi(edges[i2][1]+1); + } + else + { + gi1[enr] = el.GeomInfoPi(edges[i2][0]+1); + gi0[enr] = el.GeomInfoPi(edges[i2][1]+1); + } + } } @@ -773,48 +751,40 @@ namespace netgen TABLE senddata(ntasks), recvdata(ntasks); if (working) for (int e = 0; e < nedges; e++) - { - partop.GetDistantEdgeNums (e+1, procs); - for (int j = 0; j < procs.Size(); j++) - { - senddata.Add (procs[j], surfnr[e]); - if (surfnr[e] != -1) - { - senddata.Add (procs[j], gi0[e].trignum); - senddata.Add (procs[j], gi0[e].u); - senddata.Add (procs[j], gi0[e].v); - senddata.Add (procs[j], gi1[e].trignum); - senddata.Add (procs[j], gi1[e].u); - senddata.Add (procs[j], gi1[e].v); - } - } - } - - MyMPI_ExchangeTable (senddata, recvdata, MPI_TAG_CURVE, curve_comm); + for (int proc : partop.GetDistantEdgeProcs(e)) + { + senddata.Add (proc, surfnr[e]); + if (surfnr[e] != -1) + { + senddata.Add (proc, gi0[e].trignum); + senddata.Add (proc, gi0[e].u); + senddata.Add (proc, gi0[e].v); + senddata.Add (proc, gi1[e].trignum); + senddata.Add (proc, gi1[e].u); + senddata.Add (proc, gi1[e].v); + } + } + MyMPI_ExchangeTable (senddata, recvdata, MPI_TAG_CURVE, comm); NgArray cnt(ntasks); cnt = 0; if (working) for (int e = 0; e < nedges; e++) - { - partop.GetDistantEdgeNums (e+1, procs); - for (int j = 0; j < procs.Size(); j++) - { - int surfnr1 = recvdata[procs[j]][cnt[procs[j]]++]; - if (surfnr1 != -1) - { - surfnr[e] = surfnr1; - gi0[e].trignum = int (recvdata[procs[j]][cnt[procs[j]]++]); - gi0[e].u = recvdata[procs[j]][cnt[procs[j]]++]; - gi0[e].v = recvdata[procs[j]][cnt[procs[j]]++]; - gi1[e].trignum = int (recvdata[procs[j]][cnt[procs[j]]++]); - gi1[e].u = recvdata[procs[j]][cnt[procs[j]]++]; - gi1[e].v = recvdata[procs[j]][cnt[procs[j]]++]; - } - } - } - + for (int proc : partop.GetDistantEdgeProcs(e)) + { + int surfnr1 = recvdata[proc][cnt[proc]++]; + if (surfnr1 != -1) + { + surfnr[e] = surfnr1; + gi0[e].trignum = int (recvdata[proc][cnt[proc]++]); + gi0[e].u = recvdata[proc][cnt[proc]++]; + gi0[e].v = recvdata[proc][cnt[proc]++]; + gi1[e].trignum = int (recvdata[proc][cnt[proc]++]); + gi1[e].u = recvdata[proc][cnt[proc]++]; + gi1[e].v = recvdata[proc][cnt[proc]++]; + } + } } #endif @@ -976,59 +946,53 @@ namespace netgen TABLE senddata(ntasks), recvdata(ntasks); if (working) for (int e = 0; e < nedges; e++) - { - partop.GetDistantEdgeNums (e+1, procs); - for (int j = 0; j < procs.Size(); j++) - { - senddata.Add (procs[j], use_edge[e]); - if (use_edge[e]) - { - senddata.Add (procs[j], edge_surfnr1[e]); - senddata.Add (procs[j], edge_surfnr2[e]); - senddata.Add (procs[j], edge_gi0[e].edgenr); - senddata.Add (procs[j], edge_gi0[e].body); - senddata.Add (procs[j], edge_gi0[e].dist); - senddata.Add (procs[j], edge_gi0[e].u); - senddata.Add (procs[j], edge_gi0[e].v); - senddata.Add (procs[j], edge_gi1[e].edgenr); - senddata.Add (procs[j], edge_gi1[e].body); - senddata.Add (procs[j], edge_gi1[e].dist); - senddata.Add (procs[j], edge_gi1[e].u); - senddata.Add (procs[j], edge_gi1[e].v); - senddata.Add (procs[j], swap_edge[e]); - } - } - } - MyMPI_ExchangeTable (senddata, recvdata, MPI_TAG_CURVE, curve_comm); + for (int proc : partop.GetDistantEdgeProcs(e)) + { + senddata.Add (proc, use_edge[e]); + if (use_edge[e]) + { + senddata.Add (proc, edge_surfnr1[e]); + senddata.Add (proc, edge_surfnr2[e]); + senddata.Add (proc, edge_gi0[e].edgenr); + senddata.Add (proc, edge_gi0[e].body); + senddata.Add (proc, edge_gi0[e].dist); + senddata.Add (proc, edge_gi0[e].u); + senddata.Add (proc, edge_gi0[e].v); + senddata.Add (proc, edge_gi1[e].edgenr); + senddata.Add (proc, edge_gi1[e].body); + senddata.Add (proc, edge_gi1[e].dist); + senddata.Add (proc, edge_gi1[e].u); + senddata.Add (proc, edge_gi1[e].v); + senddata.Add (proc, swap_edge[e]); + } + } + + MyMPI_ExchangeTable (senddata, recvdata, MPI_TAG_CURVE, comm); NgArray cnt(ntasks); cnt = 0; if (working) for (int e = 0; e < edge_surfnr1.Size(); e++) - { - partop.GetDistantEdgeNums (e+1, procs); - for (int j = 0; j < procs.Size(); j++) - { - int get_edge = int(recvdata[procs[j]][cnt[procs[j]]++]); - if (get_edge) - { - use_edge[e] = 1; - edge_surfnr1[e] = int (recvdata[procs[j]][cnt[procs[j]]++]); - edge_surfnr2[e] = int (recvdata[procs[j]][cnt[procs[j]]++]); - edge_gi0[e].edgenr = int (recvdata[procs[j]][cnt[procs[j]]++]); - edge_gi0[e].body = int (recvdata[procs[j]][cnt[procs[j]]++]); - edge_gi0[e].dist = recvdata[procs[j]][cnt[procs[j]]++]; - edge_gi0[e].u = recvdata[procs[j]][cnt[procs[j]]++]; - edge_gi0[e].v = recvdata[procs[j]][cnt[procs[j]]++]; - edge_gi1[e].edgenr = int (recvdata[procs[j]][cnt[procs[j]]++]); - edge_gi1[e].body = int (recvdata[procs[j]][cnt[procs[j]]++]); - edge_gi1[e].dist = recvdata[procs[j]][cnt[procs[j]]++]; - edge_gi1[e].u = recvdata[procs[j]][cnt[procs[j]]++]; - edge_gi1[e].v = recvdata[procs[j]][cnt[procs[j]]++]; - swap_edge[e] = recvdata[procs[j]][cnt[procs[j]]++]; - } - } - } - + for (int proc : partop.GetDistantEdgeProcs(e)) + { + int get_edge = int(recvdata[proc][cnt[proc]++]); + if (get_edge) + { + use_edge[e] = 1; + edge_surfnr1[e] = int (recvdata[proc][cnt[proc]++]); + edge_surfnr2[e] = int (recvdata[proc][cnt[proc]++]); + edge_gi0[e].edgenr = int (recvdata[proc][cnt[proc]++]); + edge_gi0[e].body = int (recvdata[proc][cnt[proc]++]); + edge_gi0[e].dist = recvdata[proc][cnt[proc]++]; + edge_gi0[e].u = recvdata[proc][cnt[proc]++]; + edge_gi0[e].v = recvdata[proc][cnt[proc]++]; + edge_gi1[e].edgenr = int (recvdata[proc][cnt[proc]++]); + edge_gi1[e].body = int (recvdata[proc][cnt[proc]++]); + edge_gi1[e].dist = recvdata[proc][cnt[proc]++]; + edge_gi1[e].u = recvdata[proc][cnt[proc]++]; + edge_gi1[e].v = recvdata[proc][cnt[proc]++]; + swap_edge[e] = recvdata[proc][cnt[proc]++]; + } + } } #endif @@ -1184,26 +1148,20 @@ namespace netgen if (ntasks > 1 && working) { for (int f = 0; f < nfaces; f++) - { - partop.GetDistantFaceNums (f+1, procs); - for (int j = 0; j < procs.Size(); j++) - send_surfnr.Add (procs[j], surfnr[f]); - } + for (int proc : partop.GetDistantFaceProcs(f)) + send_surfnr.Add (proc, surfnr[f]); } if (ntasks > 1) - MyMPI_ExchangeTable (send_surfnr, recv_surfnr, MPI_TAG_CURVE, curve_comm); + MyMPI_ExchangeTable (send_surfnr, recv_surfnr, MPI_TAG_CURVE, comm); if (ntasks > 1 && working) { NgArray cnt(ntasks); cnt = 0; for (int f = 0; f < nfaces; f++) - { - partop.GetDistantFaceNums (f+1, procs); - for (int j = 0; j < procs.Size(); j++) - surfnr[f] = max(surfnr[f], recv_surfnr[procs[j]][cnt[procs[j]]++]); - } + for (int proc : partop.GetDistantFaceProcs(f)) + surfnr[f] = max(surfnr[f], recv_surfnr[proc][cnt[proc]++]); } #endif @@ -1303,6 +1261,10 @@ namespace netgen SurfaceElementIndex sei = top.GetFace2SurfaceElement (f+1)-1; if (sei != SurfaceElementIndex(-1)) { PointGeomInfo gi = mesh[sei].GeomInfoPi(1); + // use improved initial guess + gi.u = (lami[fnums[0]]*mesh[sei].GeomInfoPi(1).u+lami[fnums[1]]*mesh[sei].GeomInfoPi(2).u+lami[fnums[2]]*mesh[sei].GeomInfoPi(3).u); + gi.v = (lami[fnums[0]]*mesh[sei].GeomInfoPi(1).v+lami[fnums[1]]*mesh[sei].GeomInfoPi(2).v+lami[fnums[2]]*mesh[sei].GeomInfoPi(3).v); + geo.ProjectPointGI(surfnr[facenr], pp, gi); } else @@ -1386,8 +1348,7 @@ namespace netgen #ifdef PARALLEL - curve_comm.Barrier(); - // MPI_Comm_free (&curve_comm); + comm.Barrier(); #endif } diff --git a/libsrc/meshing/delaunay.cpp b/libsrc/meshing/delaunay.cpp index a152b762..cd675ef3 100644 --- a/libsrc/meshing/delaunay.cpp +++ b/libsrc/meshing/delaunay.cpp @@ -3,285 +3,6 @@ namespace netgen { - template - class DelaunayTree - { - public: - // Number of entries per leaf - static constexpr int N = 100; - - struct Node; - - struct Leaf - { - Point<2*dim, TSCAL> p[N]; - T index[N]; - int n_elements; - int nr; - - Leaf() : n_elements(0) - { } - - - void Add( Array &leaves, Array &leaf_index, const Point<2*dim> &ap, T aindex ) - { - p[n_elements] = ap; - index[n_elements] = aindex; - n_elements++; - if(leaf_index.Size() leaves; - Array leaf_index; - - Point global_min, global_max; - double tol; - size_t n_leaves; - size_t n_nodes; - BlockAllocator ball_nodes; - BlockAllocator ball_leaves; - - public: - - DelaunayTree (const Point & pmin, const Point & pmax) - : global_min(pmin), global_max(pmax), n_leaves(1), n_nodes(1), ball_nodes(sizeof(Node)), ball_leaves(sizeof(Leaf)) - { - root.leaf = (Leaf*) ball_leaves.Alloc(); new (root.leaf) Leaf(); - root.leaf->nr = 0; - leaves.Append(root.leaf); - root.level = 0; - tol = 1e-7 * Dist(pmax, pmin); - } - - DelaunayTree (const Box & box) - : DelaunayTree(box.PMin(), box.PMax()) - { } - - size_t GetNLeaves() - { - return n_leaves; - } - - size_t GetNNodes() - { - return n_nodes; - } - - template - void GetFirstIntersecting (const Point & pmin, const Point & pmax, - TFunc func=[](auto pi){return false;}) const - { - // static Timer timer("DelaunayTree::GetIntersecting"); RegionTimer rt(timer); - // static Timer timer1("DelaunayTree::GetIntersecting-LinearSearch"); - ArrayMem stack; - ArrayMem dir_stack; - - - Point<2*dim> tpmin, tpmax; - - for (size_t i : IntRange(dim)) - { - tpmin(i) = global_min(i); - tpmax(i) = pmax(i)+tol; - - tpmin(i+dim) = pmin(i)-tol; - tpmax(i+dim) = global_max(i); - } - - stack.SetSize(0); - stack.Append(&root); - dir_stack.SetSize(0); - dir_stack.Append(0); - - while(stack.Size()) - { - const Node *node = stack.Last(); - stack.DeleteLast(); - - int dir = dir_stack.Last(); - dir_stack.DeleteLast(); - - if(Leaf *leaf = node->GetLeaf()) - { - // RegionTimer rt1(timer1); - for (auto i : IntRange(leaf->n_elements)) - { - bool intersect = true; - const auto p = leaf->p[i]; - - for (int d = 0; d < dim; d++) - if (p[d] > tpmax[d]) - intersect = false; - for (int d = dim; d < 2*dim; d++) - if (p[d] < tpmin[d]) - intersect = false; - if(intersect) - if(func(leaf->index[i])) return; - } - } - else - { - int newdir = dir+1; - if(newdir==2*dim) newdir = 0; - if (tpmin[dir] <= node->sep) - { - stack.Append(node->children[0]); - dir_stack.Append(newdir); - } - if (tpmax[dir] >= node->sep) - { - stack.Append(node->children[1]); - dir_stack.Append(newdir); - } - } - } - } - - void GetIntersecting (const Point & pmin, const Point & pmax, - NgArray & pis) const - { - pis.SetSize(0); - GetFirstIntersecting(pmin, pmax, [&pis](auto pi) { pis.Append(pi); return false;}); - } - - void Insert (const Box & box, T pi) - { - Insert (box.PMin(), box.PMax(), pi); - } - - void Insert (const Point & pmin, const Point & pmax, T pi) - { - // static Timer timer("DelaunayTree::Insert"); RegionTimer rt(timer); - int dir = 0; - Point<2*dim> p; - for (auto i : IntRange(dim)) - { - p(i) = pmin[i]; - p(i+dim) = pmax[i]; - } - - Node * node = &root; - Leaf * leaf = node->GetLeaf(); - - // search correct leaf to add point - while(!leaf) - { - node = p[dir] < node->sep ? node->children[0] : node->children[1]; - dir++; - if(dir==2*dim) dir = 0; - leaf = node->GetLeaf(); - } - - // add point to leaf - if(leaf->n_elements < N) - leaf->Add(leaves, leaf_index, p,pi); - else // assume leaf->n_elements == N - { - // add two new nodes and one new leaf - int n_elements = leaf->n_elements; - ArrayMem coords(n_elements); - ArrayMem order(n_elements); - - // separate points in two halves, first sort all coordinates in direction dir - for (auto i : IntRange(n_elements)) - { - order[i] = i; - coords[i] = leaf->p[i][dir]; - } - - QuickSortI(coords, order); - int isplit = N/2; - Leaf *leaf1 = (Leaf*) ball_leaves.Alloc(); new (leaf1) Leaf(); - Leaf *leaf2 = (Leaf*) ball_leaves.Alloc(); new (leaf2) Leaf(); - - leaf1->nr = leaf->nr; - leaf2->nr = leaves.Size(); - leaves.Append(leaf2); - leaves[leaf1->nr] = leaf1; - - for (auto i : order.Range(isplit)) - leaf1->Add(leaves, leaf_index, leaf->p[i], leaf->index[i] ); - for (auto i : order.Range(isplit, N)) - leaf2->Add(leaves, leaf_index, leaf->p[i], leaf->index[i] ); - - Node *node1 = (Node*) ball_nodes.Alloc(); new (node1) Node(); - node1->leaf = leaf1; - node1->level = node->level+1; - - Node *node2 = (Node*) ball_nodes.Alloc(); new (node2) Node(); - node2->leaf = leaf2; - node2->level = node->level+1; - - node->children[0] = node1; - node->children[1] = node2; - node->sep = 0.5 * (leaf->p[order[isplit-1]][dir] + leaf->p[order[isplit]][dir]); - - // add new point to one of the new leaves - if (p[dir] < node->sep) - leaf1->Add( leaves, leaf_index, p, pi ); - else - leaf2->Add( leaves, leaf_index, p, pi ); - - ball_leaves.Free(leaf); - n_leaves++; - n_nodes+=2; - } - } - - void DeleteElement (T pi) - { - // static Timer timer("DelaunayTree::DeleteElement"); RegionTimer rt(timer); - Leaf *leaf = leaves[leaf_index[pi]]; - leaf_index[pi] = -1; - auto & n_elements = leaf->n_elements; - auto & index = leaf->index; - auto & p = leaf->p; - - for (auto i : IntRange(n_elements)) - { - if(index[i] == pi) - { - n_elements--; - if(i!=n_elements) - { - index[i] = index[n_elements]; - p[i] = p[n_elements]; - } - return; - } - } - } - }; - // typedef BoxTree<3> DTREE; typedef DelaunayTree<3> DTREE; @@ -1051,6 +772,7 @@ namespace netgen // improve delaunay - mesh by swapping !!!! Mesh tempmesh; + tempmesh.GetMemoryTracer().SetName("delaunay-tempmesh"); for (auto & meshpoint : mesh.Points()) tempmesh.AddPoint (meshpoint); diff --git a/libsrc/meshing/delaunay2d.cpp b/libsrc/meshing/delaunay2d.cpp index a61016e2..55538ad6 100644 --- a/libsrc/meshing/delaunay2d.cpp +++ b/libsrc/meshing/delaunay2d.cpp @@ -1,34 +1,54 @@ #include #include "meshing.hpp" +#include + + // not yet working .... namespace netgen { + using ngcore::INT; + extern void Optimize2d (Mesh & mesh, MeshingParameters & mp); + static inline Point<2> P2( Point<3> p ) + { + return {p[0], p[1]}; + } + + static inline Point<3> P3( Point<2> p ) + { + return {p[0], p[1], 0}; + } class DelaunayTrig { PointIndex pnums[3]; - Point<3> c; + Point<2> c; + public: double r; double rad2; - public: - DelaunayTrig () { ; } + DelaunayTrig () = default; DelaunayTrig (int p1, int p2, int p3) - { pnums[0] = p1; pnums[1] = p2; pnums[2] = p3; } + { + pnums[0] = p1; + pnums[1] = p2; + pnums[2] = p3; + } PointIndex & operator[] (int j) { return pnums[j]; } const PointIndex & operator[] (int j) const { return pnums[j]; } void CalcCenter (Mesh & mesh) { - Point<3> p1 = mesh[pnums[0]]; - Point<3> p2 = mesh[pnums[1]]; - Point<3> p3 = mesh[pnums[2]]; - Vec<3> v1 = p2-p1; - Vec<3> v2 = p3-p1; + Point<2> p1 = P2(mesh[pnums[0]]); + Point<2> p2 = P2(mesh[pnums[1]]); + Point<2> p3 = P2(mesh[pnums[2]]); + + Vec<2> v1 = p2-p1; + Vec<2> v2 = p3-p1; + /* Mat<2,2> mat, inv; mat(0,0) = v1*v1; mat(0,1) = v1*v2; @@ -42,12 +62,357 @@ namespace netgen c = p1 + sol(0) * v1 + sol(1) * v2; rad2 = Dist2(c, p1); - r = sqrt(rad2); + r = sqrt(rad2); + */ + + // without normal equation ... + Mat<2,2> mat, inv; + mat(0,0) = v1(0); mat(0,1) = v1(1); + mat(1,0) = v2(0); mat(1,1) = v2(1); + CalcInverse (mat, inv); + Vec<2> rhs, sol; + rhs(0) = 0.5 * v1*v1; + rhs(1) = 0.5 * v2*v2; + sol = inv * rhs; + c = p1 + sol; + + rad2 = Dist2(c, p1); + r = sqrt(rad2); } - Point<3> Center() const { return c; } + Point<2> Center() const { return c; } double Radius2() const { return rad2; } - Box<3> BoundingBox() const { return Box<3> (c-Vec<3>(r,r,0.1), c+Vec<3>(r,r,0.1)); } + Box<2> BoundingBox() const { return Box<2> (c-Vec<2>(r,r), c+Vec<2>(r,r)); } + + mutable PointIndex visited_pi = -1; + }; + + class DelaunayMesh + { + ngcore::ClosedHashTable, INT<2>> edge_to_trig; + Array trigs; + unique_ptr> tree; + Mesh & mesh; + + Array closeels; + Array intersecting; + Array> edges; + + int GetNeighbour( int eli, int edge ) + { + auto p0 = trigs[eli][(edge+1)%3]; + auto p1 = trigs[eli][(edge+2)%3]; + if(p1 hash = {p0,p1}; + /* + if(!edge_to_trig.Used(hash)) + return -1; + + auto i2 = edge_to_trig.Get({p0,p1}); + + return i2[0] == eli ? i2[1] : i2[0]; + */ + auto pos = edge_to_trig.Position(hash); + if (pos == -1) return -1; + auto i2 = edge_to_trig.GetData(pos); + return i2[0] == eli ? i2[1] : i2[0]; + } + + void SetNeighbour( int eli, int edge ) + { + auto p0 = trigs[eli][(edge+1)%3]; + auto p1 = trigs[eli][(edge+2)%3]; + if(p1 hash = {p0,p1}; + auto pos = edge_to_trig.Position(hash); + if (pos == -1) + edge_to_trig[hash] = {eli, -1}; + else + { + auto i2 = edge_to_trig.GetData(pos); + if(i2[0]==-1) + i2[0] = eli; + else + { + if(i2[1]==-1) + i2[1] = eli; + } + edge_to_trig.SetData (pos, i2); + } + /* + if(!edge_to_trig.Used(hash)) + edge_to_trig[hash] = {eli, -1}; + else + { + + auto i2 = edge_to_trig.Get({p0,p1}); + + if(i2[0]==-1) + i2[0] = eli; + else + { + if(i2[1]==-1) + i2[1] = eli; + } + + edge_to_trig[hash] = i2; + } + */ + } + + void UnsetNeighbours( int eli ) + { + for(int edge : Range(3)) + { + auto p0 = trigs[eli][(edge+1)%3]; + auto p1 = trigs[eli][(edge+2)%3]; + if(p1 hash = {p0,p1}; + // auto i2 = edge_to_trig.Get({p0,p1}); + auto pos = edge_to_trig.Position(hash); + auto i2 = edge_to_trig.GetData(pos); + + if(i2[0]==eli) + i2[0] = i2[1]; + i2[1] = -1; + + // edge_to_trig[hash] = i2; + edge_to_trig.SetData (pos, i2); + } + } + + + void AppendTrig( int pi0, int pi1, int pi2 ) + { + DelaunayTrig el; + el[0] = pi0; + el[1] = pi1; + el[2] = pi2; + + el.CalcCenter(mesh); + + trigs.Append(el); + int ti = trigs.Size()-1; + tree->Insert(el.BoundingBox(), ti); + + for(int i : Range(3)) + SetNeighbour(ti, i); + } + + public: + DelaunayMesh( Mesh & mesh_, Box<2> box ) + : mesh(mesh_) + { + Vec<2> vdiag = box.PMax()-box.PMin(); + + double w = vdiag(0); + double h = vdiag(1); + + Point<2> p0 = box.PMin() + Vec<2> ( -3*h, -h); + Point<2> p1 = box.PMin() + Vec<2> (w+3*h, -h); + Point<2> p2 = box.Center() + Vec<2> (0, 1.5*h+0.5*w); + + box.Add( p0 ); + box.Add( p1 ); + box.Add( p2 ); + + tree = make_unique>(box); + + auto pi0 = mesh.AddPoint (P3(p0)); + auto pi1 = mesh.AddPoint (P3(p1)); + auto pi2 = mesh.AddPoint (P3(p2)); + AppendTrig(pi0, pi1, pi2); + } + + void AddPoint( PointIndex pi_new ) + { + static Timer t("AddPoint"); RegionTimer reg(t); + Point<2> newp = P2(mesh[pi_new]); + intersecting.SetSize(0); + edges.SetSize(0); + + int definitive_overlapping_trig = -1; + + double minquot{1e20}; + tree->GetFirstIntersecting (newp, newp, [&] (const auto i_trig) + { + const auto trig = trigs[i_trig]; + double rad2 = trig.Radius2(); + double d2 = Dist2 (trig.Center(), newp); + if (d2 >= rad2) return false; + + if (d2 < 0.999 * rad2) + { + definitive_overlapping_trig = i_trig; + return true; + } + + if (definitive_overlapping_trig == -1 || d2 < 0.99*minquot*rad2) + { + minquot = d2/rad2; + definitive_overlapping_trig = i_trig; + } + return false; + }); + + if(definitive_overlapping_trig==-1) + { + static Timer t("slow check"); RegionTimer reg(t); + PrintMessage (5, "Warning in delaunay tree - didn't find overlapping circle, check all trigs again"); + for(auto i_trig : trigs.Range()) + { + const auto trig = trigs[i_trig]; + + if(trig[0]==-1) + continue; + + double rad2 = trig.Radius2(); + double d2 = Dist2 (trig.Center(), newp); + + // if (d2 < 0.999 * rad2) + if (d2 < (1-1e-10)*rad2) + { + definitive_overlapping_trig = i_trig; + break; + } + } + } + + // static Timer tvis("trig visited"); + // tvis.Start(); + // BitArray trig_visited(trigs.Size()); + // trig_visited.Clear(); + if(definitive_overlapping_trig==-1) + throw Exception("point not in any circle "+ ToString(pi_new)); + // tvis.Stop(); + // static Timer t2("addpoint - rest"); RegionTimer r2(t2); + Array trigs_to_visit; + trigs_to_visit.Append(definitive_overlapping_trig); + intersecting.Append(definitive_overlapping_trig); + // trig_visited.SetBit(definitive_overlapping_trig); + trigs[definitive_overlapping_trig].visited_pi = pi_new; + + while(trigs_to_visit.Size()) + { + int ti = trigs_to_visit.Last(); + trigs_to_visit.DeleteLast(); + + // trig_visited.SetBit(ti); + + auto & trig = trigs[ti]; + trig.visited_pi = pi_new; + + for(auto ei : Range(3)) + { + auto nb = GetNeighbour(ti, ei); + if(nb==-1) + continue; + // if(trig_visited.Test(nb)) + // continue; + + const auto & trig_nb = trigs[nb]; + if (trig_nb.visited_pi == pi_new) + continue; + + // trig_visited.SetBit(nb); + trig_nb.visited_pi = pi_new; + + bool is_intersecting = Dist2(newp, trig_nb.Center()) < trig_nb.Radius2()*(1+1e-12); + + if(!is_intersecting) + { + const Point<2> p0 = P2(mesh[PointIndex (trig[(ei+1)%3])]); + const Point<2> p1 = P2(mesh[PointIndex (trig[(ei+2)%3])]); + const Point<2> p2 = P2(mesh[PointIndex (trig[ei])]); + auto v = p1-p0; + + Vec<2> n = {-v[1], v[0]}; + n /= n.Length(); + + double dist = n * (newp-p1); + double scal = n * (p2 - p1); + if (scal > 0) dist *= -1; + + if (dist > -1e-10) + is_intersecting = true; + } + + if(is_intersecting) + { + trigs_to_visit.Append(nb); + intersecting.Append(nb); + } + } + } + + // find outer edges + for (auto j : intersecting) + { + const DelaunayTrig & trig = trigs[j]; + for (int k = 0; k < 3; k++) + { + int p1 = trig[k]; + int p2 = trig[(k+1)%3]; + INT<2> edge{p1,p2}; + edge.Sort(); + bool found = false; + for (int l = 0; l < edges.Size(); l++) + if (edges[l] == edge) + { + edges.RemoveElement(l); + found = true; + break; + } + if (!found) + edges.Append (edge); + } + } + + for (int j : intersecting) + { + UnsetNeighbours(j); + trigs[j][0] = -1; + trigs[j][1] = -1; + trigs[j][2] = -1; + } + + for (auto edge : edges) + AppendTrig( edge[0], edge[1], pi_new ); + + for (int j : intersecting) + tree->DeleteElement (j); + + static int counter=0; + if(0) + { + Mesh m; + m = mesh; + m.ClearSurfaceElements(); + for (DelaunayTrig & trig : trigs) + { + if (trig[0] < 0) continue; + + Vec<3> n = Cross (mesh[trig[1]]-mesh[trig[0]], + mesh[trig[2]]-mesh[trig[0]]); + if (n(2) < 0) Swap (trig[1], trig[2]); + + Element2d el(trig[0], trig[1], trig[2]); + el.SetIndex (1); + m.AddSurfaceElement (el); + } + m.Save("meshes/mesh_"+ToString(counter++)+".vol.gz"); + } + + } + + Array & GetElements() { return trigs; } + }; ostream & operator<< (ostream & ost, DelaunayTrig trig) @@ -59,20 +424,18 @@ namespace netgen void Meshing2 :: BlockFillLocalH (Mesh & mesh, const MeshingParameters & mp) { - static int timer = NgProfiler::CreateTimer ("Meshing2::BlockFill"); - static int timer1 = NgProfiler::CreateTimer ("Meshing2::BlockFill 1"); - static int timer2 = NgProfiler::CreateTimer ("Meshing2::BlockFill 2"); - static int timer3 = NgProfiler::CreateTimer ("Meshing2::BlockFill 3"); - static int timer4 = NgProfiler::CreateTimer ("Meshing2::BlockFill 4"); - NgProfiler::RegionTimer reg (timer); + static Timer timer("Meshing2::BlockFill"); + static Timer timer1("Meshing2::BlockFill 1"); + static Timer timer2("Meshing2::BlockFill 2"); + static Timer timer3("Meshing2::BlockFill 3"); + static Timer timer4("Meshing2::BlockFill 4"); + RegionTimer reg (timer); - NgProfiler::StartTimer (timer1); + timer1.Start(); double filldist = mp.filldist; - cout << "blockfill local h" << endl; - cout << "rel filldist = " << filldist << endl; - PrintMessage (3, "blockfill local h"); + PrintMessage (6, "blockfill local h"); NgArray > npoints; @@ -95,15 +458,12 @@ namespace netgen } - cout << "bbox = " << bbox << endl; - - // Point<3> mpc = bbox.Center(); bbox.Increase (bbox.Diam()/2); Box<3> meshbox = bbox; - NgProfiler::StopTimer (timer1); - NgProfiler::StartTimer (timer2); + timer1.Stop(); + timer2.Start(); LocalH loch2 (bbox, 1, 2); @@ -113,8 +473,14 @@ namespace netgen bool changed; do { - mesh.LocalHFunction().ClearFlags(); - + static Timer tcf("clear flags"); + tcf.Start(); + // mesh.LocalHFunction().ClearFlags(); + mesh.LocalHFunction().ClearRootFlags(); + tcf.Stop(); + + static Timer tcut("tcut"); + tcut.Start(); for (int i = 0; i < adfront.GetNFL(); i++) { const FrontLine & line = adfront.GetLine(i); @@ -128,7 +494,7 @@ namespace netgen mesh.LocalHFunction().CutBoundary (bbox); } - + tcut.Stop(); mesh.LocalHFunction().FindInnerBoxes (&adfront, NULL); @@ -147,28 +513,43 @@ namespace netgen } while (changed); - NgProfiler::StopTimer (timer2); - NgProfiler::StartTimer (timer3); + timer2.Stop(); + timer3.Start(); if (debugparam.slowchecks) - (*testout) << "Blockfill with points: " << endl; - *testout << "loch = " << mesh.LocalHFunction() << endl; - - *testout << "npoints = " << endl << npoints << endl; - - for (int i = 1; i <= npoints.Size(); i++) { - if (meshbox.IsIn (npoints.Get(i))) + (*testout) << "Blockfill with points: " << endl; + *testout << "loch = " << mesh.LocalHFunction() << endl; + + *testout << "npoints = " << endl << npoints << endl; + } + + + int prims[] = { 211, 223, 227, 229, 233, 239, 241, 251, 257, 263 }; + int prim; + + { + int i = 0; + if (npoints.Size()) + while (npoints.Size() % prims[i] == 0) i++; + prim = prims[i]; + } + + for (int i = 0; i < npoints.Size(); i++) + { + size_t hi = (size_t(prim) * size_t(i)) % npoints.Size(); + + if (meshbox.IsIn (npoints[hi])) { - PointIndex gpnum = mesh.AddPoint (npoints.Get(i)); - adfront.AddPoint (npoints.Get(i), gpnum); + PointIndex gpnum = mesh.AddPoint (npoints[hi]); + adfront.AddPoint (npoints[hi], gpnum); if (debugparam.slowchecks) { - (*testout) << npoints.Get(i) << endl; + (*testout) << npoints[hi] << endl; - Point<2> p2d (npoints.Get(i)(0), npoints.Get(i)(1)); + Point<2> p2d (npoints[hi](0), npoints[hi](1)); if (!adfront.Inside(p2d)) { cout << "add outside point" << endl; @@ -179,8 +560,8 @@ namespace netgen } } - NgProfiler::StopTimer (timer3); - NgProfiler::StartTimer (timer4); + timer3.Stop(); + timer4.Start(); // find outer points @@ -214,7 +595,8 @@ namespace netgen // outer points : smooth mesh-grading npoints.SetSize(0); loch2.GetOuterPoints (npoints); - + + /* for (int i = 1; i <= npoints.Size(); i++) { if (meshbox.IsIn (npoints.Get(i))) @@ -223,8 +605,15 @@ namespace netgen adfront.AddPoint (npoints.Get(i), gpnum); } } + */ - NgProfiler::StopTimer (timer4); + for (const Point<3> p : npoints) + if (meshbox.IsIn(p)) + { + PointIndex gpnum = mesh.AddPoint (p); + adfront.AddPoint (p, gpnum); + } + timer4.Stop(); } @@ -232,191 +621,349 @@ namespace netgen void Meshing2 :: Delaunay (Mesh & mesh, int domainnr, const MeshingParameters & mp) { - static int timer = NgProfiler::CreateTimer ("Meshing2::Delaunay - total"); - static int timerstart = NgProfiler::CreateTimer ("Meshing2::Delaunay - start"); - static int timerfinish = NgProfiler::CreateTimer ("Meshing2::Delaunay - finish"); - static int timer1 = NgProfiler::CreateTimer ("Meshing2::Delaunay - incremental"); - static int timer1a = NgProfiler::CreateTimer ("Meshing2::Delaunay - incremental a"); - static int timer1b = NgProfiler::CreateTimer ("Meshing2::Delaunay - incremental b"); - static int timer1c = NgProfiler::CreateTimer ("Meshing2::Delaunay - incremental c"); - static int timer1d = NgProfiler::CreateTimer ("Meshing2::Delaunay - incremental d"); - NgProfiler::RegionTimer reg (timer); + static Timer timer("Meshing2::Delaunay"); + static Timer t1("Meshing2::Delaunay1"); + static Timer t2("Meshing2::Delaunay2"); + static Timer t3("Meshing2::Delaunay3"); + static Timer timer_addpoints("add points"); + RegionTimer reg (timer); + PrintMessage (4, "2D Delaunay meshing"); - - cout << "2D Delaunay meshing (in progress)" << endl; - + auto first_point_blockfill = mesh.Points().Range().Next(); BlockFillLocalH (mesh, mp); - NgProfiler::StartTimer (timerstart); + auto last_point_blockfill = mesh.Points().Range().Next(); - // do the delaunay - - - // face bounding box: - Box<3> bbox (Box<3>::EMPTY_BOX); + t1.Start(); + // Bounding box for starting trig in delaunay + Box<2> bbox (Box<2>::EMPTY_BOX); for (int i = 0; i < adfront.GetNFL(); i++) { const FrontLine & line = adfront.GetLine(i); - bbox.Add (Point<3> (adfront.GetPoint (line.L()[0]))); - bbox.Add (Point<3> (adfront.GetPoint (line.L()[1]))); + bbox.Add (P2(Point<3> (adfront.GetPoint (line.L()[0])))); + bbox.Add (P2(Point<3> (adfront.GetPoint (line.L()[1])))); } + for (PointIndex pi : Range(first_point_blockfill, last_point_blockfill)) + bbox.Add(P2(mesh[pi])); + for (int i = 0; i < mesh.LockedPoints().Size(); i++) - bbox.Add (mesh.Point (mesh.LockedPoints()[i])); + bbox.Add (P2(mesh.Point (mesh.LockedPoints()[i]))); + t1.Stop(); - cout << "bbox = " << bbox << endl; - - // external point - Vec<3> vdiag = bbox.PMax()-bbox.PMin(); - - auto old_points = mesh.Points().Range(); - DelaunayTrig startel; - startel[0] = mesh.AddPoint (bbox.PMin() + Vec<3> (-8*vdiag(0), -8*vdiag(1), 0)); - startel[1] = mesh.AddPoint (bbox.PMin() + Vec<3> (+8*vdiag(0), -8*vdiag(1), 0)); - startel[2] = mesh.AddPoint (bbox.PMin() + Vec<3> (0, 8*vdiag(1), 0)); - - Box<3> hbox; - hbox.Set (mesh[startel[0]]); - hbox.Add (mesh[startel[1]]); - hbox.Add (mesh[startel[2]]); - Point<3> hp = mesh[startel[0]]; - hp(2) = 1; hbox.Add (hp); - hp(2) = -1; hbox.Add (hp); - BoxTree<3> searchtree(hbox); - - NgArray tempels; - startel.CalcCenter (mesh); - - tempels.Append (startel); - searchtree.Insert(startel.BoundingBox(), 0); - - NgArray closeels; - NgArray intersecting; - NgArray edges; - - - - - // reorder points - NgArray mixed(old_points.Size()); - int prims[] = { 11, 13, 17, 19, 23, 29, 31, 37 }; - int prim; - + t2.Start(); + Array old_points; + BitArray add_point(mesh.Points().Size()+1); + Array addpoints; + add_point.Clear(); + /* + for (SegmentIndex si = 0; si < mesh.GetNSeg(); si++) { - int i = 0; - while (old_points.Size() % prims[i] == 0) i++; - prim = prims[i]; + const auto & s = mesh[si]; + if ( s.domin==domainnr || s.domout==domainnr ) + { + add_point.SetBit(s[0]); + add_point.SetBit(s[1]); + } + } + */ + /* + for (int i = 0; i < adfront.GetNFL(); i++) + { + const FrontLine & line = adfront.GetLine(i); + for (int j = 0; j < 2; j++) + add_point.SetBit (adfront.GetGlobalIndex (line.L()[j]))adfront.GetGlobalIndex (line.L()[j])); + } + */ + for (const auto & line : adfront.GetLines()) + for (int j = 0; j < 2; j++) + { + PointIndex pnum = adfront.GetGlobalIndex (line.L()[j]); + if (!add_point.Test(pnum)) + addpoints.Append(pnum); + add_point.SetBit (pnum); + } + + + t2.Stop(); + + t3.Start(); + Mesh tempmesh; + tempmesh.AddFaceDescriptor (FaceDescriptor (1, 1, 0, 0)); + tempmesh.AddFaceDescriptor (FaceDescriptor (2, 1, 0, 0)); + tempmesh.AddFaceDescriptor (FaceDescriptor (3, 1, 0, 0)); + + Array compress; + Array icompress(mesh.Points().Size()); + + /* + for(auto pi : mesh.Points().Range()) + if(add_point.Test(pi)) + */ + for (PointIndex pi : addpoints) + { + icompress[pi] = tempmesh.AddPoint(mesh[pi]); + compress.Append(pi); + } + + for (PointIndex pi : Range(first_point_blockfill, last_point_blockfill)) + { + icompress[pi] = tempmesh.AddPoint(mesh[pi]); + compress.Append(pi); + } + t3.Stop(); + // DelaunayMesh adds surrounding trig (don't add the last 3 points to delaunay AGAIN! + auto tempmesh_points = tempmesh.Points().Range(); + + DelaunayMesh dmesh(tempmesh, bbox); + + timer_addpoints.Start(); + +// // reorder points +// NgArray mixed(old_points.Size()); +// int prims[] = { 11, 13, 17, 19, 23, 29, 31, 37 }; +// int prim; +// +// { +// int i = 0; +// while (old_points.Size() % prims[i] == 0) i++; +// prim = prims[i]; +// } +// +// for (PointIndex pi : old_points) +// mixed[pi] = PointIndex ( (prim * pi) % old_points.Size() + PointIndex::BASE ); + + for (auto pi : tempmesh_points) + dmesh.AddPoint(pi); + + timer_addpoints.Stop(); + + static Timer taddseg("addseg"); + taddseg.Start(); + + /* + for (auto seg : mesh.LineSegments()) + { + if ( seg.domin == domainnr || seg.domout == domainnr ) + { + if(seg.domin==domainnr) + seg.domout = 0; + if(seg.domout==domainnr) + seg.domin = 0; + seg[0] = icompress[seg[0]]; + seg[1] = icompress[seg[1]]; + tempmesh.AddSegment(seg); + } + } + */ + for (const auto & line : adfront.GetLines()) + { + Segment seg; + for (int j = 0; j < 2; j++) + seg[j] = icompress [adfront.GetGlobalIndex (line.L()[j])]; + seg.domin = domainnr; + seg.domout = 0; + tempmesh.AddSegment(seg); + } + + taddseg.Stop(); + + for (auto & trig : dmesh.GetElements()) + { + if (trig[0] < 0) continue; + + Element2d el(trig[0], trig[1], trig[2]); + el.SetIndex (1); + tempmesh.AddSurfaceElement (el); } - for (PointIndex pi : old_points) - mixed[pi] = PointIndex ( (prim * pi) % old_points.Size() + PointIndex::BASE ); - - NgProfiler::StopTimer (timerstart); - NgProfiler::StartTimer (timer1); - - - for (PointIndex i1 : old_points) + bool conforming = false; + while(!conforming) + { + conforming = true; + BitArray marked_points(tempmesh.Points().Size()+1); + marked_points = false; + // Check for trigs cutting a boundary edge (non-conforming mesh) + auto point_to_trigs = tempmesh.CreatePoint2SurfaceElementTable( 0 ); + for (auto & seg : tempmesh.LineSegments()) { - PointIndex i = mixed[i1]; + int count_adjacent = 0;; + PointIndex pi0 = seg[0]; + PointIndex pi1 = seg[1]; + if(marked_points.Test(pi0)) continue; + if(marked_points.Test(pi1)) continue; - NgProfiler::StartTimer (timer1a); - Point<3> newp = mesh[i]; - intersecting.SetSize(0); - edges.SetSize(0); + for(auto sei : point_to_trigs[pi0]) + for( auto i : Range(3)) + if(tempmesh[sei][i] == pi1) + count_adjacent++; - searchtree.GetIntersecting (newp, newp, closeels); - // for (int jj = 0; jj < closeels.Size(); jj++) - // for (int j = 0; j < tempels.Size(); j++) - for (int j : closeels) + if(count_adjacent==2) + continue; + + PointIndex pi2; + PointIndex pi3; + ArrayMem cutting_trigs; + for(auto sei : point_to_trigs[pi0]) + { + auto & el = tempmesh[sei]; + pi2 = el[0] == pi0 ? el[1] : el[0]; + pi3 = el[2] == pi0 ? el[1] : el[2]; + double alpha, beta; + auto itype = intersect( P2(tempmesh[pi0]), P2(tempmesh[pi1]), P2(tempmesh[pi2]), P2(tempmesh[pi3]), alpha, beta ); + if(itype == X_INTERSECTION) { - if (tempels[j][0] < 0) continue; - Point<3> c = tempels[j].Center(); - double r2 = tempels[j].Radius2(); - - bool inside = Dist2(mesh[i], c) < r2; - if (inside) intersecting.Append (j); + cutting_trigs.Append(sei); + break; } + } + if(cutting_trigs.Size()==0) + continue; + for(auto sei : point_to_trigs[pi2]) + { + if(sei==cutting_trigs[0]) + continue; + for(auto i : IntRange(3)) + if(tempmesh[sei][i]==pi3) + cutting_trigs.Append(sei); + } - NgProfiler::StopTimer (timer1a); - NgProfiler::StartTimer (timer1b); + // Found two trigs cutting a boundary edge -> perform swap + if(cutting_trigs.Size()==2) + { + conforming = false; + if(marked_points.Test(pi2)) continue; + if(marked_points.Test(pi3)) continue; - // find outer edges - for (auto j : intersecting) - { - const DelaunayTrig & trig = tempels[j]; - for (int k = 0; k < 3; k++) - { - int p1 = trig[k]; - int p2 = trig[(k+1)%3]; - INDEX_2 edge(p1,p2); - edge.Sort(); - bool found = false; - for (int l = 0; l < edges.Size(); l++) - if (edges[l] == edge) - { - edges.Delete(l); - found = true; - break; - } - if (!found) edges.Append (edge); - } - } + auto & el0 = tempmesh[cutting_trigs[0]]; + auto & el1 = tempmesh[cutting_trigs[1]]; - NgProfiler::StopTimer (timer1b); - NgProfiler::StartTimer (timer1c); + pi1 = el1[0]+el1[1]+el1[2] - pi2-pi3; - /* - for (int j = intersecting.Size()-1; j >= 0; j--) - tempels.Delete (intersecting[j]); - */ - for (int j : intersecting) - { - searchtree.DeleteElement (j); - tempels[j][0] = -1; - tempels[j][1] = -1; - tempels[j][2] = -1; - } + if(marked_points.Test(pi1)) continue; - NgProfiler::StopTimer (timer1c); - NgProfiler::StartTimer (timer1d); + marked_points.SetBit(pi0); + marked_points.SetBit(pi1); + marked_points.SetBit(pi2); + marked_points.SetBit(pi3); - for (auto edge : edges) - { - DelaunayTrig trig (edge[0], edge[1], i); - trig.CalcCenter (mesh); - tempels.Append (trig); - searchtree.Insert(trig.BoundingBox(), tempels.Size()-1); - } + el0[0] = pi2; + el0[1] = pi1; + el0[2] = pi0; - NgProfiler::StopTimer (timer1d); + el1[0] = pi3; + el1[1] = pi0; + el1[2] = pi1; + } } + } - NgProfiler::StopTimer (timer1); - NgProfiler::StartTimer (timerfinish); + auto point_to_trigs = tempmesh.CreatePoint2SurfaceElementTable( 0 ); - for (DelaunayTrig & trig : tempels) + // Mark edges and trigs as inside or outside, starting with boundary edges + enum POSITION { UNKNOWN, BOUNDARY, INSIDE, OUTSIDE }; + Array trig_pos(tempmesh.SurfaceElements().Size()); + ngcore::ClosedHashTable, POSITION> edge_pos(3*tempmesh.SurfaceElements().Size()); + trig_pos = UNKNOWN; + + for (auto & seg : tempmesh.LineSegments()) + { + ArrayMem els; + INT<2> edge{seg[0], seg[1]}; + edge.Sort(); + edge_pos[edge] = BOUNDARY; + + for(auto sei : point_to_trigs[seg[0]]) + for( auto i : Range(3)) + if(tempmesh[sei][i] == seg[1]) + els.Append(sei); + + for(auto sei : els) { - if (trig[0] < 0) continue; + auto & el = tempmesh[sei]; + PointIndex pi2 = el[0]+el[1]+el[2] - seg[0] - seg[1]; + bool is_left = ::netgen::Area(P2(tempmesh[seg[0]]), P2(tempmesh[seg[1]]), P2(tempmesh[pi2]))>0.0; + POSITION pos; - Point<3> c = Center (mesh[trig[0]], mesh[trig[1]], mesh[trig[2]]); - if (!adfront.Inside (Point<2> (c(0),c(1)))) continue; + if(is_left == (seg.domin==domainnr)) + pos = INSIDE; + else + pos = OUTSIDE; - Vec<3> n = Cross (mesh[trig[1]]-mesh[trig[0]], - mesh[trig[2]]-mesh[trig[0]]); - if (n(2) < 0) Swap (trig[1], trig[2]); - - Element2d el(trig[0], trig[1], trig[2]); - el.SetIndex (domainnr); - mesh.AddSurfaceElement (el); + INT<2> e1{seg[0], pi2}; + INT<2> e2{seg[1], pi2}; + e1.Sort(); + e2.Sort(); + if(!edge_pos.Used(e1)) + edge_pos[e1] = pos; + if(!edge_pos.Used(e2)) + edge_pos[e2] = pos; + trig_pos[sei] = pos; } + } - for (PointIndex pi : mesh.Points().Range()) - *testout << pi << ": " << mesh[pi].Type() << endl; + // Advance from boundary edges/trigs to all others + bool have_unknown_trigs = true; + while(have_unknown_trigs) + { + have_unknown_trigs = false; - NgProfiler::StopTimer (timerfinish); + for (auto sei : Range(tempmesh.SurfaceElements())) + { + auto & el = tempmesh[sei]; + + if(trig_pos[sei] == UNKNOWN) + { + have_unknown_trigs = true; + + // any edge of unkown trig already marked? + for(auto i : IntRange(3)) + { + INT<2> edge{el[(i+1)%3], el[(i+2)%3]}; + edge.Sort(); + if(edge_pos.Used(edge) && edge_pos[edge]!=BOUNDARY) + { + trig_pos[sei] = edge_pos[edge]; + break; + } + } + } + + // if we could mark the trig -> also mark all edges + if(trig_pos[sei] != UNKNOWN) + for(auto i : IntRange(3)) + { + INT<2> edge{el[(i+1)%3], el[(i+2)%3]}; + edge.Sort(); + if(!edge_pos.Used(edge) || edge_pos[edge]==BOUNDARY) + edge_pos[edge] = trig_pos[sei]; + } + } + } + + // add inside trigs to actual mesh + for (auto sei : Range(tempmesh.SurfaceElements())) + { + if(trig_pos[sei] == INSIDE) + { + auto el = tempmesh[sei]; + + Vec<3> n = Cross (tempmesh[el[1]]-tempmesh[el[0]], + tempmesh[el[2]]-tempmesh[el[0]]); + if (n(2) < 0) Swap (el[1], el[2]); + + el[0] = compress[el[0]]; + el[1] = compress[el[1]]; + el[2] = compress[el[2]]; + el.SetIndex(domainnr); + mesh.AddSurfaceElement(el); + } + } + + // mesh.Compress(); // don't compress whole mesh after every sub-domain } } diff --git a/libsrc/meshing/global.cpp b/libsrc/meshing/global.cpp index bd74162c..9bbce766 100644 --- a/libsrc/meshing/global.cpp +++ b/libsrc/meshing/global.cpp @@ -1,5 +1,6 @@ #include #include "meshing.hpp" +#include namespace netgen @@ -21,6 +22,8 @@ namespace netgen // NetgenOutStream * testout = new NetgenOutStream; + const string netgen_version = NETGEN_VERSION; + ostream * mycout = &cout; ostream * myerr = &cerr; diff --git a/libsrc/meshing/hprefinement.cpp b/libsrc/meshing/hprefinement.cpp index b7507e33..d27f7a29 100644 --- a/libsrc/meshing/hprefinement.cpp +++ b/libsrc/meshing/hprefinement.cpp @@ -626,7 +626,7 @@ namespace netgen // prepare new points fac1 = max(0.001,min(0.33,fac1)); - cout << " in HP-REFINEMENT with fac1 " << fac1 << endl; + PrintMessage(3, " in HP-REFINEMENT with fac1 ", fac1); *testout << " in HP-REFINEMENT with fac1 " << fac1 << endl; @@ -1283,7 +1283,7 @@ namespace netgen // cout << nwrong << " wrong prisms, " << nright << " right prisms" << endl; } - cout << nwrong << " wrong prisms, " << nright << " right prisms" << endl; + PrintMessage(3, nwrong, " wrong prisms, ", nright, " right prisms"); NgArray hpts(mesh.GetNP()); @@ -1338,7 +1338,7 @@ namespace netgen sing = true; // iterate at least once while(sing) { - cout << " Start new hp-refinement: step " << act_ref << endl; + PrintMessage(3, " Start new hp-refinement: step ", act_ref); DoRefinement (mesh, hpelements, ref, fac1); DoRefineDummies (mesh, hpelements, ref); @@ -1435,16 +1435,16 @@ namespace netgen int(Get_HPRef_Struct (hpel.type) -> geom)); } } - cout << " Start with Update Topology " << endl; + PrintMessage(5, " Start with Update Topology "); mesh.UpdateTopology(); - cout << " Mesh Update Topology done " << endl; + PrintMessage(5, " Mesh Update Topology done "); act_ref++; sing = ClassifyHPElements(mesh,hpelements, act_ref, levels); } - cout << " HP-Refinement done with " << --act_ref << " refinement steps." << endl; + PrintMessage(3, " HP-Refinement done with ", --act_ref, " refinement steps."); if(act_ref>=1) { @@ -1813,7 +1813,7 @@ bool CheckSingularities(Mesh & mesh, INDEX_2_HASHTABLE & edges, INDEX_2_HAS } if (!sing) - cout << "PrepareElements no more to do for actual refinement " << act_ref << endl; + PrintMessage(3, "PrepareElements no more to do for actual refinement ", act_ref); return(sing); } @@ -1960,8 +1960,8 @@ bool CheckSingularities(Mesh & mesh, INDEX_2_HASHTABLE & edges, INDEX_2_HAS } - cout << "undefined elements update classification: " << cnt_undef << endl; - cout << "non-implemented in update classification: " << cnt_nonimplement << endl; + PrintMessage(3, "undefined elements update classification: ", cnt_undef); + PrintMessage(3, "non-implemented in update classification: ", cnt_nonimplement); for (int i = 0; i < misses.Size(); i++) if (misses[i]) diff --git a/libsrc/meshing/improve2.cpp b/libsrc/meshing/improve2.cpp index fe40259f..acbfa1ea 100644 --- a/libsrc/meshing/improve2.cpp +++ b/libsrc/meshing/improve2.cpp @@ -190,7 +190,12 @@ namespace netgen SurfaceElementIndex sei(i); seia[i] = sei; if (mesh[sei].GetNP() != 3) - mixed = true; + { + const auto & sel = mesh[sei]; + for(auto i : Range(sel.GetNP())) + if(mesh[sel[i]].Type() == INNERPOINT) + mixed = true; + } }); } else diff --git a/libsrc/meshing/improve3.cpp b/libsrc/meshing/improve3.cpp index d9cc3534..fe7d897d 100644 --- a/libsrc/meshing/improve3.cpp +++ b/libsrc/meshing/improve3.cpp @@ -14,6 +14,14 @@ namespace netgen { +static constexpr int IMPROVEMENT_CONFORMING_EDGE = -1e6; + +static inline bool NotTooBad(double bad1, double bad2) +{ + return (bad2 <= bad1) || + (bad2 <= 100 * bad1 && bad2 <= 1e18) || + (bad2 <= 1e8); +} // Calc badness of new element where pi1 and pi2 are replaced by pnew double CalcBadReplacePoints (const Mesh::T_POINTS & points, const MeshingParameters & mp, const Element & elem, double h, PointIndex &pi1, PointIndex &pi2, MeshPoint &pnew) @@ -28,6 +36,106 @@ double CalcBadReplacePoints (const Mesh::T_POINTS & points, const MeshingParamet return CalcTetBadness (*p[0], *p[1], *p[2], *p[3], h, mp); } +static ArrayMem SplitElement (Element old, PointIndex pi0, PointIndex pi1, PointIndex pinew) +{ + ArrayMem new_elements; + // split element by cutting edge pi0,pi1 at pinew + auto np = old.GetNP(); + old.flags.illegal_valid = 0; + if(np == 4) + { + // Split tet into two tets + Element newel0 = old; + Element newel1 = old; + for (int i : Range(4)) + { + if(newel0[i] == pi0) newel0[i] = pinew; + if(newel1[i] == pi1) newel1[i] = pinew; + } + new_elements.Append(newel0); + new_elements.Append(newel1); + } + else if (np == 5) + { + // split pyramid into pyramid and two tets + Element new_pyramid = old; + new_pyramid[4] = pinew; + new_elements.Append(new_pyramid); + + auto pibase = (pi0==old[4]) ? pi1 : pi0; + auto pitop = (pi0==old[4]) ? pi0 : pi1; + + Element new_tet0 = old; + Element new_tet1 = old; + new_tet0.SetType(TET); + new_tet1.SetType(TET); + + size_t pibase_index=0; + for(auto i : Range(4)) + if(old[i]==pibase) + pibase_index = i; + + new_tet0[0] = old[(pibase_index+1)%4]; + new_tet0[1] = old[(pibase_index+2)%4]; + new_tet0[2] = pinew; + new_tet0[3] = pitop; + new_elements.Append(new_tet0); + + new_tet1[0] = old[(pibase_index+2)%4]; + new_tet1[1] = old[(pibase_index+3)%4]; + new_tet1[2] = pinew; + new_tet1[3] = pitop; + new_elements.Append(new_tet1); + } + + return new_elements; +}; + +static double SplitElementBadness (const Mesh::T_POINTS & points, const MeshingParameters & mp, Element old, PointIndex pi0, PointIndex pi1, MeshPoint & pnew) +{ + double badness = 0; + auto np = old.GetNP(); + PointIndex dummy{-1}; + if(np == 4) + { + // Split tet into two tets + badness += CalcBadReplacePoints ( points, mp, old, 0, pi0, dummy, pnew ); + badness += CalcBadReplacePoints ( points, mp, old, 0, pi1, dummy, pnew ); + } + else if (np == 5) + { + // split pyramid into pyramid and two tets + auto pibase = (pi0==old[4]) ? pi1 : pi0; + auto pitop = (pi0==old[4]) ? pi0 : pi1; + + badness += CalcBadReplacePoints ( points, mp, old, 0, pitop, dummy, pnew ); + + Element tet = old; + tet.SetType(TET); + + size_t pibase_index=0; + for(auto i : Range(4)) + if(old[i]==pibase) + pibase_index = i; + + MeshPoint p[4]; + p[0] = points[old[(pibase_index+1)%4]]; + p[1] = points[old[(pibase_index+2)%4]]; + p[2] = pnew; + p[3] = points[pitop]; + badness += CalcTetBadness (p[0], p[1], p[2], p[3], 0, mp); + + p[0] = points[old[(pibase_index+2)%4]]; + p[1] = points[old[(pibase_index+3)%4]]; + p[2] = pnew; + p[3] = points[pitop]; + badness += CalcTetBadness (p[0], p[1], p[2], p[3], 0, mp); + } + + return badness; +}; + + /* Combine two points to one. Set new point into the center, if both are @@ -59,6 +167,7 @@ double MeshOptimize3d :: CombineImproveEdge (Mesh & mesh, { Element & elem = mesh[ei]; if (elem.IsDeleted()) return false; + if(elem.GetType() != TET) return false; // TODO: implement case where pi0 or pi1 is top of a pyramid if (elem[0] == pi1 || elem[1] == pi1 || elem[2] == pi1 || elem[3] == pi1) { @@ -420,7 +529,7 @@ void MeshOptimize3d :: CombineImprove (Mesh & mesh, // return CombineImproveSequential(mesh, goal); - mesh.BoundaryEdge (1,2); // ensure the boundary-elements table is built + mesh.BuildBoundaryEdges(false); int np = mesh.GetNP(); int ne = mesh.GetNE(); @@ -716,7 +825,7 @@ void MeshOptimize3d :: SplitImprove (Mesh & mesh, PrintMessage (3, "SplitImprove"); (*testout) << "start SplitImprove" << "\n"; - mesh.BoundaryEdge (1,2); // ensure the boundary-elements table is built + mesh.BuildBoundaryEdges(false); ParallelFor( mesh.VolumeElements().Range(), [&] (ElementIndex ei) NETGEN_LAMBDA_INLINE { @@ -1819,12 +1928,8 @@ void MeshOptimize3d :: SwapImproveSequential (Mesh & mesh, OPTIMIZEGOAL goal, if (goal == OPT_CONFORM) - // (bad2 <= 100 * bad1 || bad2 <= 1e6)) { - bool nottoobad = - (bad2 <= bad1) || - (bad2 <= 100 * bad1 && bad2 <= 1e18) || - (bad2 <= 1e8); + bool nottoobad = NotTooBad(bad1, bad2); for (int k = l+1; k <= nsuround + l - 2; k++) { @@ -2015,6 +2120,8 @@ double MeshOptimize3d :: SwapImproveEdge (Mesh & mesh, OPTIMIZEGOAL goal, } } + bool have_bad_element = false; + for (ElementIndex ei : hasbothpoints) { if (mesh[ei].GetType () != TET) @@ -2037,10 +2144,13 @@ double MeshOptimize3d :: SwapImproveEdge (Mesh & mesh, OPTIMIZEGOAL goal, if ((goal == OPT_LEGAL) && mesh.LegalTet (mesh[ei]) && - CalcBad (mesh.Points(), mesh[ei], 0) < 1e3) - return 0.0; + CalcBad (mesh.Points(), mesh[ei], 0) >= 1e3) + have_bad_element = true; } + if ((goal == OPT_LEGAL) && !have_bad_element) + return 0.0; + int nsuround = hasbothpoints.Size(); int mattyp = mesh[hasbothpoints[0]].GetIndex(); @@ -2335,21 +2445,22 @@ double MeshOptimize3d :: SwapImproveEdge (Mesh & mesh, OPTIMIZEGOAL goal, bool swap2, swap3; - if (goal != OPT_CONFORM) + if (goal == OPT_CONFORM) + { + swap2 = mesh.BoundaryEdge (pi3, pi5) && NotTooBad(bad1, bad2); + swap3 = mesh.BoundaryEdge (pi4, pi6) && NotTooBad(bad1, bad3); + + if(swap2 || swap3) + d_badness = IMPROVEMENT_CONFORMING_EDGE; + } + + if (goal != OPT_CONFORM || (!swap2 && !swap3)) { swap2 = (bad2 < bad1) && (bad2 < bad3); swap3 = !swap2 && (bad3 < bad1); - } - else - { - if (mesh.BoundaryEdge (pi3, pi5)) bad2 /= 1e6; - if (mesh.BoundaryEdge (pi4, pi6)) bad3 /= 1e6; - - swap2 = (bad2 < bad1) && (bad2 < bad3); - swap3 = !swap2 && (bad3 < bad1); + d_badness = swap2 ? bad2-bad1 : bad3-bad1; } - d_badness = swap2 ? bad2-bad1 : bad3-bad1; if(check_only) return d_badness; @@ -2500,12 +2611,8 @@ double MeshOptimize3d :: SwapImproveEdge (Mesh & mesh, OPTIMIZEGOAL goal, if (goal == OPT_CONFORM) - // (bad2 <= 100 * bad1 || bad2 <= 1e6)) { - bool nottoobad = - (bad2 <= bad1) || - (bad2 <= 100 * bad1 && bad2 <= 1e18) || - (bad2 <= 1e8); + bool nottoobad = NotTooBad(bad1, bad2); for (int k = l+1; k <= nsuround + l - 2; k++) { @@ -2607,7 +2714,7 @@ void MeshOptimize3d :: SwapImprove (Mesh & mesh, OPTIMIZEGOAL goal, int np = mesh.GetNP(); int ne = mesh.GetNE(); - mesh.BoundaryEdge (1,2); // ensure the boundary-elements table is built + mesh.BuildBoundaryEdges(false); auto elementsonnode = mesh.CreatePoint2ElementTable(); @@ -3610,6 +3717,18 @@ double MeshOptimize3d :: SwapImprove2 ( Mesh & mesh, OPTIMIZEGOAL goal, ElementI FlatArray row = elementsonnode[pi1]; + for(auto ei : row) + if (mesh[ei].IsDeleted()) return 0.0; + + for(auto ei : elementsonnode[pi2]) + if (mesh[ei].IsDeleted()) return 0.0; + + for(auto ei : elementsonnode[pi3]) + if (mesh[ei].IsDeleted()) return 0.0; + + for(auto ei : elementsonnode[pi4]) + if (mesh[ei].IsDeleted()) return 0.0; + for (int k = 0; k < row.Size(); k++) { ElementIndex eli2 = row[k]; @@ -3617,7 +3736,6 @@ double MeshOptimize3d :: SwapImprove2 ( Mesh & mesh, OPTIMIZEGOAL goal, ElementI if ( eli1 != eli2 ) { Element & elem2 = mesh[eli2]; - if (elem2.IsDeleted()) continue; if (elem2.GetType() != TET) continue; @@ -3815,7 +3933,7 @@ void MeshOptimize3d :: SwapImprove2 (Mesh & mesh, OPTIMIZEGOAL goal) // return SwapImprove2Sequential(mesh, goal); - mesh.BoundaryEdge (1,2); // ensure the boundary-elements table is built + mesh.BuildBoundaryEdges(false); int cnt = 0; double bad1, bad2; @@ -3886,8 +4004,12 @@ void MeshOptimize3d :: SwapImprove2 (Mesh & mesh, OPTIMIZEGOAL goal) QuickSort(faces_with_improvement); for (auto [dummy, eli,j] : faces_with_improvement) + { + if(mesh[eli].IsDeleted()) + continue; if(SwapImprove2( mesh, goal, eli, j, elementsonnode, belementsonnode, false) < 0.0) cnt++; + } PrintMessage (5, cnt, " swaps performed"); @@ -3897,6 +4019,207 @@ void MeshOptimize3d :: SwapImprove2 (Mesh & mesh, OPTIMIZEGOAL goal) (*testout) << "swapimprove2 done" << "\n"; } +double MeshOptimize3d :: SplitImprove2Element (Mesh & mesh, + ElementIndex ei, + const Table & elements_of_point, + const Array & el_badness, + bool check_only) +{ + auto & el = mesh[ei]; + if(el.GetType() != TET) + return false; + + // Optimize only bad elements + if(el_badness[ei] < 100) + return false; + + // search for very flat tets, with two disjoint edges nearly crossing, like a rectangle with diagonals + static constexpr int tetedges[6][2] = + { { 0, 1 }, { 0, 2 }, { 0, 3 }, + { 1, 2 }, { 1, 3 }, { 2, 3 } }; + + int minedge = -1; + double mindist = 1e99; + double minlam0, minlam1; + + for (int i : Range(3)) + { + auto pi0 = el[tetedges[i][0]]; + auto pi1 = el[tetedges[i][1]]; + auto pi2 = el[tetedges[5-i][0]]; + auto pi3 = el[tetedges[5-i][1]]; + + double lam0, lam1; + double dist = MinDistLL2(mesh[pi0], mesh[pi1], mesh[pi2], mesh[pi3], lam0, lam1 ); + if(dist has_both_points0; + ArrayMem has_both_points1; + + Point3d p[4] = { mesh[el[0]], mesh[el[1]], mesh[el[2]], mesh[el[3]] }; + auto center = Center(p[0]+minlam0*(p[1]-p[0]), p[2]+minlam1*(p[3]-p[2])); + MeshPoint pnew; + + pnew(0) = center.X(); + pnew(1) = center.Y(); + pnew(2) = center.Z(); + + // find all tets with edge (pi0,pi1) or (pi2,pi3) + for (auto ei0 : elements_of_point[pi0] ) + { + Element & elem = mesh[ei0]; + if (elem.IsDeleted()) return false; + if (ei0 == ei) continue; + + if (elem[0] == pi1 || elem[1] == pi1 || elem[2] == pi1 || elem[3] == pi1 || (elem.GetNP()==5 && elem[4]==pi1) ) + if(!has_both_points0.Contains(ei0)) + has_both_points0.Append (ei0); + } + + for (auto ei1 : elements_of_point[pi2] ) + { + Element & elem = mesh[ei1]; + if (elem.IsDeleted()) return false; + if (ei1 == ei) continue; + + if (elem[0] == pi3 || elem[1] == pi3 || elem[2] == pi3 || elem[3] == pi3 || (elem.GetNP()==5 && elem[4]==pi3)) + if(!has_both_points1.Contains(ei1)) + has_both_points1.Append (ei1); + } + + double badness_before = el_badness[ei]; + double badness_after = 0.0; + + for (auto ei0 : has_both_points0) + { + if(mesh[ei0].GetType()!=TET) + return false; + badness_before += el_badness[ei0]; + badness_after += SplitElementBadness (mesh.Points(), mp, mesh[ei0], pi0, pi1, pnew); + } + for (auto ei1 : has_both_points1) + { + if(mesh[ei1].GetType()!=TET) + return false; + badness_before += el_badness[ei1]; + badness_after += SplitElementBadness (mesh.Points(), mp, mesh[ei1], pi2, pi3, pnew); + } + + if(check_only) + return badness_after-badness_before; + + if(badness_after new point where diagonals cross, remove the flat tet +void MeshOptimize3d :: SplitImprove2 (Mesh & mesh) +{ + static Timer t("MeshOptimize3d::SplitImprove2"); RegionTimer reg(t); + static Timer tsearch("Search"); + static Timer topt("Optimize"); + + int ne = mesh.GetNE(); + auto elements_of_point = mesh.CreatePoint2ElementTable(); + int ntasks = 4*ngcore::TaskManager::GetNumThreads(); + + const char * savetask = multithread.task; + multithread.task = "Optimize Volume: Split Improve 2"; + + Array el_badness (ne); + + ParallelForRange(Range(ne), [&] (auto myrange) + { + for (ElementIndex ei : myrange) + { + if(mp.only3D_domain_nr && mp.only3D_domain_nr != mesh[ei].GetIndex()) + continue; + el_badness[ei] = CalcBad (mesh.Points(), mesh[ei], 0); + } + }); + + mesh.BuildBoundaryEdges(false); + + Array> split_candidates(ne); + std::atomic improvement_counter(0); + + tsearch.Start(); + ParallelForRange(Range(ne), [&] (auto myrange) + { + for(ElementIndex ei : myrange) + { + if(mp.only3D_domain_nr && mp.only3D_domain_nr != mesh[ei].GetIndex()) + continue; + double d_badness = SplitImprove2Element(mesh, ei, elements_of_point, el_badness, true); + if(d_badness<0.0) + { + int index = improvement_counter++; + split_candidates[index] = make_tuple(d_badness, ei); + } + } + }, ntasks); + tsearch.Stop(); + + auto elements_with_improvement = split_candidates.Part(0, improvement_counter.load()); + QuickSort(elements_with_improvement); + + size_t cnt = 0; + topt.Start(); + for(auto [d_badness, ei] : elements_with_improvement) + { + if( SplitImprove2Element(mesh, ei, elements_of_point, el_badness, false) < 0.0) + cnt++; + } + topt.Stop(); + + PrintMessage (5, cnt, " elements split"); + (*testout) << "SplitImprove2 done" << "\n"; + + if(cnt>0) + mesh.Compress(); + multithread.task = savetask; +} + /* void Mesh :: SwapImprove2 (OPTIMIZEGOAL goal) diff --git a/libsrc/meshing/improve3.hpp b/libsrc/meshing/improve3.hpp index 5bced8a1..b518a910 100644 --- a/libsrc/meshing/improve3.hpp +++ b/libsrc/meshing/improve3.hpp @@ -26,6 +26,9 @@ public: void SplitImprove (Mesh & mesh, OPTIMIZEGOAL goal = OPT_QUALITY); void SplitImproveSequential (Mesh & mesh, OPTIMIZEGOAL goal = OPT_QUALITY); double SplitImproveEdge (Mesh & mesh, OPTIMIZEGOAL goal, Table & elementsonnode, Array &elerrs, NgArray &locfaces, double badmax, PointIndex pi1, PointIndex pi2, PointIndex ptmp, bool check_only=false); + + void SplitImprove2 (Mesh & mesh); + double SplitImprove2Element (Mesh & mesh, ElementIndex ei, const Table & elements_of_point, const Array & elerrs, bool check_only); double SwapImproveEdge (Mesh & mesh, OPTIMIZEGOAL goal, const NgBitArray * working_elements, Table & elementsonnode, INDEX_3_HASHTABLE & faces, PointIndex pi1, PointIndex pi2, bool check_only=false); diff --git a/libsrc/meshing/localh.cpp b/libsrc/meshing/localh.cpp index d8445b11..3afe8e25 100644 --- a/libsrc/meshing/localh.cpp +++ b/libsrc/meshing/localh.cpp @@ -23,6 +23,13 @@ namespace netgen hopt = 2 * h2; } + void GradingBox :: DoArchive(Archive& ar) + { + ar & xmid[0] & xmid[1] & xmid[2] & h2 & father & hopt & + flags.cutboundary & flags.isinner & flags.oldcell & flags.pinner; + for(auto i : Range(8)) + ar & childs[i]; + } BlockAllocator GradingBox :: ball(sizeof (GradingBox)); @@ -93,6 +100,11 @@ namespace netgen root->DeleteChilds(); } + void LocalH :: DoArchive(Archive& ar) + { + ar & root & grading & boxes & boundingbox & dimension; + } + void LocalH :: SetH (Point<3> p, double h) { if (dimension == 2) @@ -381,7 +393,12 @@ namespace netgen return; } - box->flags.cutboundary = 1; + if (!box->flags.cutboundary) + for (int i = 0; i < 8; i++) + if (box->childs[i]) + box->childs[i]->flags.cutboundary = false; + + box->flags.cutboundary = true; for (int i = 0; i < 8; i++) if (box->childs[i]) CutBoundaryRec (pmin, pmax, box->childs[i]); @@ -547,13 +564,19 @@ namespace netgen void LocalH :: FindInnerBoxes (AdFront2 * adfront, int (*testinner)(const Point<2> & p1)) { - static int timer = NgProfiler::CreateTimer ("LocalH::FindInnerBoxes 2d"); - NgProfiler::RegionTimer reg (timer); + static Timer t("LocalH::FindInnerBoxes 2d"); RegionTimer reg (t); + static Timer trec("LocalH::FindInnerBoxes 2d - rec"); + static Timer tinit("LocalH::FindInnerBoxes 2d - init"); + /* + tinit.Start(); for (int i = 0; i < boxes.Size(); i++) boxes[i] -> flags.isinner = 0; - + tinit.Stop(); + */ + root->flags.isinner = 0; + root->flags.cutboundary = true; Point<2> rpmid(root->xmid[0], root->xmid[1]); // , root->xmid[2]); Vec<2> rv(root->h2, root->h2); @@ -569,84 +592,75 @@ namespace netgen int nf = adfront->GetNFL(); - NgArray faceinds(nf); - NgArray > faceboxes(nf); + Array faceinds(nf); + Array> faceboxes(nf); for (int i = 0; i < nf; i++) { faceinds[i] = i; - // adfront->GetFaceBoundingBox(i, faceboxes.Elem(i)); - const FrontLine & line = adfront->GetLine(i); - faceboxes[i].Set (adfront->GetPoint (line.L().I1())); - faceboxes[i].Add (adfront->GetPoint (line.L().I2())); + Point<3> p1 = adfront->GetPoint (line.L().I1()); + Point<3> p2 = adfront->GetPoint (line.L().I2()); + + faceboxes[i].Set (Point<2> (p1(0), p1(1))); + faceboxes[i].Add (Point<2> (p2(0), p2(1))); } - + + RegionTimer regrc(trec); for (int i = 0; i < 8; i++) - FindInnerBoxesRec2 (root->childs[i], adfront, faceboxes, faceinds, nf); + FindInnerBoxesRec2 (root->childs[i], adfront, faceboxes, faceinds); // , nf); } void LocalH :: FindInnerBoxesRec2 (GradingBox * box, class AdFront2 * adfront, - NgArray > & faceboxes, - NgArray & faceinds, int nfinbox) + FlatArray> faceboxes, + FlatArray faceinds) // , int nfinbox) { if (!box) return; + + GradingBox * father = box -> father; - GradingBox * father = box -> father; - - Point3d c(box->xmid[0], box->xmid[1], 0); // box->xmid[2]); - Vec3d v(box->h2, box->h2, box->h2); - Box3d boxc(c-v, c+v); - - Point3d fc(father->xmid[0], father->xmid[1], 0); // father->xmid[2]); - Vec3d fv(father->h2, father->h2, father->h2); - Box3d fboxc(fc-fv, fc+fv); - Box3d boxcfc(c,fc); - - NgArrayMem faceused; - NgArrayMem faceused2; - NgArrayMem facenotused; - - for (int j = 0; j < nfinbox; j++) - { - // adfront->GetFaceBoundingBox (faceinds.Get(j), facebox); - const Box3d & facebox = faceboxes[faceinds[j]]; - - if (boxc.Intersect (facebox)) - faceused.Append(faceinds[j]); - else - facenotused.Append(faceinds[j]); - - if (boxcfc.Intersect (facebox)) - faceused2.Append (faceinds[j]); - } - - for (int j = 0; j < faceused.Size(); j++) - faceinds[j] = faceused[j]; - for (int j = 0; j < facenotused.Size(); j++) - faceinds[j+faceused.Size()] = facenotused[j]; - if (!father->flags.cutboundary) { box->flags.isinner = father->flags.isinner; box->flags.pinner = father->flags.pinner; + box->flags.cutboundary = false; } else - { - Point3d cf(father->xmid[0], father->xmid[1], father->xmid[2]); - + { if (father->flags.isinner) { + cout << "how is this possible ???" << endl; box->flags.pinner = 1; } else { - Point<2> c2d (c.X(), c.Y()); - Point<2> cf2d (cf.X(), cf.Y()); - bool sameside = adfront->SameSide (c2d, cf2d, &faceused2); + Point<2> c(box->xmid[0], box->xmid[1]); + Point<2> fc(father->xmid[0], father->xmid[1]); + Box<2> boxcfc(c,fc); + + // reorder: put faces cutting boxcfc first: + int iused = 0; + int inotused = faceinds.Size()-1; + while (iused <= inotused) + { + while ( (iused <= inotused) && boxcfc.Intersect (faceboxes[faceinds[iused]])) + iused++; + while ( (iused <= inotused) && !boxcfc.Intersect (faceboxes[faceinds[inotused]])) + inotused--; + if (iused < inotused) + { + Swap (faceinds[iused], faceinds[inotused]); + iused++; + inotused--; + } + } + + // bool sameside = adfront->SameSide (c2d, cf2d, &faceused2); + auto sub = faceinds.Range(0, iused); + bool sameside = adfront->SameSide (c, fc, &sub); if (sameside) box->flags.pinner = father->flags.pinner; else @@ -659,11 +673,36 @@ namespace netgen box->flags.isinner = box->flags.pinner; } - // cout << "faceused: " << faceused.Size() << ", " << faceused2.Size() << ", " << facenotused.Size() << endl; - int nf = faceused.Size(); - for (int i = 0; i < 8; i++) - FindInnerBoxesRec2 (box->childs[i], adfront, faceboxes, faceinds, nf); + + int iused = 0; + if (faceinds.Size()) + { + Point<2> c(box->xmid[0], box->xmid[1]); // box->xmid[2]); + Vec<2> v(box->h2, box->h2); + Box<2> boxc(c-v, c+v); + + // reorder again: put faces cutting boxc first: + int inotused = faceinds.Size()-1; + while (iused <= inotused) + { + while ( (iused <= inotused) && boxc.Intersect (faceboxes[faceinds[iused]])) + iused++; + while ( (iused <= inotused) && !boxc.Intersect (faceboxes[faceinds[inotused]])) + inotused--; + if (iused < inotused) + { + Swap (faceinds[iused], faceinds[inotused]); + iused++; + inotused--; + } + } + } + + + if (box->flags.isinner || box->flags.cutboundary) + for (int i = 0; i < 8; i++) + FindInnerBoxesRec2 (box->childs[i], adfront, faceboxes, faceinds.Range(0,iused)); } @@ -709,6 +748,13 @@ namespace netgen ClearFlagsRec (box->childs[i]); } + void LocalH :: ClearRootFlags () + { + root->flags.cutboundary = false; + root->flags.isinner = false; + } + + void LocalH :: ClearFlagsRec (GradingBox * box) { box->flags.cutboundary = 0; @@ -735,13 +781,17 @@ namespace netgen } } - void LocalH :: GetInnerPoints (NgArray > & points) + void LocalH :: GetInnerPoints (NgArray > & points) const { + static Timer t("GetInnerPoints"); RegionTimer reg(t); if (dimension == 2) { + GetInnerPointsRec (root, points); + /* for (int i = 0; i < boxes.Size(); i++) if (boxes[i] -> flags.isinner && boxes[i] -> HasChilds()) points.Append ( boxes[i] -> PMid() ); + */ } else { @@ -752,7 +802,18 @@ namespace netgen } + void LocalH :: GetInnerPointsRec (const GradingBox * box, NgArray > & points) const + { + if (box -> flags.isinner && box -> HasChilds()) + points.Append ( box -> PMid() ); + if (box->flags.isinner || box->flags.cutboundary) + for (int i = 0; i < 8; i++) + if (box->childs[i]) + GetInnerPointsRec (box->childs[i], points); + } + + void LocalH :: GetOuterPoints (NgArray > & points) { for (int i = 0; i < boxes.Size(); i++) diff --git a/libsrc/meshing/localh.hpp b/libsrc/meshing/localh.hpp index d15b11cd..caff6540 100644 --- a/libsrc/meshing/localh.hpp +++ b/libsrc/meshing/localh.hpp @@ -30,18 +30,28 @@ namespace netgen struct { + /* unsigned int cutboundary:1; unsigned int isinner:1; unsigned int oldcell:1; unsigned int pinner:1; + */ + bool cutboundary; + bool isinner; + bool oldcell; + bool pinner; } flags; /// GradingBox (const double * ax1, const double * ax2); + /// default constructor for Archive + GradingBox() = default; /// void DeleteChilds(); /// + void DoArchive(Archive& ar); + Point<3> PMid() const { return Point<3> (xmid[0], xmid[1], xmid[2]); } double H2() const { return h2; } @@ -72,7 +82,7 @@ namespace netgen /// double grading; /// - NgArray boxes; + Array boxes; /// Box<3> boundingbox; /// octree or quadtree @@ -83,11 +93,15 @@ namespace netgen /// LocalH (const Box<3> & box, double grading, int adimension = 3) : LocalH (box.PMin(), box.PMax(), grading, adimension) { ; } - /// + /// Default ctor for archive + LocalH() = default; + ~LocalH(); /// void Delete(); /// + void DoArchive(Archive& ar); + /// void SetGrading (double agrading) { grading = agrading; } /// void SetH (Point<3> x, double h); @@ -115,11 +129,14 @@ namespace netgen void ClearFlags () { ClearFlagsRec(root); } + void ClearRootFlags (); + /// widen refinement zone void WidenRefinement (); /// get points in inner elements - void GetInnerPoints (NgArray > & points); + void GetInnerPoints (NgArray > & points) const; + void GetInnerPointsRec (const GradingBox * box, NgArray > & points) const; /// get points in outer closure void GetOuterPoints (NgArray > & points); @@ -158,8 +175,8 @@ namespace netgen /// void FindInnerBoxesRec2 (GradingBox * box, class AdFront2 * adfront, - NgArray > & faceboxes, - NgArray & finds, int nfinbox); + FlatArray> faceboxes, + FlatArray finds); // , int nfinbox); diff --git a/libsrc/meshing/meshclass.cpp b/libsrc/meshing/meshclass.cpp index 8c340041..38f614e7 100644 --- a/libsrc/meshing/meshclass.cpp +++ b/libsrc/meshing/meshclass.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "meshing.hpp" #ifdef NG_PYTHON @@ -104,11 +105,18 @@ namespace netgen if ( mesh2.materials[i] ) materials[i] = new string ( *mesh2.materials[i] ); else materials[i] = 0; - + std::map bcmap; bcnames.SetSize( mesh2.bcnames.Size() ); for ( int i = 0; i < mesh2.bcnames.Size(); i++ ) + { if ( mesh2.bcnames[i] ) bcnames[i] = new string ( *mesh2.bcnames[i] ); else bcnames[i] = 0; + bcmap[mesh2.bcnames[i]] = bcnames[i]; + } + + // Remap string* members in FaceDescriptor to new mesh + for (auto & f : facedecoding) + f.SetBCName( bcmap[&f.GetBCName()] ); cd2names.SetSize(mesh2.cd2names.Size()); @@ -427,6 +435,7 @@ namespace netgen int inverttets = 0; // globflags.GetDefineFlag ("inverttets"); int invertsurf = 0; // globflags.GetDefineFlag ("invertsurfacemesh"); + outfile << "# Generated by NETGEN " << GetLibraryVersion("netgen") << endl << endl; outfile << "mesh3d" << "\n"; @@ -593,7 +602,20 @@ namespace netgen outfile << (*this)[pi](1)/scale << " "; outfile.width(22); outfile << (*this)[pi](2)/scale << "\n"; - } + } + + outfile << "\n" << "\n"; + outfile << "# pnum index" << "\n"; + outfile << "pointelements" << "\n"; + outfile << pointelements.Size() << "\n"; + + for (i = 0; i < pointelements.Size(); i++) + { + outfile.width(8); + outfile << pointelements[i].pnum << " "; + outfile.width(8); + outfile << pointelements[i].index << "\n"; + } if (ident -> GetMaxNr() > 0) { @@ -668,6 +690,18 @@ namespace netgen outfile << endl << endl; } + int cntcd3names = 0; + for (int ii = 0; ii> i; + + if(iline) + empty_line = false; + + iline >> s; + } + + if(!infile) + throw Exception("Reached end of file while parsing"); + } void Mesh :: Load (istream & infile) { @@ -1062,6 +1120,19 @@ namespace netgen PrintMessage (3, n, " points done"); } + if (strcmp (str, "pointelements") == 0) + { + infile >> n; + PrintMessage (3, n, " pointelements"); + for (i = 1; i <= n; i++) + { + Element0d el; + infile >> el.pnum >> el.index; + pointelements.Append (el); + } + PrintMessage (3, n, " pointelements done"); + } + if (strcmp (str, "identifications") == 0) { infile >> n; @@ -1090,11 +1161,11 @@ namespace netgen if (strcmp (str, "materials") == 0) { infile >> n; - for (i = 1; i <= n; i++) + for ( auto i : Range(n) ) { int nr; string mat; - infile >> nr >> mat; + ReadNumberAndName( infile, nr, mat ); SetMaterial (nr, mat.c_str()); } } @@ -1102,27 +1173,16 @@ namespace netgen if ( strcmp (str, "bcnames" ) == 0 ) { infile >> n; - NgArray bcnrs(n); + Array bcnrs(n); SetNBCNames(n); - for ( i = 1; i <= n; i++ ) + for ( auto i : Range(n) ) { string nextbcname; - infile >> bcnrs[i-1] >> nextbcname; - bcnames[bcnrs[i-1]-1] = new string(nextbcname); + ReadNumberAndName( infile, bcnrs[i], nextbcname ); + bcnames[bcnrs[i]-1] = new string(nextbcname); } - if ( GetDimension() == 2 ) - { - for (i = 1; i <= GetNSeg(); i++) - { - Segment & seg = LineSegment (i); - if ( seg.si <= n ) - seg.SetBCName (bcnames[seg.si-1]); - else - seg.SetBCName(0); - } - } - else + if ( GetDimension() == 3 ) { for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++) { @@ -1143,28 +1203,35 @@ namespace netgen if ( strcmp (str, "cd2names" ) == 0) { infile >> n; - NgArray cd2nrs(n); + Array cd2nrs(n); SetNCD2Names(n); - for( i=1; i<=n; i++) + for ( auto i : Range(n) ) + { + string nextcd2name; + ReadNumberAndName( infile, cd2nrs[i], nextcd2name ); + cd2names[cd2nrs[i]-1] = new string(nextcd2name); + } + if (GetDimension() < 2) { - string nextcd2name; - infile >> cd2nrs[i-1] >> nextcd2name; - cd2names[cd2nrs[i-1]-1] = new string(nextcd2name); + throw NgException("co dim 2 elements not implemented for dimension < 2"); } - if (GetDimension() == 2) + } + + if ( strcmp (str, "cd3names" ) == 0) + { + infile >> n; + Array cd3nrs(n); + SetNCD3Names(n); + for( auto i : Range(n) ) { - throw NgException("co dim 2 elements not implemented for dimension 2"); + string nextcd3name; + ReadNumberAndName( infile, cd3nrs[i], nextcd3name ); + infile >> cd3nrs[i-1] >> nextcd3name; + cd3names[cd3nrs[i-1]-1] = new string(nextcd3name); } - else + if (GetDimension() < 3) { - for (i = 1; i<= GetNSeg(); i++) - { - Segment & seg = LineSegment(i); - if ( seg.edgenr <= n ) - seg.SetBCName (cd2names[seg.edgenr-1]); - else - seg.SetBCName(0); - } + throw NgException("co dim 3 elements not implemented for dimension < 3"); } } @@ -1293,6 +1360,140 @@ namespace netgen void Mesh :: DoArchive (Archive & archive) { + static Timer t("Mesh::Archive"); RegionTimer r(t); + +#ifdef PARALLEL + auto comm = GetCommunicator(); + if (archive.IsParallel() && comm.Size() > 1) + { // parallel pickling supported only for output archives + if (comm.Rank() == 0) + archive & dimension; + + auto rank = comm.Rank(); + + auto & partop = GetParallelTopology(); + + // global enumration of points: + // not used now, but will be needed for refined meshes + // GridFunciton pickling is not compatible, now + // should go to paralleltopology + + + + // merge points + Array globnum(points.Size()); + PointIndex maxglob = -1; + for (auto pi : Range(points)) + { + globnum[pi] = partop.GetGlobalPNum(pi); + // globnum[pi] = global_pnums[pi]; + maxglob = max(globnum[pi], maxglob); + } + + maxglob = comm.AllReduce (maxglob, MPI_MAX); + int numglob = maxglob+1-PointIndex::BASE; + if (comm.Rank() > 0) + { + comm.Send (globnum, 0, 200); + comm.Send (points, 0, 200); + } + else + { + Array globnumi; + Array pointsi; + Array globpoints(numglob); + for (int j = 1; j < comm.Size(); j++) + { + comm.Recv (globnumi, j, 200); + comm.Recv (pointsi, j, 200); + for (auto i : Range(globnumi)) + globpoints[globnumi[i]] = pointsi[i]; + } + archive & globpoints; + } + + + // sending surface elements + auto copy_el2d (surfelements); + for (auto & el : copy_el2d) + for (auto & pi : el.PNums()) + pi = globnum[pi]; + + if (comm.Rank() > 0) + comm.Send(copy_el2d, 0, 200); + else + { + Array el2di; + for (int j = 1; j < comm.Size(); j++) + { + comm.Recv(el2di, j, 200); + for (auto & el : el2di) + copy_el2d += el; + } + archive & copy_el2d; + } + + + // sending volume elements + auto copy_el3d (volelements); + for (auto & el : copy_el3d) + for (auto & pi : el.PNums()) + pi = globnum[pi]; + + if (comm.Rank() > 0) + comm.Send(copy_el3d, 0, 200); + else + { + Array el3di; + for (int j = 1; j < comm.Size(); j++) + { + comm.Recv(el3di, j, 200); + for (auto & el : el3di) + copy_el3d += el; + } + archive & copy_el3d; + } + + + // sending 1D elements + auto copy_el1d (segments); + for (auto & el : copy_el1d) + for (auto & pi : el.pnums) + if (pi != PointIndex(PointIndex::INVALID)) + pi = globnum[pi]; + + if (comm.Rank() > 0) + comm.Send(copy_el1d, 0, 200); + else + { + Array el1di; + for (int j = 1; j < comm.Size(); j++) + { + comm.Recv(el1di, j, 200); + for (auto & el : el1di) + copy_el1d += el; + } + archive & copy_el1d; + } + + if (comm.Rank() == 0) + { + archive & facedecoding; + archive & materials & bcnames & cd2names & cd3names; + auto mynv = numglob; + archive & mynv; // numvertices; + archive & *ident; + + archive.Shallow(geometry); + archive & *curvedelems; + } + + if (comm.Rank() == 0) + return; + } +#endif + + archive & dimension; archive & points; archive & surfelements; @@ -1593,12 +1794,12 @@ namespace netgen volelements.SetAllocSize(nel); } - - void Mesh :: BuildBoundaryEdges(void) + void Mesh :: BuildBoundaryEdges(bool rebuild) { static Timer t("Mesh::BuildBoundaryEdges"); RegionTimer reg(t); - // delete boundaryedges; + if(!rebuild && boundaryedges) + return; boundaryedges = make_unique> (3 * (GetNSE() + GetNOpenElements()) + GetNSeg() + 1); @@ -3782,7 +3983,7 @@ namespace netgen (*this)[tri[0]].GetLayer() != (*this)[tri[2]].GetLayer()) { incons_layers = 1; - cout << "inconsistent layers in triangle" << endl; + // cout << "inconsistent layers in triangle" << endl; } const netgen::Point<3> *trip1[3], *trip2[3]; @@ -3796,16 +3997,19 @@ namespace netgen { overlap = 1; lock_guard guard(m); - PrintWarning ("Intersecting elements " - ,int(sei), " and ", int(sej)); + if(!incons_layers) + { + PrintWarning ("Intersecting elements " + ,int(sei), " and ", int(sej)); - (*testout) << "Intersecting: " << endl; - (*testout) << "openelement " << sei << " with open element " << sej << endl; + (*testout) << "Intersecting: " << endl; + (*testout) << "openelement " << sei << " with open element " << sej << endl; - cout << "el1 = " << tri << endl; - cout << "el2 = " << tri2 << endl; - cout << "layer1 = " << (*this)[tri[0]].GetLayer() << endl; - cout << "layer2 = " << (*this)[tri2[0]].GetLayer() << endl; + cout << "el1 = " << tri << endl; + cout << "el2 = " << tri2 << endl; + cout << "layer1 = " << (*this)[tri[0]].GetLayer() << endl; + cout << "layer2 = " << (*this)[tri2[0]].GetLayer() << endl; + } for (int k = 1; k <= 3; k++) (*testout) << tri.PNum(k) << " "; @@ -4647,6 +4851,11 @@ namespace netgen for (auto pi : volelements[ei].PNums()) box.Add (points[pi]); + auto & el = volelements[ei]; + if(el.IsCurved()) + box.Increase(1.2*box.Diam()); + + elementsearchtree -> Insert (box, ei+1); } } @@ -5059,7 +5268,7 @@ namespace netgen //(*testout) << "col1 " << col1 << " col2 " << col2 << " col3 " << col3 << " rhs " << rhs << endl; //(*testout) << "sol " << sol << endl; - if (SurfaceElement(element).GetType() ==TRIG6) + if (SurfaceElement(element).GetType() ==TRIG6 || curvedelems->IsSurfaceElementCurved(element-1)) { netgen::Point<2> lam(1./3,1./3); Vec<3> rhs; @@ -5094,6 +5303,14 @@ namespace netgen sol.X() = lam(0); sol.Y() = lam(1); + + if (SurfaceElement(element).GetType() !=TRIG6 ) + { + sol.Z() = sol.X(); + sol.X() = sol.Y(); + sol.Y() = 1.0 - sol.Z() - sol.X(); + } + } if (sol.X() >= -eps && sol.Y() >= -eps && sol.X() + sol.Y() <= 1+eps) @@ -5470,6 +5687,47 @@ namespace netgen { if (dimension == 2) { + double vlam[3]; + int velement = GetElementOfPoint(p, vlam, NULL, build_searchtree, allowindex); + if(velement == 0) + return 0; + + vlam[2] = 1.-vlam[0] - vlam[1]; + NgArray edges; + topology.GetSurfaceElementEdges(velement, edges); + Array segs(edges.Size()); + for(auto i : Range(edges)) + segs[i] = topology.GetSegmentOfEdge(edges[i]); + + for(auto i : Range(segs)) + { + if(IsInvalid(segs[i])) + continue; + auto& el = SurfaceElement(velement); + if(el.GetType() == TRIG) + { + double seg_lam; + double lam; + auto seg = LineSegment(segs[i]); + for(auto k : Range(3)) + { + if(seg[0] == el[k]) + lam = vlam[k]; + if(seg[1] == el[k]) + seg_lam = vlam[k]; + } + if(1.- seg_lam - lam < 1e-5) + { + // found point close to segment -> use barycentric coordinates directly + lami[0] = lam; + return int(segs[i])+1; + } + } + else + throw NgException("Quad not implemented yet!"); + } + + return 0; throw NgException("GetSurfaceElementOfPoint not yet implemented for 2D meshes"); } else @@ -5503,6 +5761,26 @@ namespace netgen if(faces[i] == 0) continue; + auto & el = VolumeElement(velement); + + if (el.GetType() == TET) + { + double lam4[4] = { vlam[0], vlam[1], vlam[2], 1.0-vlam[0]-vlam[1]-vlam[2] }; + double face_lam = lam4[i]; + if(face_lam < 1e-5) + { + // found volume point very close to a face -> use barycentric coordinates directly + lami[2] = 0.0; + auto sel = SurfaceElement(faces[i]); + + for(auto j : Range(1,3)) + for(auto k : Range(4)) + if(sel[j] == el[k]) + lami[j-1] = lam4[k]/(1.0-face_lam); + return faces[i]; + } + } + if(indices && indices->Size() != 0) { if(indices->Contains(SurfaceElement(faces[i]).GetIndex()) && @@ -5740,6 +6018,12 @@ namespace netgen SurfaceElement(els_of_face[i]).next = facedecoding[ind-1].firstelement; facedecoding[ind-1].firstelement = els_of_face[i]; } + + // map the segments + for(auto& seg : segments) + if(!usedp.Test(seg[0]) || !usedp.Test(seg[1])) + if(seg.si == fdi) + seg.si = nface; } fdi++; @@ -6112,7 +6396,63 @@ namespace netgen // } // #endif + int Mesh::IdentifyPeriodicBoundaries(const string &s1, + const string &s2, + const Transformation<3> &mapping, + double pointTolerance) + { + auto nr = ident->GetMaxNr() + 1; + ident->SetType(nr, Identifications::PERIODIC); + double lami[4]; + set identified_points; + if(pointTolerance < 0.) + { + Point3d pmin, pmax; + GetBox(pmin, pmax); + pointTolerance = 1e-8 * (pmax-pmin).Length(); + } + for(const auto& se : surfelements) + { + if(GetBCName(se.index-1) != s1) + continue; + for(const auto& pi : se.PNums()) + { + if(identified_points.find(pi) != identified_points.end()) + continue; + auto pt = (*this)[pi]; + auto mapped_pt = mapping(pt); + auto other_nr = GetElementOfPoint(mapped_pt, lami, true); + int index = -1; + if(other_nr != 0) + { + auto other_el = VolumeElement(other_nr); + for(auto i : Range(other_el.PNums().Size())) + if((mapped_pt - (*this)[other_el.PNums()[i]]).Length() < pointTolerance) + { + index = i; + break; + } + if(index == -1) + { + cout << "point coordinates = " << pt << endl; + cout << "mapped coordinates = " << mapped_pt << endl; + throw Exception("Did not find mapped point with nr " + ToString(pi) + ", are you sure your mesh is periodic?"); + } + auto other_pi = other_el.PNums()[index]; + identified_points.insert(pi); + ident->Add(pi, other_pi, nr); + } + else + { + cout << "point coordinates = " << pt << endl; + cout << "mapped coordinates = " << mapped_pt << endl; + throw Exception("Mapped point with nr " + ToString(pi) + " is outside of mesh, are you sure your mesh is periodic?"); + } + } + } + return nr; + } void Mesh :: InitPointCurve(double red, double green, double blue) const { @@ -6230,7 +6570,9 @@ namespace netgen for (SurfaceElementIndex ei : myrange) for (PointIndex pi : (*this)[ei].PNums()) creator.Add (pi, ei); - }, ngcore::TasksPerThread(4)); + }, + // ngcore::TasksPerThread(4)); + (surfelements.Size()>100) ? ngcore::TasksPerThread(4) : 1); } else { @@ -6252,7 +6594,8 @@ namespace netgen { for (PointIndex pi : myrange) QuickSort(elementsonnode[pi]); - }); + }, + (surfelements.Size()>100) ? ngcore::TasksPerThread(1) : 1); return move(elementsonnode); } @@ -6383,9 +6726,10 @@ namespace netgen return 1; } - void Mesh :: UpdateTopology (TaskManager tm, - Tracer tracer) + void Mesh :: UpdateTopology (NgTaskManager tm, + NgTracer tracer) { + static Timer t("Update Topology"); RegionTimer reg(t); topology.Update(tm, tracer); (*tracer)("call update clusters", false); clusters->Update(tm, tracer); @@ -6397,6 +6741,7 @@ namespace netgen paralleltop->UpdateCoarseGrid(); } #endif + updateSignal.Emit(); } void Mesh :: BuildCurvedElements (const Refinement * ref, int aorder, bool arational) @@ -6493,7 +6838,7 @@ namespace netgen return defaultstring; if (bcnr < 0 || bcnr >= bcnames.Size()) - throw NgException ("illegal bc-number"); + throw RangeException("Illegal bc number ", bcnr, 0, bcnames.Size()); if ( bcnames[bcnr] ) return *bcnames[bcnr]; @@ -6691,4 +7036,90 @@ namespace netgen if (surfelementht) surfelementht->PrintMemInfo (cout); } + + shared_ptr Mesh :: Mirror ( netgen::Point<3> p_plane, Vec<3> n_plane ) + { + Mesh & m = *this; + auto nm_ = make_shared(); + Mesh & nm = *nm_; + nm = m; + + Point3d pmin, pmax; + GetBox(pmin, pmax); + auto v = pmax-pmin; + double eps = v.Length()*1e-8; + + auto onPlane = [&] (const MeshPoint & p) -> bool + { + auto v = p_plane-p; + auto l = v.Length(); + if(l PointIndex + { + auto & p = m[pi]; + + auto v = p_plane-p; + auto l = v.Length(); + if(l point_map; + point_map.SetSize(GetNP()); + point_map = -1; + + for(auto pi : Range(points)) + point_map[pi] = mirror(pi); + + for(auto & el : VolumeElements()) + { + auto nel = el; + for(auto i : Range(el.GetNP())) + nel[i] = point_map[el[i]]; + nm.AddVolumeElement(nel); + } + + for (auto ei : Range(SurfaceElements())) + { + auto & el = m[ei]; + auto nel = el; + for(auto i : Range(el.GetNP())) + nel[i] = point_map[el[i]]; + + if(!(nel==el)) + nm.AddSurfaceElement(nel); + } + + for (auto ei : Range(LineSegments())) + { + auto & el = LineSegments()[ei]; + auto nel = el; + bool is_same = true; + + for(auto i : Range(el.GetNP())) + { + auto pi = el[i]; + nel[i] = point_map[pi]; + if(point_map[pi]!=pi) + is_same = false; + } + + if(!is_same) + nm.AddSegment(nel); + } + + return nm_; + } + } diff --git a/libsrc/meshing/meshclass.hpp b/libsrc/meshing/meshclass.hpp index e455f02e..e3e61477 100644 --- a/libsrc/meshing/meshclass.hpp +++ b/libsrc/meshing/meshclass.hpp @@ -38,7 +38,7 @@ namespace netgen NgMPI_Comm comm; /// line-segments at edges - Array segments; + Array segments; /// surface elements, 2d-inner elements Array surfelements; /// volume elements @@ -69,7 +69,7 @@ namespace netgen /** Representation of local mesh-size h */ - unique_ptr lochfunc; + shared_ptr lochfunc; /// double hglob; /// @@ -156,10 +156,10 @@ namespace netgen shared_ptr geometry; - private: - void BuildBoundaryEdges(void); public: + void BuildBoundaryEdges(bool rebuild=true); + bool PointContainedIn2DElement(const Point3d & p, double lami[3], const int element, @@ -172,6 +172,7 @@ namespace netgen const int element) const; public: + Signal<> updateSignal; // store coarse mesh before hp-refinement unique_ptr> hpelements; @@ -464,6 +465,10 @@ namespace netgen bool HasLocalHFunction () { return lochfunc != nullptr; } /// LocalH & LocalHFunction () { return * lochfunc; } + + shared_ptr GetLocalH() const { return lochfunc; } + void SetLocalH(shared_ptr loch) { lochfunc = loch; } + /// bool LocalHFunctionGenerated(void) const { return (lochfunc != NULL); } @@ -640,7 +645,7 @@ namespace netgen int AddEdgeDescriptor(const EdgeDescriptor & fd) { edgedecoding.Append(fd); return edgedecoding.Size() - 1; } - auto GetCommunicator() const { return this->comm; } + auto & GetCommunicator() const { return this->comm; } void SetCommunicator(NgMPI_Comm acomm); /// @@ -710,6 +715,11 @@ namespace netgen FaceDescriptor & GetFaceDescriptor (int i) { return facedecoding.Elem(i); } + int IdentifyPeriodicBoundaries(const string& s1, + const string& s2, + const Transformation<3>& mapping, + double pointTolerance); + // #ifdef NONE // /* // Identify points pi1 and pi2, due to @@ -769,11 +779,11 @@ namespace netgen DLL_HEADER bool PureTetMesh () const; - const MeshTopology & GetTopology () const - { return topology; } + const MeshTopology & GetTopology () const { return topology; } + MeshTopology & GetTopology () { return topology; } - DLL_HEADER void UpdateTopology (TaskManager tm = &DummyTaskManager, - Tracer tracer = &DummyTracer); + DLL_HEADER void UpdateTopology (NgTaskManager tm = &DummyTaskManager, + NgTracer tracer = &DummyTracer); class CurvedElements & GetCurvedElements () const { return *curvedelems; } @@ -792,7 +802,7 @@ namespace netgen double area; public: CSurfaceArea (const Mesh & amesh) - : mesh(amesh), valid(false) { ; } + : mesh(amesh), valid(false), area(0.) { ; } void Add (const Element2d & sel) { @@ -918,7 +928,17 @@ namespace netgen NgArray & segment_weights){ } #endif + shared_ptr Mirror( netgen::Point<3> p, Vec<3> n ); + private: + MemoryTracer mem_tracer = {"Mesh", + points, "points", + segments, "segments", + surfelements, "surfelements", + volelements, "volelements" + }; + public: + const MemoryTracer & GetMemoryTracer() { return mem_tracer; } }; inline ostream& operator<<(ostream& ost, const Mesh& mesh) diff --git a/libsrc/meshing/meshfunc.cpp b/libsrc/meshing/meshfunc.cpp index f21e0812..f3ebccde 100644 --- a/libsrc/meshing/meshfunc.cpp +++ b/libsrc/meshing/meshfunc.cpp @@ -684,6 +684,7 @@ namespace netgen { case 'c': optmesh.CombineImprove(mesh3d, OPT_REST); break; case 'd': optmesh.SplitImprove(mesh3d); break; + case 'D': optmesh.SplitImprove2(mesh3d); break; case 's': optmesh.SwapImprove(mesh3d); break; // case 'u': optmesh.SwapImproveSurface(mesh3d); break; case 't': optmesh.SwapImprove2(mesh3d); break; diff --git a/libsrc/meshing/meshfunc2d.cpp b/libsrc/meshing/meshfunc2d.cpp index 88955a36..84f5d399 100644 --- a/libsrc/meshing/meshfunc2d.cpp +++ b/libsrc/meshing/meshfunc2d.cpp @@ -20,6 +20,19 @@ namespace netgen } mesh.Compress(); + bool optimize_swap_separate_faces = false; + if(!mp.quad) + { + bool mixed = false; + ParallelFor( Range(mesh.GetNSE()), [&] (auto i) NETGEN_LAMBDA_INLINE + { + if (mesh[SurfaceElementIndex(i)].GetNP() != 3) + mixed = true; + }); + if(mixed) + optimize_swap_separate_faces = true; + } + const char * optstr = mp.optimize2d.c_str(); int optsteps = mp.optsteps2d; @@ -31,16 +44,42 @@ namespace netgen { case 's': { // topological swap - MeshOptimize2d meshopt(mesh); + MeshOptimize2d meshopt(mesh); meshopt.SetMetricWeight (mp.elsizeweight); - meshopt.EdgeSwapping (0); + + if(optimize_swap_separate_faces) + { + for(auto i : Range(1, mesh.GetNFD()+1)) + { + meshopt.SetFaceIndex(i); + meshopt.EdgeSwapping (0); + } + } + else + { + meshopt.SetFaceIndex(0); + meshopt.EdgeSwapping (0); + } break; } case 'S': { // metric swap MeshOptimize2d meshopt(mesh); meshopt.SetMetricWeight (mp.elsizeweight); - meshopt.EdgeSwapping (1); + + if(optimize_swap_separate_faces) + { + for(auto i : Range(1, mesh.GetNFD()+1)) + { + meshopt.SetFaceIndex(i); + meshopt.EdgeSwapping (1); + } + } + else + { + meshopt.SetFaceIndex(0); + meshopt.EdgeSwapping (1); + } break; } case 'm': diff --git a/libsrc/meshing/meshing.hpp b/libsrc/meshing/meshing.hpp index 35cb9add..253f2f93 100644 --- a/libsrc/meshing/meshing.hpp +++ b/libsrc/meshing/meshing.hpp @@ -57,10 +57,12 @@ namespace netgen #include "hprefinement.hpp" #include "boundarylayer.hpp" #include "specials.hpp" + } #include "validate.hpp" #include "basegeom.hpp" +#include "surfacegeom.hpp" #ifdef PARALLEL #include "paralleltop.hpp" diff --git a/libsrc/meshing/meshing2.cpp b/libsrc/meshing/meshing2.cpp index bda90593..edf4770d 100644 --- a/libsrc/meshing/meshing2.cpp +++ b/libsrc/meshing/meshing2.cpp @@ -675,7 +675,7 @@ namespace netgen plainpoints.Append (newpout); - const auto& pout3d = locpoints.Get(pouti); + auto pout3d = locpoints.Get(pouti); locpoints.Append (pout3d); plainzones.Append (0); diff --git a/libsrc/meshing/meshtype.cpp b/libsrc/meshing/meshtype.cpp index 9523fab7..9bebaec4 100644 --- a/libsrc/meshing/meshtype.cpp +++ b/libsrc/meshing/meshtype.cpp @@ -20,9 +20,9 @@ namespace netgen #ifdef PARALLEL MPI_Datatype MeshPoint :: MyGetMPIType ( ) { - static MPI_Datatype type = NULL; - static MPI_Datatype htype = NULL; - if (!type) + static MPI_Datatype type = MPI_DATATYPE_NULL; + static MPI_Datatype htype = MPI_DATATYPE_NULL; + if (type == MPI_DATATYPE_NULL) { MeshPoint hp; int blocklen[] = { 3, 1, 1 }; @@ -30,14 +30,14 @@ namespace netgen (char*)&hp.layer - (char*)&hp, (char*)&hp.singular - (char*)&hp }; MPI_Datatype types[] = { MPI_DOUBLE, MPI_INT, MPI_DOUBLE }; - *testout << "displ = " << displ[0] << ", " << displ[1] << ", " << displ[2] << endl; - *testout << "sizeof = " << sizeof (MeshPoint) << endl; + // *testout << "displ = " << displ[0] << ", " << displ[1] << ", " << displ[2] << endl; + // *testout << "sizeof = " << sizeof (MeshPoint) << endl; MPI_Type_create_struct (3, blocklen, displ, types, &htype); MPI_Type_commit ( &htype ); MPI_Aint lb, ext; MPI_Type_get_extent (htype, &lb, &ext); - *testout << "lb = " << lb << endl; - *testout << "ext = " << ext << endl; + // *testout << "lb = " << lb << endl; + // *testout << "ext = " << ext << endl; ext = sizeof (MeshPoint); MPI_Type_create_resized (htype, lb, ext, &type); MPI_Type_commit ( &type ); @@ -45,6 +45,102 @@ namespace netgen } return type; } + + + MPI_Datatype Element2d :: MyGetMPIType ( ) + { + static MPI_Datatype type = MPI_DATATYPE_NULL; + static MPI_Datatype htype = MPI_DATATYPE_NULL; + if (type == MPI_DATATYPE_NULL) + { + Element2d hel; + int blocklen[] = { ELEMENT2D_MAXPOINTS, 1, 1, 1 }; + MPI_Aint displ[] = + { (char*)&hel.pnum[0] - (char*)&hel, + (char*)&hel.index - (char*)&hel, + (char*)&hel.typ - (char*)&hel, + (char*)&hel.np - (char*)&hel + }; + MPI_Datatype types[] = { GetMPIType(), GetMPIType(hel.index), + GetMPIType(hel.typ), GetMPIType(hel.np) }; + // *testout << "displ = " << displ[0] << ", " << displ[1] << ", " << displ[2] << endl; + // *testout << "sizeof = " << sizeof (MeshPoint) << endl; + MPI_Type_create_struct (4, blocklen, displ, types, &htype); + MPI_Type_commit ( &htype ); + MPI_Aint lb, ext; + MPI_Type_get_extent (htype, &lb, &ext); + // *testout << "lb = " << lb << endl; + // *testout << "ext = " << ext << endl; + ext = sizeof (Element2d); + MPI_Type_create_resized (htype, lb, ext, &type); + MPI_Type_commit ( &type ); + } + return type; + } + + MPI_Datatype Element :: MyGetMPIType ( ) + { + static MPI_Datatype type = MPI_DATATYPE_NULL; + static MPI_Datatype htype = MPI_DATATYPE_NULL; + if (type == MPI_DATATYPE_NULL) + { + Element hel; + int blocklen[] = { ELEMENT_MAXPOINTS, 1, 1, 1 }; + MPI_Aint displ[] = + { (char*)&hel.pnum[0] - (char*)&hel, + (char*)&hel.index - (char*)&hel, + (char*)&hel.typ - (char*)&hel, + (char*)&hel.np - (char*)&hel + }; + MPI_Datatype types[] = { GetMPIType(), GetMPIType(hel.index), + GetMPIType(hel.typ), GetMPIType(hel.np) }; + // *testout << "displ = " << displ[0] << ", " << displ[1] << ", " << displ[2] << endl; + // *testout << "sizeof = " << sizeof (MeshPoint) << endl; + MPI_Type_create_struct (4, blocklen, displ, types, &htype); + MPI_Type_commit ( &htype ); + MPI_Aint lb, ext; + MPI_Type_get_extent (htype, &lb, &ext); + // *testout << "lb = " << lb << endl; + // *testout << "ext = " << ext << endl; + ext = sizeof (Element); + MPI_Type_create_resized (htype, lb, ext, &type); + MPI_Type_commit ( &type ); + } + return type; + } + + MPI_Datatype Segment :: MyGetMPIType ( ) + { + static MPI_Datatype type = MPI_DATATYPE_NULL; + static MPI_Datatype htype = MPI_DATATYPE_NULL; + if (type == MPI_DATATYPE_NULL) + { + Segment hel; + int blocklen[] = { 3, 1, 1, 1 }; + MPI_Aint displ[] = + { (char*)&hel.pnums[0] - (char*)&hel, + (char*)&hel.edgenr - (char*)&hel, + (char*)&hel.cd2i - (char*)&hel, + (char*)&hel.si - (char*)&hel + }; + MPI_Datatype types[] = { + GetMPIType(), GetMPIType(hel.edgenr), GetMPIType(hel.cd2i), GetMPIType(hel.si) + }; + // *testout << "displ = " << displ[0] << ", " << displ[1] << ", " << displ[2] << endl; + // *testout << "sizeof = " << sizeof (MeshPoint) << endl; + MPI_Type_create_struct (4, blocklen, displ, types, &htype); + MPI_Type_commit ( &htype ); + MPI_Aint lb, ext; + MPI_Type_get_extent (htype, &lb, &ext); + // *testout << "lb = " << lb << endl; + // *testout << "ext = " << ext << endl; + ext = sizeof (Segment); + MPI_Type_create_resized (htype, lb, ext, &type); + MPI_Type_commit ( &type ); + } + return type; + } + #endif @@ -80,8 +176,6 @@ namespace netgen epgeominfo[1].edgenr = 1; epgeominfo[1].dist = 0; */ - - bcname = nullptr; } Segment::Segment (const Segment & other) @@ -109,7 +203,6 @@ namespace netgen geominfo[1] = other.geominfo[1]; epgeominfo[0] = other.epgeominfo[0]; epgeominfo[1] = other.epgeominfo[1]; - bcname = other.bcname; } Segment& Segment::operator=(const Segment & other) @@ -135,7 +228,6 @@ namespace netgen pnums[2] = other.pnums[2]; meshdocval = other.meshdocval; hp_elnr = other.hp_elnr; - bcname = other.bcname; is_curved = other.is_curved; } @@ -144,11 +236,12 @@ namespace netgen void Segment :: DoArchive (Archive & ar) { + string * bcname_dummy = nullptr; ar & pnums[0] & pnums[1] & pnums[2] & edgenr & singedge_left & singedge_right & si & cd2i & domin & domout & tlosurf & surfnr1 & surfnr2 - & bcname + & bcname_dummy // keep this for backward compatiblity & epgeominfo[0].edgenr & epgeominfo[1].edgenr; } @@ -161,7 +254,8 @@ namespace netgen << " si = " << seg.si << ", edgenr = " << seg.edgenr; return s; } - /* + + // needed, e.g. for MPI communication Element2d :: Element2d () { for (int i = 0; i < ELEMENT2D_MAXPOINTS; i++) @@ -180,7 +274,7 @@ namespace netgen strongrefflag = false; is_curved = false; } - */ + Element2d :: Element2d (int anp) { for (int i = 0; i < ELEMENT2D_MAXPOINTS; i++) diff --git a/libsrc/meshing/meshtype.hpp b/libsrc/meshing/meshtype.hpp index e6ccc83e..b3b33cc4 100644 --- a/libsrc/meshing/meshtype.hpp +++ b/libsrc/meshing/meshtype.hpp @@ -177,6 +177,7 @@ namespace netgen PointIndex operator-- (int) { PointIndex hi(*this); i--; return hi; } PointIndex & operator++ () { i++; return *this; } PointIndex operator-- () { i--; return *this; } + PointIndex operator+= (int add) { i += add; return *this; } void Invalidate() { i = PointIndex::BASE-1; } bool IsValid() const { return i != PointIndex::BASE-1; } #ifdef BASE0 @@ -219,17 +220,29 @@ namespace netgen PointIndices (PointIndex i1, PointIndex i2) : INDEX_2(i1,i2) { ; } PointIndex operator[] (int i) const { return PointIndex(INDEX_2::operator[](i)); } PointIndex & operator[] (int i) { return reinterpret_cast(INDEX_2::operator[](i)); } - static PointIndices Sort(PointIndex i1, PointIndex i2) { return INDEX_2::Sort(i1, i2); } + static PointIndices Sort(PointIndex i1, PointIndex i2) { return INDEX_2::Sort(i1, i2); } + template + PointIndex get() const { return PointIndex(INDEX_2::operator[](J)); } }; - +} +namespace std +{ + // structured binding support + template + struct tuple_size> : std::integral_constant {}; + template struct tuple_element> { using type = netgen::PointIndex; }; +} + +namespace netgen +{ class ElementIndex { int i; public: - ElementIndex () { ; } - ElementIndex (int ai) : i(ai) { ; } + ElementIndex () = default; + constexpr ElementIndex (int ai) : i(ai) { ; } ElementIndex & operator= (const ElementIndex & ai) { i = ai.i; return *this; } ElementIndex & operator= (int ai) { i = ai; return *this; } operator int () const { return i; } @@ -288,18 +301,22 @@ namespace netgen { int i; public: - SegmentIndex () { ; } - SegmentIndex (int ai) : i(ai) { ; } + SegmentIndex () = default; + constexpr SegmentIndex (int ai) : i(ai) { ; } SegmentIndex & operator= (const SegmentIndex & ai) { i = ai.i; return *this; } SegmentIndex & operator= (int ai) { i = ai; return *this; } - operator int () const { return i; } + constexpr operator int () const { return i; } SegmentIndex& operator++ () { ++i; return *this; } SegmentIndex& operator-- () { --i; return *this; } SegmentIndex operator++ (int) { return i++; } SegmentIndex operator-- (int) { return i--; } }; + inline void SetInvalid (SegmentIndex & id) { id = -1; } + inline bool IsInvalid (SegmentIndex & id) { return id == -1; } + + inline istream & operator>> (istream & ist, SegmentIndex & pi) { int i; ist >> i; pi = i; return ist; @@ -389,11 +406,11 @@ namespace netgen PointGeomInfo geominfo[ELEMENT2D_MAXPOINTS]; /// surface nr - short int index; + int index; /// ELEMENT_TYPE typ; /// number of points - unsigned int np:4; + int8_t np; bool badel:1; bool refflag:1; // marked for refinement bool strongrefflag:1; @@ -417,7 +434,7 @@ namespace netgen public: /// - Element2d () = default; + Element2d (); Element2d (const Element2d &) = default; Element2d (Element2d &&) = default; Element2d & operator= (const Element2d &) = default; @@ -548,6 +565,11 @@ namespace netgen ar & pnum[i]; } +#ifdef PARALLEL + static MPI_Datatype MyGetMPIType(); +#endif + + void SetIndex (int si) { index = si; } /// int GetIndex () const { return index; } @@ -699,7 +721,7 @@ namespace netgen /// ELEMENT_TYPE typ; /// number of points (4..tet, 5..pyramid, 6..prism, 8..hex, 10..quad tet, 12..quad prism) - int np:6; + int8_t np; /// class flagstruct { public: @@ -716,7 +738,7 @@ namespace netgen }; /// sub-domain index - short int index; + int index; /// order for hp-FEM unsigned int orderx:6; unsigned int ordery:6; @@ -826,6 +848,10 @@ namespace netgen for (size_t i = 0; i < np; i++) ar & pnum[i]; } + +#ifdef PARALLEL + static MPI_Datatype MyGetMPIType(); +#endif /// void SetIndex (int si) { index = si; } @@ -1031,7 +1057,6 @@ namespace netgen // #endif private: - string* bcname; bool is_curved; public: @@ -1048,29 +1073,15 @@ namespace netgen int hp_elnr; - void SetBCName ( string * abcname ) - { - bcname = abcname; - } - - string * BCNamePtr () - { return bcname; } - - const string * BCNamePtr () const - { return bcname; } - - const string & GetBCName () const - { - static string defaultstring = "default"; - if (! bcname ) return defaultstring; - return *bcname; - } - int GetNP() const { return pnums[2].IsValid() ? 3 : 2; } + auto PNums() const { return FlatArray (GetNP(), &pnums[0]); } + auto PNums() { return FlatArray (GetNP(), &pnums[0]); } + + ELEMENT_TYPE GetType() const { return pnums[2].IsValid() ? SEGMENT3 : SEGMENT; @@ -1093,6 +1104,10 @@ namespace netgen */ void DoArchive (Archive & ar); +#ifdef PARALLEL + static MPI_Datatype MyGetMPIType(); +#endif + }; ostream & operator<<(ostream & s, const Segment & seg); @@ -1217,12 +1232,13 @@ namespace netgen // s .. swap faces // c .. combine elements // d .. divide elements + // D .. divide and join opposite edges, remove element // p .. plot, no pause // P .. plot, Pause // h .. Histogramm, no pause // H .. Histogramm, pause */ - string optimize3d = "cmdmustm"; + string optimize3d = "cmdDmustm"; /// number of 3d optimization steps int optsteps3d = 3; /** @@ -1251,8 +1267,10 @@ namespace netgen bool uselocalh = true; /// grading for local h double grading = 0.3; - /// use delaunay meshing + /// use delaunay for 3d meshing bool delaunay = true; + /// use delaunay for 2d meshing + bool delaunay2d = false; /// maximal mesh size double maxh = 1e10; /// minimal mesh size @@ -1565,6 +1583,33 @@ namespace netgen } +#ifdef PARALLEL +namespace ngcore +{ + template <> struct MPI_typetrait { + static MPI_Datatype MPIType () { return MPI_INT; } + }; + + template <> struct MPI_typetrait { + static MPI_Datatype MPIType () { return MPI_CHAR; } + }; + + template <> struct MPI_typetrait { + static MPI_Datatype MPIType () { return netgen::MeshPoint::MyGetMPIType(); } + }; + + template <> struct MPI_typetrait { + static MPI_Datatype MPIType () { return netgen::Element::MyGetMPIType(); } + }; + template <> struct MPI_typetrait { + static MPI_Datatype MPIType () { return netgen::Element2d::MyGetMPIType(); } + }; + template <> struct MPI_typetrait { + static MPI_Datatype MPIType () { return netgen::Segment::MyGetMPIType(); } + }; + +} +#endif #endif diff --git a/libsrc/meshing/parallelmesh.cpp b/libsrc/meshing/parallelmesh.cpp index 99a5f981..76c8dabb 100644 --- a/libsrc/meshing/parallelmesh.cpp +++ b/libsrc/meshing/parallelmesh.cpp @@ -25,13 +25,150 @@ namespace metis { using namespace metis; #endif -namespace netgen +/* +namespace ngcore { + template <> struct MPI_typetrait { + static MPI_Datatype MPIType () { return MPI_INT; } }; +} +*/ + +namespace ngcore { + /** An MPI-Package for a Surface element **/ + class SurfPointPackage + { + public: + int num; // point numebr + int trignum; // STL geo info + double u, v; // OCC geo info + SurfPointPackage () { ; } + SurfPointPackage & operator = (const SurfPointPackage & other) { + num = other.num; + trignum = other.trignum; + u = other.u; + v = other.v; + return *this; + } + }; // class SurfPointPackage + + template<> struct MPI_typetrait { + static MPI_Datatype MPIType () { + static MPI_Datatype MPI_T = 0; + if (!MPI_T) + { + int block_len[2] = { 2, 2 }; + MPI_Aint displs[3] = { 0, 2*sizeof(int) }; + MPI_Datatype types[2] = { MPI_INT, MPI_DOUBLE }; + MPI_Type_create_struct(2, block_len, displs, types, &MPI_T); + MPI_Type_commit(&MPI_T); + } + return MPI_T; + } + }; // struct MPI_typetrait + + + class SelPackage + { + public: + int sei; + int index; + int np; + /** we send too much here, especially in 2d! **/ + SurfPointPackage points[ELEMENT2D_MAXPOINTS]; + SelPackage () { ; } + SelPackage (const netgen::Mesh & mesh, netgen::SurfaceElementIndex _sei) + { + const netgen::Element2d & el = mesh[_sei]; + sei = _sei; + index = el.GetIndex(); + np = el.GetNP(); + for (int k : Range(1, np+1)) { + auto & pnt = points[k-1];; + pnt.num = el.PNum(k); + pnt.trignum = el.GeomInfoPi(k).trignum; + pnt.u = el.GeomInfoPi(k).u; + pnt.v = el.GeomInfoPi(k).v; + } + /** otherwise, we use uninitialized values **/ + for (int k : Range(np, ELEMENT2D_MAXPOINTS)) { + points[k].num = -1; + points[k].trignum = -1; + points[k].u = -1; + points[k].v = -1; + } + } + void Unpack (netgen::Element2d & el) const { + el.SetIndex(index); + for (int k : Range(1, np + 1)) { + auto & pnt = points[k-1]; + el.PNum(k) = pnt.num; + el.GeomInfoPi(k).trignum = pnt.trignum; + el.GeomInfoPi(k).u = pnt.u; + el.GeomInfoPi(k).v = pnt.v; + } + } + SelPackage & operator = (const SelPackage & other) { + sei = other.sei; + index = other.index; + np = other.np; + for (int k : Range(ELEMENT2D_MAXPOINTS)) + { points[k] = other.points[k]; } + return *this; + } + }; // class SelPackage + + template<> struct MPI_typetrait { + static MPI_Datatype MPIType () { + static MPI_Datatype MPI_T = 0; + if (!MPI_T) + { + int block_len[2] = { 3, ELEMENT2D_MAXPOINTS }; + MPI_Aint displs[3] = { 0, 3*sizeof(int) }; + MPI_Datatype types[2] = { MPI_INT, GetMPIType() }; + MPI_Type_create_struct(2, block_len, displs, types, &MPI_T); + MPI_Type_commit(&MPI_T); + } + return MPI_T; + } + }; // MPI_typetrait + + + class PointElPackage + { + public: + netgen::PointIndex pnum; + int index; + PointElPackage () { pnum = -1; index = -1; } + PointElPackage (const netgen::Element0d & el) + { pnum = el.pnum; index = el.index; } + }; // class PointElPackage + + template<> struct MPI_typetrait { + static MPI_Datatype MPIType () { + static MPI_Datatype MPI_T = 0; + if (!MPI_T) + { + int block_len[2] = { 1, 1 }; + MPI_Aint displs[3] = { 0, sizeof(netgen::PointIndex) }; + MPI_Datatype types[2] = { GetMPIType(), MPI_INT }; + MPI_Type_create_struct(2, block_len, displs, types, &MPI_T); + MPI_Type_commit(&MPI_T); + } + return MPI_T; + } + }; // MPI_typetrait + + +} // namespace ngcore + +namespace netgen +{ + /* template <> inline MPI_Datatype MyGetMPIType ( ) { return MPI_INT; } - + */ void Mesh :: SendRecvMesh () { @@ -49,6 +186,7 @@ namespace netgen if (id == 0) { paralleltop -> SetNV (GetNV()); + paralleltop -> SetNV_Loc2Glob (GetNV()); paralleltop -> SetNE (GetNE()); paralleltop -> SetNSegm (GetNSeg()); paralleltop -> SetNSE (GetNSE()); @@ -70,15 +208,16 @@ namespace netgen void Mesh :: SendMesh () const { - NgArray sendrequests; NgMPI_Comm comm = GetCommunicator(); int id = comm.Rank(); int ntasks = comm.Size(); int dim = GetDimension(); - MyMPI_Bcast(dim, comm); + comm.Bcast(dim); + Array sendrequests(8*(ntasks-1)); + sendrequests.SetSize0(); // If the topology is not already updated, we do not need to // build edges/faces. @@ -91,32 +230,36 @@ namespace netgen PrintMessage ( 3, "Sending nr of elements"); - NgArray num_els_on_proc(ntasks); + Array num_els_on_proc(ntasks); num_els_on_proc = 0; for (ElementIndex ei = 0; ei < GetNE(); ei++) - // num_els_on_proc[(*this)[ei].GetPartition()]++; num_els_on_proc[vol_partition[ei]]++; - MPI_Scatter (&num_els_on_proc[0], 1, MPI_INT, - MPI_IN_PLACE, -1, MPI_INT, 0, comm); + comm.ScatterRoot (num_els_on_proc); - TABLE els_of_proc (num_els_on_proc); + Table els_of_proc (num_els_on_proc); + num_els_on_proc = 0; for (ElementIndex ei = 0; ei < GetNE(); ei++) - // els_of_proc.Add ( (*this)[ei].GetPartition(), ei); - els_of_proc.Add (vol_partition[ei], ei); - + { + auto nr = vol_partition[ei]; + els_of_proc[nr][num_els_on_proc[nr]++] = ei; + } + PrintMessage ( 3, "Building vertex/proc mapping"); - NgArray num_sels_on_proc(ntasks); + Array num_sels_on_proc(ntasks); num_sels_on_proc = 0; for (SurfaceElementIndex ei = 0; ei < GetNSE(); ei++) - // num_sels_on_proc[(*this)[ei].GetPartition()]++; num_sels_on_proc[surf_partition[ei]]++; - TABLE sels_of_proc (num_sels_on_proc); + Table sels_of_proc (num_sels_on_proc); + num_sels_on_proc = 0; for (SurfaceElementIndex ei = 0; ei < GetNSE(); ei++) - // sels_of_proc.Add ( (*this)[ei].GetPartition(), ei); - sels_of_proc.Add (surf_partition[ei], ei); + { + auto nr = surf_partition[ei]; + sels_of_proc[nr][num_sels_on_proc[nr]++] = ei; + } + NgArray num_segs_on_proc(ntasks); num_segs_on_proc = 0; @@ -223,20 +366,31 @@ namespace netgen vert_flag = -1; for (int dest = 1; dest < ntasks; dest++) { - NgFlatArray els = els_of_proc[dest]; + /* + FlatArray els = els_of_proc[dest]; for (int hi = 0; hi < els.Size(); hi++) { const Element & el = (*this) [ els[hi] ]; for (int i = 0; i < el.GetNP(); i++) f(el[i], dest); } - NgFlatArray sels = sels_of_proc[dest]; + */ + for (auto & ei : els_of_proc[dest]) + for (auto pnum : (*this)[ei].PNums()) + f(pnum, dest); + /* + FlatArray sels = sels_of_proc[dest]; for (int hi = 0; hi < sels.Size(); hi++) { const Element2d & el = (*this) [ sels[hi] ]; for (int i = 0; i < el.GetNP(); i++) f(el[i], dest); } + */ + for (auto & ei : sels_of_proc[dest]) + for (auto pnum : (*this)[ei].PNums()) + f(pnum, dest); + NgFlatArray segs = segs_of_proc[dest]; for (int hi = 0; hi < segs.Size(); hi++) { @@ -254,7 +408,8 @@ namespace netgen vert_flag[vertex] = dest; num_verts_on_proc[dest]++; num_procs_on_vert[vertex]++; - GetParallelTopology().SetDistantPNum (dest, vertex); + // GetParallelTopology().SetDistantPNum (dest, vertex); + GetParallelTopology().AddDistantProc (PointIndex(vertex), dest); } }; countit(vertex, dest); @@ -300,7 +455,8 @@ namespace netgen for (int dest = 1; dest < ntasks; dest++) { NgFlatArray verts = verts_of_proc[dest]; - sendrequests.Append (MyMPI_ISend (verts, dest, MPI_TAG_MESH+1, comm)); + // sendrequests.Append (MyMPI_ISend (verts, dest, MPI_TAG_MESH+1, comm)); + sendrequests.Append (comm.ISend (FlatArray(verts), dest, MPI_TAG_MESH+1)); MPI_Datatype mptype = MeshPoint::MyGetMPIType(); @@ -319,9 +475,6 @@ namespace netgen sendrequests.Append (request); } - NgArray num_distpnums(ntasks); - num_distpnums = 0; - /** Next, we send the identifications themselfs. @@ -379,21 +532,25 @@ namespace netgen } } } - NgArray req_per; + Array req_per; for(int dest = 1; dest < ntasks; dest++) - req_per.Append(MyMPI_ISend(pp_data[dest], dest, MPI_TAG_MESH+1, comm)); - MPI_Waitall(req_per.Size(), &req_per[0], MPI_STATUS_IGNORE); + // req_per.Append(MyMPI_ISend(pp_data[dest], dest, MPI_TAG_MESH+1, comm)); + req_per.Append(comm.ISend(FlatArray(pp_data[dest]), dest, MPI_TAG_MESH+1)); + MyMPI_WaitAll(req_per); PrintMessage ( 3, "Sending Vertices - distprocs"); + + Array num_distpnums(ntasks); + num_distpnums = 0; for (int vert = 1; vert <= GetNP(); vert++) { - NgFlatArray procs = procs_of_vert[vert]; - for (int j = 0; j < procs.Size(); j++) - num_distpnums[procs[j]] += 3 * (procs.Size()-1); + FlatArray procs = procs_of_vert[vert]; + for (auto p : procs) + num_distpnums[p] += 3 * (procs.Size()-1); } - TABLE distpnums (num_distpnums); + DynamicTable distpnums (num_distpnums); for (int vert = 1; vert <= GetNP(); vert++) { @@ -409,13 +566,13 @@ namespace netgen } for ( int dest = 1; dest < ntasks; dest ++ ) - sendrequests.Append (MyMPI_ISend (distpnums[dest], dest, MPI_TAG_MESH+1, comm)); + sendrequests.Append (comm.ISend (distpnums[dest], dest, MPI_TAG_MESH+1)); PrintMessage ( 3, "Sending elements" ); - NgArray elarraysize (ntasks); + Array elarraysize (ntasks); elarraysize = 0; for ( int ei = 1; ei <= GetNE(); ei++) { @@ -425,7 +582,7 @@ namespace netgen elarraysize[dest] += 3 + el.GetNP(); } - TABLE elementarrays(elarraysize); + DynamicTable elementarrays(elarraysize); for (int ei = 1; ei <= GetNE(); ei++) { @@ -441,12 +598,13 @@ namespace netgen } for (int dest = 1; dest < ntasks; dest ++ ) - sendrequests.Append (MyMPI_ISend (elementarrays[dest], dest, MPI_TAG_MESH+2, comm)); + // sendrequests.Append (MyMPI_ISend (elementarrays[dest], dest, MPI_TAG_MESH+2, comm)); + sendrequests.Append (comm.ISend (elementarrays[dest], dest, MPI_TAG_MESH+2)); PrintMessage ( 3, "Sending Face Descriptors" ); - NgArray fddata (6 * GetNFD()); + Array fddata (6 * GetNFD()); for (int fdi = 1; fdi <= GetNFD(); fdi++) { fddata[6*fdi-6] = GetFaceDescriptor(fdi).SurfNr(); @@ -458,8 +616,8 @@ namespace netgen } for (int dest = 1; dest < ntasks; dest++) - sendrequests.Append (MyMPI_ISend (fddata, dest, MPI_TAG_MESH+3, comm)); - + sendrequests.Append (comm.ISend (fddata, dest, MPI_TAG_MESH+3)); + /** Surface Elements **/ PrintMessage ( 3, "Sending Surface elements" ); @@ -521,30 +679,20 @@ namespace netgen } } }; - NgArray nlocsel(ntasks), bufsize(ntasks); + Array nlocsel(ntasks), bufsize(ntasks); nlocsel = 0; - bufsize = 1; + bufsize = 0; iterate_sels([&](SurfaceElementIndex sei, const Element2d & sel, int dest){ nlocsel[dest]++; - bufsize[dest] += 4 + 2*sel.GetNP(); + bufsize[dest]++; }); - TABLE selbuf(bufsize); - for (int dest = 1; dest < ntasks; dest++ ) - selbuf.Add (dest, nlocsel[dest]); + DynamicTable selbuf(bufsize); iterate_sels([&](SurfaceElementIndex sei, const auto & sel, int dest) { - selbuf.Add (dest, sei); - selbuf.Add (dest, sel.GetIndex()); - // selbuf.Add (dest, 0); - selbuf.Add (dest, sel.GetNP()); - for ( int ii = 1; ii <= sel.GetNP(); ii++) - { - selbuf.Add (dest, sel.PNum(ii)); - selbuf.Add (dest, sel.GeomInfoPi(ii).trignum); - } + selbuf.Add (dest, SelPackage(*this, sei)); }); // distribute sel data for (int dest = 1; dest < ntasks; dest++) - sendrequests.Append (MyMPI_ISend(selbuf[dest], dest, MPI_TAG_MESH+4, comm)); + sendrequests.Append (comm.ISend(selbuf[dest], dest, MPI_TAG_MESH+4)); /** Segments **/ @@ -676,7 +824,7 @@ namespace netgen nloc_seg[dest]++; bufsize[dest] += 14; }); - TABLE segm_buf(bufsize); + DynamicTable segm_buf(bufsize); iterate_segs2([&](auto segi, const auto & seg, int dest) { segm_buf.Add (dest, segi); @@ -696,29 +844,54 @@ namespace netgen }); // distrubute segment data for (int dest = 1; dest < ntasks; dest++) - sendrequests.Append (MyMPI_ISend(segm_buf[dest], dest, MPI_TAG_MESH+5, comm)); + sendrequests.Append (comm.ISend(segm_buf[dest], dest, MPI_TAG_MESH+5)); + + /** Point-Elements **/ + PrintMessage ( 3, "Point-Elements ..."); + + auto iterate_zdes = [&](auto f) { + for (auto k : Range(pointelements)) { + auto & el = pointelements[k]; + PointElPackage pack(el); + auto dests = procs_of_vert[el.pnum]; + for (auto dest : dests) + { f(pack, dest); } + } + }; + + bufsize = 0; + iterate_zdes([&](const auto & pack, auto dest) { bufsize[dest]++; }); + DynamicTable zde_buf(bufsize); // zero dim elements + iterate_zdes([&](const auto & pack, auto dest) { zde_buf.Add(dest, pack); }); + + for (int dest = 1; dest < ntasks; dest++) + { sendrequests.Append (comm.ISend(zde_buf[dest], dest, MPI_TAG_MESH+6)); } PrintMessage ( 3, "now wait ..."); - MPI_Waitall (sendrequests.Size(), &sendrequests[0], MPI_STATUS_IGNORE); + MyMPI_WaitAll (sendrequests); // clean up MPI-datatypes we allocated earlier for (auto t : point_types) { MPI_Type_free(&t); } + paralleltop -> SetNV_Loc2Glob (0); + paralleltop -> SetNV (0); + paralleltop -> EnumeratePointsGlobally(); PrintMessage ( 3, "Sending names"); sendrequests.SetSize(3*ntasks); /** Send bc/mat/cd*-names **/ // nr of names - int nnames[4] = {0,0,0,0}; + ArrayMem nnames{0,0,0,0}; nnames[0] = materials.Size(); nnames[1] = bcnames.Size(); nnames[2] = GetNCD2Names(); nnames[3] = GetNCD3Names(); int tot_nn = nnames[0] + nnames[1] + nnames[2] + nnames[3]; for( int k = 1; k < ntasks; k++) - (void) MPI_Isend(nnames, 4, MPI_INT, k, MPI_TAG_MESH+6, comm, &sendrequests[k]); + sendrequests[k] = comm.ISend(nnames, k, MPI_TAG_MESH+7); + // (void) MPI_Isend(nnames, 4, MPI_INT, k, MPI_TAG_MESH+6, comm, &sendrequests[k]); auto iterate_names = [&](auto func) { for (int k = 0; k < nnames[0]; k++) func(materials[k]); for (int k = 0; k < nnames[1]; k++) func(bcnames[k]); @@ -730,7 +903,7 @@ namespace netgen tot_nn = 0; iterate_names([&](auto ptr) { name_sizes[tot_nn++] = (ptr==NULL) ? 0 : ptr->size(); }); for( int k = 1; k < ntasks; k++) - (void) MPI_Isend(&name_sizes[0], tot_nn, MPI_INT, k, MPI_TAG_MESH+6, comm, &sendrequests[ntasks+k]); + (void) MPI_Isend(&name_sizes[0], tot_nn, MPI_INT, k, MPI_TAG_MESH+7, comm, &sendrequests[ntasks+k]); // names int strs = 0; iterate_names([&](auto ptr) { strs += (ptr==NULL) ? 0 : ptr->size(); }); @@ -742,13 +915,13 @@ namespace netgen for (int j=0; j < name.size(); j++) compiled_names[strs++] = name[j]; }); for( int k = 1; k < ntasks; k++) - (void) MPI_Isend(&(compiled_names[0]), strs, MPI_CHAR, k, MPI_TAG_MESH+6, comm, &sendrequests[2*ntasks+k]); + (void) MPI_Isend(&(compiled_names[0]), strs, MPI_CHAR, k, MPI_TAG_MESH+7, comm, &sendrequests[2*ntasks+k]); PrintMessage ( 3, "wait for names"); - MPI_Waitall (sendrequests.Size(), &sendrequests[0], MPI_STATUS_IGNORE); + MyMPI_WaitAll (sendrequests); - MPI_Barrier(comm); + comm.Barrier(); PrintMessage( 3, "Clean up local memory"); @@ -757,6 +930,7 @@ namespace netgen self.surfelements = Array(0); self.volelements = Array(0); self.segments = Array(0); + self.pointelements = Array(0); self.lockedpoints = Array(0); auto cleanup_ptr = [](auto & ptr) { if (ptr != nullptr) { @@ -787,6 +961,9 @@ namespace netgen self.BuildElementSearchTree(); // const_cast(*this).DeleteMesh(); + + // paralleltop -> SetNV (0); + // paralleltop->EnumeratePointsGlobally(); PrintMessage( 3, "send mesh complete"); } @@ -798,7 +975,7 @@ namespace netgen - // slaves receive the mesh from the master + // workers receive the mesh from the master void Mesh :: ReceiveParallelMesh ( ) { int timer = NgProfiler::CreateTimer ("ReceiveParallelMesh"); @@ -812,25 +989,23 @@ namespace netgen int ntasks = comm.Size(); int dim; - MyMPI_Bcast(dim, comm); + comm.Bcast(dim); SetDimension(dim); // Receive number of local elements int nelloc; - MPI_Scatter (NULL, 0, MPI_INT, - &nelloc, 1, MPI_INT, 0, comm); + comm.Scatter (nelloc); paralleltop -> SetNE (nelloc); - // string st; - // receive vertices NgProfiler::StartTimer (timer_pts); - NgArray verts; - MyMPI_Recv (verts, 0, MPI_TAG_MESH+1, comm); + Array verts; + comm.Recv (verts, 0, MPI_TAG_MESH+1); int numvert = verts.Size(); paralleltop -> SetNV (numvert); + paralleltop -> SetNV_Loc2Glob (numvert); // INDEX_CLOSED_HASHTABLE glob2loc_vert_ht (3*numvert+1); INDEX_HASHTABLE glob2loc_vert_ht (3*numvert+1); @@ -838,7 +1013,8 @@ namespace netgen for (int vert = 0; vert < numvert; vert++) { int globvert = verts[vert] + IndexBASE(); - paralleltop->SetLoc2Glob_Vert ( vert+1, globvert ); + // paralleltop->SetLoc2Glob_Vert ( vert+1, globvert ); + paralleltop->L2G (PointIndex(vert+PointIndex::BASE)) = globvert; glob2loc_vert_ht.Set (globvert, vert+1); } @@ -849,16 +1025,13 @@ namespace netgen MPI_Status status; MPI_Recv( points.Data(), numvert, mptype, 0, MPI_TAG_MESH+1, comm, &status); - NgArray pp_data; - MyMPI_Recv(pp_data, 0, MPI_TAG_MESH+1, comm); + Array pp_data; + comm.Recv(pp_data, 0, MPI_TAG_MESH+1); int maxidentnr = pp_data[0]; auto & idents = GetIdentifications(); for (int idnr = 1; idnr < maxidentnr+1; idnr++) - { - - idents.SetType(idnr, (Identifications::ID_TYPE)pp_data[idnr]); - } + idents.SetType(idnr, (Identifications::ID_TYPE)pp_data[idnr]); int offset = 2*maxidentnr+1; for(int idnr = 1; idnr < maxidentnr+1; idnr++) @@ -873,19 +1046,21 @@ namespace netgen } } - NgArray dist_pnums; - MyMPI_Recv (dist_pnums, 0, MPI_TAG_MESH+1, comm); + Array dist_pnums; + comm.Recv (dist_pnums, 0, MPI_TAG_MESH+1); for (int hi = 0; hi < dist_pnums.Size(); hi += 3) paralleltop -> - SetDistantPNum (dist_pnums[hi+1], dist_pnums[hi]); // , dist_pnums[hi+2]); + // SetDistantPNum (dist_pnums[hi+1], dist_pnums[hi]); // , dist_pnums[hi+2]); + AddDistantProc (PointIndex(dist_pnums[hi]), dist_pnums[hi+1]); NgProfiler::StopTimer (timer_pts); *testout << "got " << numvert << " vertices" << endl; + { - NgArray elarray; - MyMPI_Recv (elarray, 0, MPI_TAG_MESH+2, comm); + Array elarray; + comm.Recv (elarray, 0, MPI_TAG_MESH+2); NgProfiler::RegionTimer reg(timer_els); @@ -905,8 +1080,8 @@ namespace netgen } { - NgArray fddata; - MyMPI_Recv (fddata, 0, MPI_TAG_MESH+3, comm); + Array fddata; + comm.Recv (fddata, 0, MPI_TAG_MESH+3); for (int i = 0; i < fddata.Size(); i += 6) { int faceind = AddFaceDescriptor @@ -919,33 +1094,25 @@ namespace netgen { NgProfiler::RegionTimer reg(timer_sels); - NgArray selbuf; + Array selbuf; - MyMPI_Recv ( selbuf, 0, MPI_TAG_MESH+4, comm); + comm.Recv ( selbuf, 0, MPI_TAG_MESH+4); - int ii = 0; - int sel = 0; - - int nlocsel = selbuf[ii++]; + int nlocsel = selbuf.Size(); paralleltop -> SetNSE ( nlocsel ); - while (ii < selbuf.Size()-1) - { - int globsel = selbuf[ii++]; - int faceind = selbuf[ii++]; - //bool isghost = selbuf[ii++]; - int nep = selbuf[ii++]; - Element2d tri(nep); - tri.SetIndex(faceind); - for(int j = 1; j <= nep; j++) - { - tri.PNum(j) = glob2loc_vert_ht.Get (selbuf[ii++]); - tri.GeomInfoPi(j).trignum = selbuf[ii++]; - } - paralleltop->SetLoc2Glob_SurfEl ( sel+1, globsel ); - AddSurfaceElement (tri); - sel ++; - } + int sel = 0; + for (auto k : Range(selbuf)) { + auto & pack = selbuf[k]; + Element2d el(pack.np); + pack.Unpack(el); + /** map global point numbers to local ones **/ + for (int k : Range(1, 1+el.GetNP())) + { el.PNum(k) = glob2loc_vert_ht.Get(el.PNum(k)); } + paralleltop->SetLoc2Glob_SurfEl (sel+1, pack.sei); + AddSurfaceElement (el); + sel++; + } } @@ -991,13 +1158,27 @@ namespace netgen AddSegment (seg); segi++; } - } } + { /** 0d-Elements **/ + Array zdes; + comm.Recv ( zdes, 0, MPI_TAG_MESH+6); + pointelements.SetSize(zdes.Size()); + for (auto k : Range(pointelements)) { + auto & el = pointelements[k]; + el.pnum = glob2loc_vert_ht.Get(zdes[k].pnum); + el.index = zdes[k].index; + } + } + + // paralleltop -> SetNV_Loc2Glob (0); + paralleltop -> EnumeratePointsGlobally(); /** Recv bc-names **/ - int nnames[4] = {0,0,0,0}; - MPI_Recv(nnames, 4, MPI_INT, 0, MPI_TAG_MESH+6, comm, MPI_STATUS_IGNORE); + ArrayMem nnames{0,0,0,0}; + // MPI_Recv(nnames, 4, MPI_INT, 0, MPI_TAG_MESH+6, comm, MPI_STATUS_IGNORE); + comm.Recv(nnames, 0, MPI_TAG_MESH+7); + // cout << "nnames = " << FlatArray(nnames) << endl; materials.SetSize(nnames[0]); bcnames.SetSize(nnames[1]); cd2names.SetSize(nnames[2]); @@ -1005,12 +1186,12 @@ namespace netgen int tot_nn = nnames[0] + nnames[1] + nnames[2] + nnames[3]; NgArray name_sizes(tot_nn); - MPI_Recv(&name_sizes[0], tot_nn, MPI_INT, 0, MPI_TAG_MESH+6, comm, MPI_STATUS_IGNORE); + MPI_Recv(&name_sizes[0], tot_nn, MPI_INT, 0, MPI_TAG_MESH+7, comm, MPI_STATUS_IGNORE); int tot_size = 0; for (int k = 0; k < tot_nn; k++) tot_size += name_sizes[k]; NgArray compiled_names(tot_size); - MPI_Recv(&(compiled_names[0]), tot_size, MPI_CHAR, 0, MPI_TAG_MESH+6, comm, MPI_STATUS_IGNORE); + MPI_Recv(&(compiled_names[0]), tot_size, MPI_CHAR, 0, MPI_TAG_MESH+7, comm, MPI_STATUS_IGNORE); tot_nn = tot_size = 0; auto write_names = [&] (auto & array) { @@ -1026,7 +1207,7 @@ namespace netgen write_names(cd2names); write_names(cd3names); - MPI_Barrier(comm); + comm.Barrier(); int timerloc = NgProfiler::CreateTimer ("Update local mesh"); int timerloc2 = NgProfiler::CreateTimer ("CalcSurfacesOfNode"); @@ -1050,7 +1231,7 @@ namespace netgen clusters -> Update(); // paralleltop -> UpdateCoarseGrid(); - + // paralleltop->EnumeratePointsGlobally(); SetNextMajorTimeStamp(); } @@ -1058,7 +1239,7 @@ namespace netgen - // distribute the mesh to the slave processors + // distribute the mesh to the worker processors // call it only for the master ! void Mesh :: Distribute () { @@ -1358,7 +1539,7 @@ namespace netgen - // distribute the mesh to the slave processors + // distribute the mesh to the worker processors // call it only for the master ! void Mesh :: Distribute (NgArray & volume_weights , NgArray & surface_weights, NgArray & segment_weights) { diff --git a/libsrc/meshing/paralleltop.cpp b/libsrc/meshing/paralleltop.cpp index c410f425..08698e5d 100644 --- a/libsrc/meshing/paralleltop.cpp +++ b/libsrc/meshing/paralleltop.cpp @@ -25,14 +25,10 @@ namespace netgen { *testout << "ParallelMeshTopology::Reset" << endl; - NgMPI_Comm comm = mesh.GetCommunicator(); - int id = comm.Rank(); - int ntasks = comm.Size(); - - if ( ntasks == 1 ) return; + if ( mesh.GetCommunicator().Size() == 1 ) return; - int ned = mesh.GetTopology().GetNEdges(); - int nfa = mesh.GetTopology().GetNFaces(); + size_t ned = mesh.GetTopology().GetNEdges(); + size_t nfa = mesh.GetTopology().GetNFaces(); if (glob_edge.Size() != ned) { @@ -59,6 +55,174 @@ namespace netgen } + void ParallelMeshTopology :: EnumeratePointsGlobally () + { + auto comm = mesh.GetCommunicator(); + auto rank = comm.Rank(); + + size_t oldnv = glob_vert.Size(); + size_t nv = loc2distvert.Size(); + *testout << "enumerate globally, loc2distvert.size = " << loc2distvert.Size() + << ", glob_vert.size = " << glob_vert.Size() << endl; + + + if (rank == 0) + nv = 0; + + // IntRange newvr(oldnv, nv); // new vertex range + auto new_pir = Range(PointIndex(oldnv+PointIndex::BASE), + PointIndex(nv+PointIndex::BASE)); + + glob_vert.SetSize (nv); + for (auto pi : new_pir) + L2G(pi) = -1; + + int num_master_points = 0; + + for (auto pi : new_pir) + { + auto dps = GetDistantProcs(pi); + // check sorted: + for (int j = 0; j+1 < dps.Size(); j++) + if (dps[j+1] < dps[j]) cout << "wrong sort" << endl; + + if (dps.Size() == 0 || dps[0] > comm.Rank()) + L2G(pi) = num_master_points++; + } + + *testout << "nummaster = " << num_master_points << endl; + + Array first_master_point(comm.Size()); + comm.AllGather (num_master_points, first_master_point); + auto max_oldv = comm.AllReduce (Max (glob_vert.Range(0, oldnv)), MPI_MAX); + if (comm.AllReduce (oldnv, MPI_SUM) == 0) + max_oldv = PointIndex::BASE-1; + + size_t num_glob_points = max_oldv+1; + for (int i = 0; i < comm.Size(); i++) + { + int cur = first_master_point[i]; + first_master_point[i] = num_glob_points; + num_glob_points += cur; + } + + for (auto pi : new_pir) + if (L2G(pi) != -1) + L2G(pi) += first_master_point[comm.Rank()]; + + // ScatterDofData (global_nums); + + Array nsend(comm.Size()), nrecv(comm.Size()); + nsend = 0; + nrecv = 0; + + /** Count send/recv size **/ + for (auto pi : new_pir) + if (auto dps = GetDistantProcs(pi); dps.Size()) + { + if (rank < dps[0]) + for (auto p : dps) + nsend[p]++; + else + nrecv[dps[0]]++; + } + + Table send_data(nsend); + Table recv_data(nrecv); + + /** Fill send_data **/ + nsend = 0; + for (auto pi : new_pir) + if (auto dps = GetDistantProcs(pi); dps.Size()) + if (rank < dps[0]) + for (auto p : dps) + send_data[p][nsend[p]++] = L2G(pi); + + Array requests; + for (int i = 0; i < comm.Size(); i++) + { + if (nsend[i]) + requests.Append (comm.ISend (send_data[i], i, 200)); + if (nrecv[i]) + requests.Append (comm.IRecv (recv_data[i], i, 200)); + } + + MyMPI_WaitAll (requests); + + Array cnt(comm.Size()); + cnt = 0; + + for (auto pi : new_pir) + if (auto dps = GetDistantProcs(pi); dps.Size()) + if (int master = dps[0]; master < comm.Rank()) + L2G(pi) = recv_data[master][cnt[master]++]; + + // reorder following global ordering: + Array index0(glob_vert.Size()); + for (int pi : Range(index0)) + index0[pi] = pi; + QuickSortI (glob_vert, index0); + + if (rank != 0) + { + Array inv_index(index0.Size()); + for (int i = 0; i < index0.Size(); i++) + inv_index[index0[i]+PointIndex::BASE] = i+PointIndex::BASE; + + for (auto & el : mesh.VolumeElements()) + for (PointIndex & pi : el.PNums()) + pi = inv_index[pi]; + for (auto & el : mesh.SurfaceElements()) + for (PointIndex & pi : el.PNums()) + pi = inv_index[pi]; + for (auto & el : mesh.LineSegments()) + for (PointIndex & pi : el.PNums()) + pi = inv_index[pi]; + + // auto hpoints (mesh.Points()); + Array hpoints { mesh.Points() }; + for (PointIndex pi : Range(mesh.Points())) + mesh.Points()[inv_index[pi]] = hpoints[pi]; + + if (mesh.mlbetweennodes.Size() == mesh.Points().Size()) + { + NgArray,PointIndex::BASE> hml { mesh.mlbetweennodes }; + for (PointIndex pi : Range(mesh.Points())) + mesh.mlbetweennodes[inv_index[pi]] = hml[pi]; + } + + // *testout << "index0 = " << endl << index0 << endl; + // *testout << "loc2distvertold = " << endl; + // for (auto i : Range(index0)) + // *testout << "l " << i << " globi "<< glob_vert[i] << " dist = " << loc2distvert[i] << endl; + + DynamicTable oldtable = std::move(loc2distvert); + loc2distvert = DynamicTable (oldtable.Size()); + for (size_t i = 0; i < oldtable.Size(); i++) + for (auto val : oldtable[index0[i]]) + loc2distvert.Add (i, val); + + Array hglob_vert(glob_vert); + for (int i = 0; i < index0.Size(); i++) + glob_vert[i] = hglob_vert[index0[i]]; + + // *testout << "loc2distvertnew = " << endl; + // for (auto i : Range(index0)) + // *testout << "l " << i << " globi "<< glob_vert[i] << " dist = " << loc2distvert[i] << endl; + } + + /* + for (size_t i = 0; i+1 < glob_vert.Size(); i++) + if (glob_vert[i] > glob_vert[i+1]) + cout << "wrong ordering of globvert" << endl; + */ + if (glob_vert.Size() > 1) + for (auto i : Range(glob_vert).Modify(0,-1)) + if (glob_vert[i] > glob_vert[i+1]) + cout << "wrong ordering of globvert" << endl; + } + + /* void ParallelMeshTopology :: SetDistantFaceNum (int dest, int locnum) { for ( int i = 0; i < loc2distface[locnum-1].Size(); i+=1 ) @@ -83,12 +247,28 @@ namespace netgen return; loc2distedge.Add (locnum-1, dest); } - - void ParallelMeshTopology :: SetNV (int anv) + */ + + void ParallelMeshTopology :: SetNV_Loc2Glob (int anv) { glob_vert.SetSize(anv); glob_vert = -1; - loc2distvert.ChangeSize (anv); + } + + void ParallelMeshTopology :: SetNV (int anv) + { + // glob_vert.SetSize(anv); + // glob_vert = -1; + // loc2distvert.ChangeSize (anv); + + DynamicTable oldtable(loc2distvert.Size()); + for (size_t i = 0; i < loc2distvert.Size(); i++) + for (auto val : loc2distvert[i]) + oldtable.Add (i, val); + loc2distvert = DynamicTable (anv); + for (size_t i = 0; i < min(size_t(anv), oldtable.Size()); i++) + for (auto val : oldtable[i]) + loc2distvert.Add (i, val); } void ParallelMeshTopology :: SetNE ( int ane ) @@ -126,7 +306,7 @@ namespace netgen *testout << "ParallelMeshTopology :: UpdateCoarseGridGlobal" << endl; const MeshTopology & topology = mesh.GetTopology(); - MPI_Comm comm = mesh.GetCommunicator(); + auto comm = mesh.GetCommunicator(); if ( id == 0 ) { @@ -162,10 +342,11 @@ namespace netgen sendarray.Append (topology.GetSurfaceElementFace (el)); } - NgArray sendrequests; + Array sendrequests; for (int dest = 1; dest < ntasks; dest++) - sendrequests.Append (MyMPI_ISend (*sendarrays[dest], dest, MPI_TAG_MESH+10, comm)); - MPI_Waitall (sendrequests.Size(), &sendrequests[0], MPI_STATUS_IGNORE); + // sendrequests.Append (MyMPI_ISend (*sendarrays[dest], dest, MPI_TAG_MESH+10, comm)); + sendrequests.Append (comm.ISend (FlatArray(*sendarrays[dest]), dest, MPI_TAG_MESH+10)); + MyMPI_WaitAll (sendrequests); for (int dest = 1; dest < ntasks; dest++) delete sendarrays[dest]; @@ -205,11 +386,179 @@ namespace netgen is_updated = true; } + + void ParallelMeshTopology :: IdentifyVerticesAfterRefinement() + { + static Timer t("ParallelTopology::UpdateCoarseGrid"); RegionTimer r(t); + NgMPI_Comm comm = mesh.GetCommunicator(); + int id = comm.Rank(); + int ntasks = comm.Size(); + + if (ntasks == 1) return; + + Reset(); + static int timer = NgProfiler::CreateTimer ("UpdateCoarseGrid"); + NgProfiler::RegionTimer reg(timer); + + + (*testout) << "UPDATE COARSE GRID PARALLEL TOPOLOGY " << endl; + if (id == 0) + PrintMessage (1, "update parallel topology"); + + + const MeshTopology & topology = mesh.GetTopology(); + + NgArray cnt_send(ntasks); + + int maxsize = comm.AllReduce (mesh.mlbetweennodes.Size(), MPI_MAX); + // update new vertices after mesh-refinement + if (maxsize > 0) + { + int newnv = mesh.mlbetweennodes.Size(); + + loc2distvert.ChangeSize(mesh.mlbetweennodes.Size()); + + bool changed = true; + while (changed) + { + changed = false; + + // build exchange vertices + cnt_send = 0; + for (PointIndex pi : mesh.Points().Range()) + for (int dist : GetDistantProcs(pi)) + cnt_send[dist]++; + TABLE dest2vert(cnt_send); + for (PointIndex pi : mesh.Points().Range()) + for (int dist : GetDistantProcs(pi)) + dest2vert.Add (dist, pi); + + for (PointIndex pi = PointIndex::BASE; pi < newnv+PointIndex::BASE; pi++) + if (auto [v1,v2] = mesh.mlbetweennodes[pi]; v1.IsValid()) + { + auto procs1 = GetDistantProcs(v1); + auto procs2 = GetDistantProcs(v2); + for (int p : procs1) + if (procs2.Contains(p)) + cnt_send[p]++; + } + + TABLE dest2pair(cnt_send); + + for (PointIndex pi : mesh.mlbetweennodes.Range()) + if (auto [v1,v2] = mesh.mlbetweennodes[pi]; v1.IsValid()) + { + auto procs1 = GetDistantProcs(v1); + auto procs2 = GetDistantProcs(v2); + for (int p : procs1) + if (procs2.Contains(p)) + dest2pair.Add (p, pi); + } + + cnt_send = 0; + for (PointIndex pi : mesh.mlbetweennodes.Range()) + if (auto [v1,v2] = mesh.mlbetweennodes[pi]; v1.IsValid()) + { + auto procs1 = GetDistantProcs(v1); + auto procs2 = GetDistantProcs(v2); + + for (int p : procs1) + if (procs2.Contains(p)) + cnt_send[p]+=2; + } + + TABLE send_verts(cnt_send); + + NgArray loc2exchange(mesh.GetNV()); + + for (int dest = 0; dest < ntasks; dest++) + if (dest != id) + { + loc2exchange = -1; + int cnt = 0; + for (PointIndex pi : dest2vert[dest]) + loc2exchange[pi] = cnt++; + + for (PointIndex pi : dest2pair[dest]) + if (auto [v1,v2] = mesh.mlbetweennodes[pi]; v1.IsValid()) + { + auto procs1 = GetDistantProcs(v1); + auto procs2 = GetDistantProcs(v2); + + if (procs1.Contains(dest) && procs2.Contains(dest)) + { + send_verts.Add (dest, loc2exchange[v1]); + send_verts.Add (dest, loc2exchange[v2]); + } + } + } + + TABLE recv_verts(ntasks); + MyMPI_ExchangeTable (send_verts, recv_verts, MPI_TAG_MESH+9, comm); + + for (int dest = 0; dest < ntasks; dest++) + if (dest != id) + { + loc2exchange = -1; + int cnt = 0; + + for (PointIndex pi : dest2vert[dest]) + loc2exchange[pi] = cnt++; + + NgFlatArray recvarray = recv_verts[dest]; + for (int ii = 0; ii < recvarray.Size(); ii+=2) + for (PointIndex pi : dest2pair[dest]) + { + PointIndex v1 = mesh.mlbetweennodes[pi][0]; + PointIndex v2 = mesh.mlbetweennodes[pi][1]; + if (v1.IsValid()) + { + INDEX_2 re(recvarray[ii], recvarray[ii+1]); + INDEX_2 es(loc2exchange[v1], loc2exchange[v2]); + // if (es == re && !IsExchangeVert(dest, pi)) + if (es == re && !GetDistantProcs(pi).Contains(dest)) + { + // SetDistantPNum(dest, pi); + AddDistantProc (pi, dest); + changed = true; + } + } + } + } + + changed = comm.AllReduce (changed, MPI_LOR); + } + } + + NgArray sendarray, recvarray; + // cout << "UpdateCoarseGrid - edges" << endl; + + // static int timerv = NgProfiler::CreateTimer ("UpdateCoarseGrid - ex vertices"); + static int timere = NgProfiler::CreateTimer ("UpdateCoarseGrid - ex edges"); + static int timerf = NgProfiler::CreateTimer ("UpdateCoarseGrid - ex faces"); + + + NgProfiler::StartTimer (timere); + + // build exchange vertices + cnt_send = 0; + for (PointIndex pi : mesh.Points().Range()) + for (int dist : GetDistantProcs(pi)) + cnt_send[dist]++; + TABLE dest2vert(cnt_send); + for (PointIndex pi : mesh.Points().Range()) + for (int dist : GetDistantProcs(pi)) + dest2vert.Add (dist, pi); + + // MPI_Group_free(&MPI_LocalGroup); + // MPI_Comm_free(&MPI_LocalComm); + } void ParallelMeshTopology :: UpdateCoarseGrid () { + static Timer t("ParallelTopology::UpdateCoarseGrid"); RegionTimer r(t); // cout << "UpdateCoarseGrid" << endl; // if (is_updated) return; @@ -232,7 +581,7 @@ namespace netgen // UpdateCoarseGridGlobal(); - + /* // MPI_Barrier (MPI_COMM_WORLD); MPI_Group MPI_GROUP_comm; @@ -244,151 +593,19 @@ namespace netgen MPI_Group_excl (MPI_GROUP_comm, 1, process_ranks, &MPI_LocalGroup); MPI_Comm_create (comm, MPI_LocalGroup, &MPI_LocalComm); - if (id == 0) return; - + if (id == 0) + { + // SetNV(0); + // EnumeratePointsGlobally(); + return; + } + */ + const MeshTopology & topology = mesh.GetTopology(); - NgArray cnt_send(ntasks-1); + NgArray cnt_send(ntasks); - - // update new vertices after mesh-refinement - if (mesh.mlbetweennodes.Size() > 0) - { - // cout << "UpdateCoarseGrid - vertices" << endl; - int newnv = mesh.mlbetweennodes.Size(); - loc2distvert.ChangeSize(mesh.mlbetweennodes.Size()); - /* - for (PointIndex pi = PointIndex::BASE; pi < newnv+PointIndex::BASE; pi++) - { - PointIndex v1 = mesh.mlbetweennodes[pi][0]; - PointIndex v2 = mesh.mlbetweennodes[pi][1]; - if (mesh.mlbetweennodes[pi][0] != PointIndex::BASE-1) - for (int dest = 1; dest < ntasks; dest++) - if (IsExchangeVert (dest, v1) && IsExchangeVert (dest, v2)) - SetDistantPNum(dest, pi); - } - */ - - bool changed = true; - while (changed) - { - changed = false; - - // build exchange vertices - cnt_send = 0; - for (PointIndex pi : mesh.Points().Range()) - for (int dist : GetDistantPNums(pi-PointIndex::BASE)) - cnt_send[dist-1]++; - TABLE dest2vert(cnt_send); - for (PointIndex pi : mesh.Points().Range()) - for (int dist : GetDistantPNums(pi-PointIndex::BASE)) - dest2vert.Add (dist-1, pi); - - - for (PointIndex pi = PointIndex::BASE; pi < newnv+PointIndex::BASE; pi++) - { - PointIndex v1 = mesh.mlbetweennodes[pi][0]; - PointIndex v2 = mesh.mlbetweennodes[pi][1]; - if (mesh.mlbetweennodes[pi][0] != PointIndex::BASE-1) - // for (int dest = 1; dest < ntasks; dest++) - for (int dest : GetDistantPNums(v1-PointIndex::BASE)) - if (IsExchangeVert (dest, v1) && IsExchangeVert (dest, v2)) - cnt_send[dest-1]++; - } - - TABLE dest2pair(cnt_send); - // for (int dest = 1; dest < ntasks; dest++) - for (PointIndex pi = PointIndex::BASE; pi < newnv+PointIndex::BASE; pi++) - { - PointIndex v1 = mesh.mlbetweennodes[pi][0]; - PointIndex v2 = mesh.mlbetweennodes[pi][1]; - if (mesh.mlbetweennodes[pi][0] != PointIndex::BASE-1) - for (int dest : GetDistantPNums(v1-PointIndex::BASE)) - if (IsExchangeVert (dest, v1) && IsExchangeVert (dest, v2)) - dest2pair.Add (dest-1, pi); - } - - cnt_send = 0; - int v1, v2; - for (PointIndex pi = PointIndex::BASE; pi < newnv+PointIndex::BASE; pi++) - { - PointIndex v1 = mesh.mlbetweennodes[pi][0]; - PointIndex v2 = mesh.mlbetweennodes[pi][1]; - if (mesh.mlbetweennodes[pi][0] != PointIndex::BASE-1) - for (int dest : GetDistantPNums(v1-PointIndex::BASE)) - if (IsExchangeVert(dest, v2)) - cnt_send[dest-1]+=2; - } - - TABLE send_verts(cnt_send); - - NgArray loc2exchange(mesh.GetNV()); - for (int dest = 1; dest < ntasks; dest++) - if (dest != id) - { - loc2exchange = -1; - int cnt = 0; - /* - for (PointIndex pi : mesh.Points().Range()) - if (IsExchangeVert(dest, pi)) - loc2exchange[pi] = cnt++; - */ - for (PointIndex pi : dest2vert[dest-1]) - loc2exchange[pi] = cnt++; - - // for (PointIndex pi = PointIndex::BASE; pi < newnv+PointIndex::BASE; pi++) - for (PointIndex pi : dest2pair[dest-1]) - { - PointIndex v1 = mesh.mlbetweennodes[pi][0]; - PointIndex v2 = mesh.mlbetweennodes[pi][1]; - if (mesh.mlbetweennodes[pi][0] != PointIndex::BASE-1) - if (IsExchangeVert (dest, v1) && IsExchangeVert (dest, v2)) - { - send_verts.Add (dest-1, loc2exchange[v1]); - send_verts.Add (dest-1, loc2exchange[v2]); - } - } - } - - TABLE recv_verts(ntasks-1); - MyMPI_ExchangeTable (send_verts, recv_verts, MPI_TAG_MESH+9, MPI_LocalComm); - - for (int dest = 1; dest < ntasks; dest++) - if (dest != id) - { - loc2exchange = -1; - int cnt = 0; - /* - for (PointIndex pi : mesh.Points().Range()) - if (IsExchangeVert(dest, pi)) - loc2exchange[pi] = cnt++; - */ - for (PointIndex pi : dest2vert[dest-1]) - loc2exchange[pi] = cnt++; - - NgFlatArray recvarray = recv_verts[dest-1]; - for (int ii = 0; ii < recvarray.Size(); ii+=2) - for (PointIndex pi : dest2pair[dest-1]) - // for (PointIndex pi = PointIndex::BASE; pi < newnv+PointIndex::BASE; pi++) - { - PointIndex v1 = mesh.mlbetweennodes[pi][0]; - PointIndex v2 = mesh.mlbetweennodes[pi][1]; - if (mesh.mlbetweennodes[pi][0] != PointIndex::BASE-1) - { - INDEX_2 re(recvarray[ii], recvarray[ii+1]); - INDEX_2 es(loc2exchange[v1], loc2exchange[v2]); - if (es == re && !IsExchangeVert(dest, pi)) - { - SetDistantPNum(dest, pi); - changed = true; - } - } - } - } - } - } - - NgArray sendarray, recvarray; + // NgArray sendarray, recvarray; // cout << "UpdateCoarseGrid - edges" << endl; // static int timerv = NgProfiler::CreateTimer ("UpdateCoarseGrid - ex vertices"); @@ -405,12 +622,12 @@ namespace netgen // build exchange vertices cnt_send = 0; for (PointIndex pi : mesh.Points().Range()) - for (int dist : GetDistantPNums(pi-PointIndex::BASE)) - cnt_send[dist-1]++; + for (int dist : GetDistantProcs(pi)) + cnt_send[dist]++; TABLE dest2vert(cnt_send); for (PointIndex pi : mesh.Points().Range()) - for (int dist : GetDistantPNums(pi-PointIndex::BASE)) - dest2vert.Add (dist-1, pi); + for (int dist : GetDistantProcs(pi)) + dest2vert.Add (dist, pi); // exchange edges cnt_send = 0; @@ -418,9 +635,15 @@ namespace netgen for (int edge = 1; edge <= ned; edge++) { topology.GetEdgeVertices (edge, v1, v2); + /* for (int dest = 1; dest < ntasks; dest++) - if (IsExchangeVert (dest, v1) && IsExchangeVert (dest, v2)) + // if (IsExchangeVert (dest, v1) && IsExchangeVert (dest, v2)) + if (GetDistantProcs(v1).Contains(dest) && GetDistantProcs(v2).Contains(dest)) cnt_send[dest-1]+=1; + */ + for (auto p : GetDistantProcs(v1)) + if (GetDistantProcs(v2).Contains(p)) + cnt_send[p]+=1; } TABLE dest2edge(cnt_send); @@ -430,73 +653,58 @@ namespace netgen for (int edge = 1; edge <= ned; edge++) { topology.GetEdgeVertices (edge, v1, v2); - for (int dest = 1; dest < ntasks; dest++) - if (IsExchangeVert (dest, v1) && IsExchangeVert (dest, v2)) - dest2edge.Add (dest-1, edge); + for (int dest = 0; dest < ntasks; dest++) + // if (IsExchangeVert (dest, v1) && IsExchangeVert (dest, v2)) + if (GetDistantProcs(v1).Contains(dest) && GetDistantProcs(v2).Contains(dest)) + dest2edge.Add (dest, edge); } NgArray loc2exchange(mesh.GetNV()); - for (int dest = 1; dest < ntasks; dest++) + for (int dest = 0; dest < ntasks; dest++) { loc2exchange = -1; int cnt = 0; - for (PointIndex pi : dest2vert[dest-1]) + for (PointIndex pi : dest2vert[dest]) loc2exchange[pi] = cnt++; - for (int edge : dest2edge[dest-1]) + for (int edge : dest2edge[dest]) { topology.GetEdgeVertices (edge, v1, v2); - if (IsExchangeVert (dest, v1) && IsExchangeVert (dest, v2)) + // if (IsExchangeVert (dest, v1) && IsExchangeVert (dest, v2)) + if (GetDistantProcs(v1).Contains(dest) && GetDistantProcs(v2).Contains(dest)) { - send_edges.Add (dest-1, loc2exchange[v1]); - send_edges.Add (dest-1, loc2exchange[v2]); + send_edges.Add (dest, loc2exchange[v1]); + send_edges.Add (dest, loc2exchange[v2]); } } } // cout << "UpdateCoarseGrid - edges mpi-exchange" << endl; - TABLE recv_edges(ntasks-1); - MyMPI_ExchangeTable (send_edges, recv_edges, MPI_TAG_MESH+9, MPI_LocalComm); + TABLE recv_edges(ntasks); + MyMPI_ExchangeTable (send_edges, recv_edges, MPI_TAG_MESH+9, comm); // cout << "UpdateCoarseGrid - edges mpi-exchange done" << endl; - /* - for (int dest = 1; dest < ntasks; dest++) + for (int dest = 0; dest < ntasks; dest++) { - auto ex2loc = dest2vert[dest-1]; - NgFlatArray recvarray = recv_edges[dest-1]; - for (int ii = 0; ii < recvarray.Size(); ii+=2) - for (int edge : dest2edge[dest-1]) - { - topology.GetEdgeVertices (edge, v1, v2); - INDEX_2 re(ex2loc[recvarray[ii]], - ex2loc[recvarray[ii+1]]); - INDEX_2 es(v1, v2); - if (es == re) - SetDistantEdgeNum(dest, edge); - } - } - */ - - for (int dest = 1; dest < ntasks; dest++) - { - auto ex2loc = dest2vert[dest-1]; + auto ex2loc = dest2vert[dest]; if (ex2loc.Size() == 0) continue; - INDEX_2_CLOSED_HASHTABLE vert2edge(2*dest2edge[dest-1].Size()+10); - for (int edge : dest2edge[dest-1]) + INDEX_2_CLOSED_HASHTABLE vert2edge(2*dest2edge[dest].Size()+10); + for (int edge : dest2edge[dest]) { topology.GetEdgeVertices (edge, v1, v2); vert2edge.Set(INDEX_2(v1,v2), edge); } - NgFlatArray recvarray = recv_edges[dest-1]; + NgFlatArray recvarray = recv_edges[dest]; for (int ii = 0; ii < recvarray.Size(); ii+=2) { INDEX_2 re(ex2loc[recvarray[ii]], ex2loc[recvarray[ii+1]]); if (vert2edge.Used(re)) - SetDistantEdgeNum(dest, vert2edge.Get(re)); + // SetDistantEdgeNum(dest, vert2edge.Get(re)); + AddDistantEdgeProc(vert2edge.Get(re)-1, dest); } } @@ -504,8 +712,6 @@ namespace netgen NgProfiler::StopTimer (timere); - // MPI_Barrier (MPI_LocalComm); - // cout << "UpdateCoarseGrid - faces" << endl; if (mesh.GetDimension() == 3) { @@ -517,190 +723,104 @@ namespace netgen for (int face = 1; face <= nfa; face++) { topology.GetFaceVertices (face, verts); - for (int dest = 1; dest < ntasks; dest++) + for (int dest = 0; dest < ntasks; dest++) if (dest != id) + /* if (IsExchangeVert (dest, verts[0]) && IsExchangeVert (dest, verts[1]) && IsExchangeVert (dest, verts[2])) - cnt_send[dest-1]++; + */ + if (GetDistantProcs (verts[0]).Contains(dest) && + GetDistantProcs (verts[1]).Contains(dest) && + GetDistantProcs (verts[2]).Contains(dest)) + cnt_send[dest]++; } TABLE dest2face(cnt_send); for (int face = 1; face <= nfa; face++) { topology.GetFaceVertices (face, verts); - for (int dest = 1; dest < ntasks; dest++) + for (int dest = 0; dest < ntasks; dest++) if (dest != id) + /* if (IsExchangeVert (dest, verts[0]) && IsExchangeVert (dest, verts[1]) && IsExchangeVert (dest, verts[2])) - dest2face.Add(dest-1, face); + */ + if (GetDistantProcs (verts[0]).Contains(dest) && + GetDistantProcs (verts[1]).Contains(dest) && + GetDistantProcs (verts[2]).Contains(dest)) + dest2face.Add(dest, face); } for (int & c : cnt_send) c*=3; TABLE send_faces(cnt_send); NgArray loc2exchange(mesh.GetNV()); - for (int dest = 1; dest < ntasks; dest++) + for (int dest = 0; dest < ntasks; dest++) if (dest != id) { - /* - loc2exchange = -1; - int cnt = 0; - for (PointIndex pi : mesh.Points().Range()) - if (IsExchangeVert(dest, pi)) - loc2exchange[pi] = cnt++; - */ - if (dest2vert[dest-1].Size() == 0) continue; + if (dest2vert[dest].Size() == 0) continue; loc2exchange = -1; int cnt = 0; - for (PointIndex pi : dest2vert[dest-1]) + for (PointIndex pi : dest2vert[dest]) loc2exchange[pi] = cnt++; - for (int face : dest2face[dest-1]) + for (int face : dest2face[dest]) { topology.GetFaceVertices (face, verts); + /* if (IsExchangeVert (dest, verts[0]) && IsExchangeVert (dest, verts[1]) && IsExchangeVert (dest, verts[2])) + */ + if (GetDistantProcs (verts[0]).Contains(dest) && + GetDistantProcs (verts[1]).Contains(dest) && + GetDistantProcs (verts[2]).Contains(dest)) { - send_faces.Add (dest-1, loc2exchange[verts[0]]); - send_faces.Add (dest-1, loc2exchange[verts[1]]); - send_faces.Add (dest-1, loc2exchange[verts[2]]); + send_faces.Add (dest, loc2exchange[verts[0]]); + send_faces.Add (dest, loc2exchange[verts[1]]); + send_faces.Add (dest, loc2exchange[verts[2]]); } } } // cout << "UpdateCoarseGrid - faces mpi-exchange" << endl; - TABLE recv_faces(ntasks-1); - MyMPI_ExchangeTable (send_faces, recv_faces, MPI_TAG_MESH+9, MPI_LocalComm); + TABLE recv_faces(ntasks); + MyMPI_ExchangeTable (send_faces, recv_faces, MPI_TAG_MESH+9, comm); // cout << "UpdateCoarseGrid - faces mpi-exchange done" << endl; - - /* - for (int dest = 1; dest < ntasks; dest++) - if (dest != id) - { - loc2exchange = -1; - int cnt = 0; - for (PointIndex pi : dest2vert[dest-1]) - loc2exchange[pi] = cnt++; - - NgFlatArray recvarray = recv_faces[dest-1]; - for (int ii = 0; ii < recvarray.Size(); ii+=3) - for (int face : dest2face[dest-1]) - { - topology.GetFaceVertices (face, verts); - INDEX_3 re(recvarray[ii], recvarray[ii+1], recvarray[ii+2]); - INDEX_3 es(loc2exchange[verts[0]], loc2exchange[verts[1]], loc2exchange[verts[2]]); - if (es == re) - SetDistantFaceNum(dest, face); - } - } - */ - - for (int dest = 1; dest < ntasks; dest++) + for (int dest = 0; dest < ntasks; dest++) { - auto ex2loc = dest2vert[dest-1]; + auto ex2loc = dest2vert[dest]; if (ex2loc.Size() == 0) continue; - INDEX_3_CLOSED_HASHTABLE vert2face(2*dest2face[dest-1].Size()+10); - for (int face : dest2face[dest-1]) + INDEX_3_CLOSED_HASHTABLE vert2face(2*dest2face[dest].Size()+10); + for (int face : dest2face[dest]) { topology.GetFaceVertices (face, verts); vert2face.Set(INDEX_3(verts[0], verts[1], verts[2]), face); } - NgFlatArray recvarray = recv_faces[dest-1]; + NgFlatArray recvarray = recv_faces[dest]; for (int ii = 0; ii < recvarray.Size(); ii+=3) { INDEX_3 re(ex2loc[recvarray[ii]], ex2loc[recvarray[ii+1]], ex2loc[recvarray[ii+2]]); if (vert2face.Used(re)) - SetDistantFaceNum(dest, vert2face.Get(re)); + AddDistantFaceProc(vert2face.Get(re)-1, dest); } } - - - - - - /* - NgArray glob2loc; - - int maxface = 0; - for (int face = 1; face <= nfa; face++) - maxface = max (maxface, GetGlobalFaceNum (face)); - - // glob2loc.SetSize (nfaglob); - glob2loc.SetSize (maxface); - glob2loc = -1; - - for (int loc = 1; loc <= nfa; loc++) - glob2loc[GetGlobalFaceNum(loc)] = loc; - - cnt_send = 0; - NgArray verts; - for (int face = 1; face <= nfa; face++) - { - topology.GetFaceVertices (face, verts); - for (int dest = 1; dest < ntasks; dest++) - if (IsExchangeVert (dest, verts[0]) && - IsExchangeVert (dest, verts[1]) && - IsExchangeVert (dest, verts[2])) - { - cnt_send[dest-1]+=2; - } - } - - TABLE send_faces(cnt_send); - for (int face = 1; face <= nfa; face++) - { - topology.GetFaceVertices (face, verts); - for (int dest = 1; dest < ntasks; dest++) - { - if (IsExchangeVert (dest, verts[0]) && - IsExchangeVert (dest, verts[1]) && - IsExchangeVert (dest, verts[2])) - { - send_faces.Add (dest-1, GetGlobalFaceNum(face)); - send_faces.Add (dest-1, face); - } - } - } - TABLE recv_faces(ntasks-1); - MyMPI_ExchangeTable (send_faces, recv_faces, MPI_TAG_MESH+8, MPI_LocalComm); - - for (int sender = 1; sender < ntasks; sender ++) - if (id != sender) - { - NgFlatArray recvarray = recv_faces[sender-1]; - - for (int ii = 0; ii < recvarray.Size(); ) - { - int globf = recvarray[ii++]; - int distf = recvarray[ii++]; - - if (globf <= maxface) - { - int locf = glob2loc[globf]; - if (locf != -1) - SetDistantFaceNum (sender, locf); - } - } - } - */ - NgProfiler::StopTimer (timerf); } // cout << "UpdateCoarseGrid - done" << endl; - + // EnumeratePointsGlobally(); is_updated = true; - MPI_Group_free(&MPI_LocalGroup); - MPI_Comm_free(&MPI_LocalComm); + // MPI_Group_free(&MPI_LocalGroup); + // MPI_Comm_free(&MPI_LocalComm); } } diff --git a/libsrc/meshing/paralleltop.hpp b/libsrc/meshing/paralleltop.hpp index 197ba62e..f6826328 100644 --- a/libsrc/meshing/paralleltop.hpp +++ b/libsrc/meshing/paralleltop.hpp @@ -14,10 +14,14 @@ namespace netgen each row of the table corresponds to one vertex each row contains a list of pairs (procnr, dist_vnum) */ + + DynamicTable loc2distvert; + DynamicTable loc2distedge, loc2distface; - TABLE loc2distvert, loc2distedge, loc2distface; + Array glob_vert; - NgArray glob_vert, glob_edge, glob_face; + // will get rid of them + NgArray glob_edge, glob_face; NgArray glob_el, glob_surfel, glob_segm; bool is_updated; @@ -32,77 +36,130 @@ namespace netgen void UpdateCoarseGrid(); void UpdateCoarseGridGlobal(); - // bool DoCoarseUpdate() const { return !coarseupdate; } + void IdentifyVerticesAfterRefinement(); + void EnumeratePointsGlobally (); + + void AddDistantProc (PointIndex pi, int proc) { loc2distvert.AddUnique (pi-PointIndex::BASE, proc); } + void AddDistantFaceProc (int edge, int proc) { loc2distface.AddUnique (edge, proc); } + void AddDistantEdgeProc (int face, int proc) { loc2distedge.AddUnique (face, proc); } + + FlatArray GetDistantProcs (PointIndex pi) const { return loc2distvert[pi-PointIndex::BASE]; } + FlatArray GetDistantFaceProcs (int locnum) const { return loc2distface[locnum]; } + FlatArray GetDistantEdgeProcs (int locnum) const { return loc2distedge[locnum]; } + + auto & L2G (PointIndex pi) { return glob_vert[pi-PointIndex::BASE]; } + auto L2G (PointIndex pi) const { return glob_vert[pi-PointIndex::BASE]; } + /// set number of local vertices, reset sizes of loc2dist_vert, isexchangevert... void SetNV (int anv); + void SetNV_Loc2Glob (int anv); void SetNE (int ane); void SetNSE (int anse); void SetNSegm (int anseg); + [[deprecated("Use AddDistantFaceProc instead!")]] + void SetDistantFaceNum (int dest, int locnum) { loc2distface.AddUnique (locnum-1, dest); } + [[deprecated("Use AddDistantProc instead!")]] + void SetDistantPNum (int dest, int locnum) { loc2distvert.AddUnique (locnum-1, dest); } + [[deprecated("Use AddDistantEdgeProc instead!")]] + void SetDistantEdgeNum (int dest, int locnum) { loc2distedge.AddUnique (locnum-1, dest); } + [[deprecated("Use GetDistantFaceProcx instead!")]] + FlatArray GetDistantFaceNums (int locnum) const { return loc2distface[locnum]; } + [[deprecated("Use GetDistantEdgeProcx instead!")]] + FlatArray GetDistantEdgeNums (int locnum) const { return loc2distedge[locnum]; } + + + + [[deprecated("Use L2G(pi) instead!")]] void SetLoc2Glob_Vert (int locnum, int globnum) { glob_vert[locnum-1] = globnum; } + [[deprecated("Try to avoid global enumration!")]] void SetLoc2Glob_Edge (int locnum, int globnum) { glob_edge[locnum-1] = globnum; } + [[deprecated("Try to avoid global enumration!")]] void SetLoc2Glob_Face (int locnum, int globnum) { glob_face[locnum-1] = globnum; } + [[deprecated("Try to avoid global enumration!")]] void SetLoc2Glob_VolEl (int locnum, int globnum) { glob_el[locnum-1] = globnum; } + [[deprecated("Try to avoid global enumration!")]] void SetLoc2Glob_SurfEl (int locnum, int globnum) { glob_surfel[locnum-1] = globnum; } + [[deprecated("Try to avoid global enumration!")]] void SetLoc2Glob_Segm (int locnum, int globnum) { glob_segm[locnum-1] = globnum; } int GetGlobalPNum (int locnum) const { return glob_vert[locnum-1]; } + [[deprecated("Try to avoid global enumration!")]] int GetGlobalEdgeNum (int locnum) const { return glob_edge[locnum-1]; } + [[deprecated("Try to avoid global enumration!")]] int GetGlobalFaceNum (int locnum) const { return glob_face[locnum-1]; } + [[deprecated("Try to avoid global enumration!")]] int GetGlobalElNum (int locnum) const { return glob_el[locnum-1]; } + [[deprecated("Try to avoid global enumration!")]] int GetGlobalSElNum (int locnum) const { return glob_surfel[locnum-1]; } - - void SetDistantFaceNum (int dest, int locnum); - void SetDistantPNum (int dest, int locnum); - void SetDistantEdgeNum (int dest, int locnum); - - int GetNDistantPNums (int locpnum) const { return loc2distvert[locpnum-1].Size(); } - int GetNDistantFaceNums (int locfacenum) const { return loc2distface[locfacenum-1].Size(); } - int GetNDistantEdgeNums ( int locedgenum) const { return loc2distedge[locedgenum-1].Size(); } + + [[deprecated("Use GetDistantPNums(locnum).Size() instead!")]] + int GetNDistantPNums (int locpnum) const { return loc2distvert[locpnum-1].Size(); } + + [[deprecated("Use GetDistantFaceNums(locnum).Size() instead!")]] + int GetNDistantFaceNums (int locfacenum) const { return loc2distface[locfacenum-1].Size(); } + + [[deprecated("Use GetDistantEdgeNums(locnum).Size() instead!")]] + int GetNDistantEdgeNums ( int locedgenum) const { return loc2distedge[locedgenum-1].Size(); } + + [[deprecated("Use GetDistantPNums(locnum) -> FlatArray instead!")]] void GetDistantPNums (int locpnum, int * distpnums ) const { for (int i = 0; i < loc2distvert[locpnum-1].Size(); i++ ) distpnums[i] = loc2distvert[locpnum-1][i]; } - + + [[deprecated("Use GetDistantFaceNums(locnum) -> FlatArray instead!")]] void GetDistantFaceNums (int locfacenum, int * distfacenums ) const { for ( int i = 0; i < loc2distface[locfacenum-1].Size(); i++ ) distfacenums[i] = loc2distface[locfacenum-1][i]; } + [[deprecated("Use GetDistantFaceNums(locnum) -> FlatArray instead!")]] void GetDistantFaceNums (int locfacenum, NgArray & distfacenums ) const { - distfacenums = loc2distface[locfacenum-1]; + // distfacenums = loc2distface[locfacenum-1]; + auto loc = loc2distface[locfacenum-1]; + distfacenums.SetSize (loc.Size()); + for (int i = 0; i < loc.Size(); i++) + distfacenums[i] = loc[i]; } - + + [[deprecated("Use GetDistantEdgeNums(locnum) -> FlatArray instead!")]] void GetDistantEdgeNums (int locedgenum, int * distedgenums ) const { for (int i = 0; i < loc2distedge[locedgenum-1].Size(); i++ ) distedgenums[i] = loc2distedge[locedgenum-1][i]; } + [[deprecated("Use GetDistantEdgeNums(locnum) -> FlatArray instead!")]] void GetDistantEdgeNums (int locedgenum, NgArray & distedgenums ) const { - distedgenums = loc2distedge[locedgenum-1]; + // distedgenums = loc2distedge[locedgenum-1]; + auto loc = loc2distedge[locedgenum-1]; + distedgenums.SetSize (loc.Size()); + for (int i = 0; i < loc.Size(); i++) + distedgenums[i] = loc[i]; } - NgFlatArray GetDistantPNums (int locnum) const { return loc2distvert[locnum]; } - NgFlatArray GetDistantFaceNums (int locnum) const { return loc2distface[locnum]; } - NgFlatArray GetDistantEdgeNums (int locnum) const { return loc2distedge[locnum]; } + [[deprecated("Use GetDistantProcs(..)!")]] + FlatArray GetDistantPNums (int locnum) const { return loc2distvert[locnum]; } + + + [[deprecated("Use GetDistantProcs(..).Contains instead!")]] bool IsExchangeVert (int dest, int vnum) const { return loc2distvert[vnum-1].Contains (dest); } }; - } diff --git a/libsrc/meshing/python_mesh.cpp b/libsrc/meshing/python_mesh.cpp index 45adc9fd..fd88061c 100644 --- a/libsrc/meshing/python_mesh.cpp +++ b/libsrc/meshing/python_mesh.cpp @@ -1,5 +1,7 @@ #ifdef NG_PYTHON +#include + #include <../general/ngpython.hpp> #include #include "python_mesh.hpp" @@ -9,6 +11,62 @@ // #include // #include #include <../interface/writeuser.hpp> +#include <../include/nginterface.h> + + +class ClearSolutionClass +{ +public: + ClearSolutionClass() { } + ~ClearSolutionClass() { Ng_ClearSolutionData(); } +}; + + +#ifdef NG_MPI4PY +#include + +struct mpi4py_comm { + mpi4py_comm() = default; + mpi4py_comm(MPI_Comm value) : value(value) {} + operator MPI_Comm () { return value; } + + MPI_Comm value; +}; + +namespace pybind11 { namespace detail { + template <> struct type_caster { + public: + PYBIND11_TYPE_CASTER(mpi4py_comm, _("mpi4py_comm")); + + // Python -> C++ + bool load(handle src, bool) { + PyObject *py_src = src.ptr(); + // Check that we have been passed an mpi4py communicator + if (PyObject_TypeCheck(py_src, &PyMPIComm_Type)) { + // Convert to regular MPI communicator + value.value = *PyMPIComm_Get(py_src); + } else { + return false; + } + + return !PyErr_Occurred(); + } + + // C++ -> Python + static handle cast(mpi4py_comm src, + return_value_policy /* policy */, + handle /* parent */) + { + // Create an mpi4py handle + return PyMPIComm_New(src.value); + } + }; +}} // namespace pybind11::detail + +#endif // NG_MPI4PY + + + using namespace netgen; @@ -20,23 +78,6 @@ namespace netgen extern bool netgen_executable_started; extern shared_ptr ng_geometry; extern void Optimize2d (Mesh & mesh, MeshingParameters & mp); - -#ifdef PARALLEL - /** we need allreduce in python-wrapped communicators **/ - template - inline T MyMPI_AllReduceNG (T d, const MPI_Op & op /* = MPI_SUM */, MPI_Comm comm) - { - T global_d; - MPI_Allreduce ( &d, &global_d, 1, MyGetMPIType(), op, comm); - return global_d; - } -#else - // enum { MPI_SUM = 0, MPI_MIN = 1, MPI_MAX = 2 }; - // typedef int MPI_Op; - template - inline T MyMPI_AllReduceNG (T d, const MPI_Op & op /* = MPI_SUM */, MPI_Comm comm) - { return d; } -#endif } @@ -48,8 +89,15 @@ void TranslateException (const NgException & ex) static Transformation<3> global_trafo(Vec<3> (0,0,0)); + + + + DLL_HEADER void ExportNetgenMeshing(py::module &m) { +#ifdef NG_MPI4PY + import_mpi4py(); +#endif // NG_MPI4PY py::register_exception(m, "NgException"); m.attr("_netgen_executable_started") = py::cast(netgen::netgen_executable_started); string script; @@ -69,6 +117,13 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) m.def("_SetThreadPercentage", [](double percent) { SetThreadPercent(percent); }); py::class_ (m, "MPI_Comm") +#ifdef NG_MPI4PY + .def(py::init([] (mpi4py_comm comm) + { + return NgMPI_Comm(comm); + })) + .def_property_readonly ("mpi4py", [] (NgMPI_Comm comm) { return mpi4py_comm(comm); }) +#endif // NG_MPI4PY .def_property_readonly ("rank", &NgMPI_Comm::Rank) .def_property_readonly ("size", &NgMPI_Comm::Size) .def("Barrier", &NgMPI_Comm::Barrier) @@ -78,15 +133,15 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) #else .def("WTime", [](NgMPI_Comm & c) { return -1.0; }) #endif - .def("Sum", [](NgMPI_Comm & c, double x) { return MyMPI_AllReduceNG(x, MPI_SUM, c); }) - .def("Min", [](NgMPI_Comm & c, double x) { return MyMPI_AllReduceNG(x, MPI_MIN, c); }) - .def("Max", [](NgMPI_Comm & c, double x) { return MyMPI_AllReduceNG(x, MPI_MAX, c); }) - .def("Sum", [](NgMPI_Comm & c, int x) { return MyMPI_AllReduceNG(x, MPI_SUM, c); }) - .def("Min", [](NgMPI_Comm & c, int x) { return MyMPI_AllReduceNG(x, MPI_MIN, c); }) - .def("Max", [](NgMPI_Comm & c, int x) { return MyMPI_AllReduceNG(x, MPI_MAX, c); }) - .def("Sum", [](NgMPI_Comm & c, size_t x) { return MyMPI_AllReduceNG(x, MPI_SUM, c); }) - .def("Min", [](NgMPI_Comm & c, size_t x) { return MyMPI_AllReduceNG(x, MPI_MIN, c); }) - .def("Max", [](NgMPI_Comm & c, size_t x) { return MyMPI_AllReduceNG(x, MPI_MAX, c); }) + .def("Sum", [](NgMPI_Comm & c, double x) { return c.AllReduce(x, MPI_SUM); }) + .def("Min", [](NgMPI_Comm & c, double x) { return c.AllReduce(x, MPI_MIN); }) + .def("Max", [](NgMPI_Comm & c, double x) { return c.AllReduce(x, MPI_MAX); }) + .def("Sum", [](NgMPI_Comm & c, int x) { return c.AllReduce(x, MPI_SUM); }) + .def("Min", [](NgMPI_Comm & c, int x) { return c.AllReduce(x, MPI_MIN); }) + .def("Max", [](NgMPI_Comm & c, int x) { return c.AllReduce(x, MPI_MAX); }) + .def("Sum", [](NgMPI_Comm & c, size_t x) { return c.AllReduce(x, MPI_SUM); }) + .def("Min", [](NgMPI_Comm & c, size_t x) { return c.AllReduce(x, MPI_MIN); }) + .def("Max", [](NgMPI_Comm & c, size_t x) { return c.AllReduce(x, MPI_MAX); }) .def("SubComm", [](NgMPI_Comm & c, std::vector proc_list) { Array procs(proc_list.size()); for (int i = 0; i < procs.Size(); i++) @@ -98,6 +153,9 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) ; +#ifdef NG_MPI4PY + py::implicitly_convertible(); +#endif // NG_MPI4PY py::class_(m, "NGDummyArgument") @@ -106,6 +164,10 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) py::class_> (m, "Point2d") .def(py::init()) + .def(py::init( [] (std::pair xy) + { + return Point<2>{xy.first, xy.second}; + })) .def ("__str__", &ToString>) .def(py::self-py::self) .def(py::self+Vec<2>()) @@ -113,8 +175,15 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) .def("__getitem__", [](Point<2>& self, int index) { return self[index]; }) ; + py::implicitly_convertible>(); + py::class_> (m, "Point3d") .def(py::init()) + .def(py::init([](py::tuple p) + { + return Point<3> { p[0].cast(), p[1].cast(), + p[2].cast() }; + })) .def ("__str__", &ToString>) .def(py::self-py::self) .def(py::self+Vec<3>()) @@ -122,6 +191,8 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) .def("__getitem__", [](Point<2>& self, int index) { return self[index]; }) ; + py::implicitly_convertible>(); + m.def("Pnt", [](double x, double y, double z) { return global_trafo(Point<3>(x,y,z)); }); m.def("Pnt", [](double x, double y) { return Point<2>(x,y); }); @@ -140,6 +211,10 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) py::class_> (m, "Vec2d") .def(py::init()) + .def(py::init( [] (std::pair xy) + { + return Vec<2>{xy.first, xy.second}; + })) .def ("__str__", &ToString>) .def(py::self==py::self) .def(py::self+py::self) @@ -152,8 +227,15 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) .def("__len__", [](Vec<2>& /*unused*/) { return 2; }) ; + py::implicitly_convertible>(); + py::class_> (m, "Vec3d") .def(py::init()) + .def(py::init([](py::tuple v) + { + return Vec<3> { v[0].cast(), v[1].cast(), + v[2].cast() }; + })) .def ("__str__", &ToString>) .def(py::self==py::self) .def(py::self+py::self) @@ -166,6 +248,8 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) .def("__len__", [](Vec<3>& /*unused*/) { return 3; }) ; + py::implicitly_convertible>(); + m.def ("Vec", FunctionPointer ([] (double x, double y, double z) { return global_trafo(Vec<3>(x,y,z)); })); m.def("Vec", [](py::array_t np_array) @@ -519,7 +603,7 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) ExportArray(m); ExportArray(m); - ExportArray(m); + ExportArray(m); ExportArray(m); ExportArray(m); ExportArray(m); @@ -739,7 +823,7 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) py::return_value_policy::reference) .def("Elements1D", - static_cast&(Mesh::*)()> (&Mesh::LineSegments), + static_cast&(Mesh::*)()> (&Mesh::LineSegments), py::return_value_policy::reference) .def("Elements0D", FunctionPointer([] (Mesh & self) -> Array& @@ -755,6 +839,7 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) .def("FaceDescriptor", static_cast (&Mesh::GetFaceDescriptor), py::return_value_policy::reference) .def("GetNFaceDescriptors", &Mesh::GetNFD) + .def("GetNDomains", &Mesh::GetNDomains) .def("GetVolumeNeighboursOfSurfaceElement", [](Mesh & self, size_t sel) { @@ -855,6 +940,12 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) py::arg("pid2"), py::arg("identnr"), py::arg("type")) + .def("IdentifyPeriodicBoundaries", &Mesh::IdentifyPeriodicBoundaries, + py::arg("face1"), py::arg("face2"), py::arg("mapping"), py::arg("point_tolerance") = -1.) + .def("GetNrIdentifications", [](Mesh& self) + { + return self.GetIdentifications().GetMaxNr(); + }) .def ("CalcLocalH", &Mesh::CalcLocalH) .def ("SetMaxHDomain", [] (Mesh& self, py::list maxhlist) { @@ -879,18 +970,21 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) meshingparameter_description.c_str(), py::call_guard()) - .def ("OptimizeVolumeMesh", [](Mesh & self) + .def ("OptimizeVolumeMesh", [](Mesh & self, MeshingParameters* pars) { MeshingParameters mp; - mp.optsteps3d = 5; + if(pars) mp = *pars; + else mp.optsteps3d = 5; OptimizeVolume (mp, self); - },py::call_guard()) + }, py::arg("mp"), py::call_guard()) .def ("OptimizeMesh2d", [](Mesh & self) { self.CalcLocalH(0.5); MeshingParameters mp; mp.optsteps2d = 5; + if(!self.GetGeometry()) + throw Exception("Cannot optimize surface mesh without geometry!"); Optimize2d (self, mp); },py::call_guard()) @@ -923,77 +1017,139 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) .def ("BuildSearchTree", &Mesh::BuildElementSearchTree,py::call_guard()) - .def ("BoundaryLayer", FunctionPointer - ([](Mesh & self, int bc, py::list thicknesses, int volnr, py::list materials) + .def ("BoundaryLayer", [](Mesh & self, variant boundary, + variant thickness, + string material, + variant domain, bool outside, + optional project_boundaries) { - int n = py::len(thicknesses); BoundaryLayerParameters blp; - - for (int i = 1; i <= self.GetNFD(); i++) - if (self.GetFaceDescriptor(i).BCProperty() == bc) - blp.surfid.Append (i); - - cout << "add layer at surfaces: " << blp.surfid << endl; - - blp.prismlayers = n; - blp.growthfactor = 1.0; - - // find max domain nr - int maxind = 0; - for (ElementIndex ei = 0; ei < self.GetNE(); ei++) - maxind = max (maxind, self[ei].GetIndex()); - cout << "maxind = " << maxind << endl; - for ( int i=0; i(&boundary); bc) { - blp.heights.Append( py::extract(thicknesses[i])()) ; - blp.new_matnrs.Append( maxind+1+i ); - self.SetMaterial (maxind+1+i, py::extract(materials[i])().c_str()); + for (int i = 1; i <= self.GetNFD(); i++) + if(self.GetFaceDescriptor(i).BCProperty() == *bc) + blp.surfid.Append (i); } - blp.bulk_matnr = volnr; + else + { + regex pattern(*get_if(&boundary)); + for(int i = 1; i<=self.GetNFD(); i++) + { + auto& fd = self.GetFaceDescriptor(i); + if(regex_match(fd.GetBCName(), pattern)) + { + auto dom_pattern = get_if(&domain); + // only add if adjacent to domain + if(dom_pattern) + { + regex pattern(*dom_pattern); + if((fd.DomainIn() > 0 && regex_match(self.GetMaterial(fd.DomainIn()), pattern)) || (fd.DomainOut() > 0 && regex_match(self.GetMaterial(fd.DomainOut()), pattern))) + blp.surfid.Append(i); + } + else + blp.surfid.Append(i); + } + } + } + blp.new_mat = material; + + if(project_boundaries.has_value()) + { + regex pattern(*project_boundaries); + for(int i = 1; i<=self.GetNFD(); i++) + if(regex_match(self.GetFaceDescriptor(i).GetBCName(), pattern)) + blp.project_boundaries.Append(i); + } + + if(double* pthickness = get_if(&thickness); pthickness) + { + blp.heights.Append(*pthickness); + } + else + { + auto thicknesses = *get_if(&thickness); + for(auto val : thicknesses) + blp.heights.Append(val.cast()); + } + + int nr_domains = self.GetNDomains(); + blp.domains.SetSize(nr_domains + 1); // one based + blp.domains.Clear(); + if(string* pdomain = get_if(&domain); pdomain) + { + regex pattern(*pdomain); + for(auto i : Range(1, nr_domains+1)) + if(regex_match(self.GetMaterial(i), pattern)) + blp.domains.SetBit(i); + } + else + { + auto idomain = *get_if(&domain); + blp.domains.SetBit(idomain); + } + + blp.outside = outside; + blp.grow_edges = true; + GenerateBoundaryLayer (self, blp); - } - )) + self.UpdateTopology(); + }, py::arg("boundary"), py::arg("thickness"), py::arg("material"), + py::arg("domains") = ".*", py::arg("outside") = false, + py::arg("project_boundaries")=nullopt, + R"delimiter( +Add boundary layer to mesh. - .def ("BoundaryLayer", FunctionPointer - ([](Mesh & self, int bc, double thickness, int volnr, string material) - { - BoundaryLayerParameters blp; +Parameters +---------- - for (int i = 1; i <= self.GetNFD(); i++) - if (self.GetFaceDescriptor(i).BCProperty() == bc) - blp.surfid.Append (i); +boundary : string or int + Boundary name or number. - cout << "add layer at surfaces: " << blp.surfid << endl; +thickness : float or List[float] + Thickness of boundary layer(s). - blp.prismlayers = 1; - blp.hfirst = thickness; - blp.growthfactor = 1.0; +material : str or List[str] + Material name of boundary layer(s). - // find max domain nr - int maxind = 0; - for (ElementIndex ei = 0; ei < self.GetNE(); ei++) - maxind = max (maxind, self[ei].GetIndex()); - cout << "maxind = " << maxind << endl; - self.SetMaterial (maxind+1, material.c_str()); - blp.new_matnr = maxind+1; - blp.bulk_matnr = volnr; - GenerateBoundaryLayer (self, blp); - } - )) +domain : str or int + Regexp for domain boundarylayer is going into. + +outside : bool = False + If true add the layer on the outside + +grow_edges : bool = False + Grow boundary layer over edges. + +project_boundaries : Optional[str] = None + Project boundarylayer to these boundaries if they meet them. Set + to boundaries that meet boundarylayer at a non-orthogonal edge and + layer-ending should be projected to that boundary. + +)delimiter") .def ("EnableTable", [] (Mesh & self, string name, bool set) { + const_cast(self.GetTopology()).EnableTable(name, set); + /* if (name == "edges") const_cast(self.GetTopology()).SetBuildEdges(set); - if (name == "faces") + else if (name == "faces") const_cast(self.GetTopology()).SetBuildFaces(set); + else if (name == "parentedges") + const_cast(self.GetTopology()).SetBuildParentEdges(set); + else if (name == "parentfaces") + const_cast(self.GetTopology()).SetBuildParentFaces(set); + else + throw Exception ("noting known about table "+name +"\n" + "knwon are 'edges', 'faces', 'parentedges', 'parentfaces'"); + */ }, py::arg("name"), py::arg("set")=true) .def ("Scale", [](Mesh & self, double factor) { - for(auto i = 0; i) - .def("RestrictH", FunctionPointer - ([](MP & mp, double x, double y, double z, double h) + .def("RestrictH", [](MP & mp, double x, double y, double z, double h) { - mp.meshsize_points.Append ( MeshingParameters::MeshSizePoint (Point<3> (x,y,z), h)); - }), - py::arg("x"), py::arg("y"), py::arg("z"), py::arg("h") + mp.meshsize_points.Append ( MeshingParameters::MeshSizePoint(Point<3> (x,y,z), h)); + }, py::arg("x"), py::arg("y"), py::arg("z"), py::arg("h") ) + .def("RestrictH", [](MP & mp, const Point<3>& p, double h) + { + mp.meshsize_points.Append ({p, h}); + }, py::arg("p"), py::arg("h")) + .def("RestrictHLine", [](MP& mp, const Point<3>& p1, const Point<3>& p2, + double maxh) + { + int steps = int(Dist(p1, p2) / maxh) + 2; + auto v = p2 - p1; + for (int i = 0; i <= steps; i++) + { + mp.meshsize_points.Append({p1 + double(i)/steps * v, maxh}); + } + }, py::arg("p1"), py::arg("p2"), py::arg("maxh")) ; m.def("SetTestoutFile", FunctionPointer ([] (const string & filename) @@ -1069,6 +1242,76 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) })); + m.def("ReadCGNSFile", &ReadCGNSFile, py::arg("filename"), py::arg("base")=1, "Read mesh and solution vectors from CGNS file"); + m.def("WriteCGNSFile", &WriteCGNSFile, py::arg("mesh"), py::arg("filename"), py::arg("names"), py::arg("values"), py::arg("locations"), + R"(Write mesh and solution vectors to CGNS file, possible values for locations: + Vertex = 0 + EdgeCenter = 1 + FaceCenter = 2 + CellCenter = 3 + )"); + + py::class_> (m, "SurfaceGeometry") + .def(py::init<>()) + .def(py::init([](py::object pyfunc) + { + std::function (Point<2>)> func = [pyfunc](Point<2> p) + { + py::gil_scoped_acquire aq; + py::tuple pyres = py::extract(pyfunc(p[0],p[1],0.0)) (); + return Vec<3>(py::extract(pyres[0])(),py::extract(pyres[1])(),py::extract(pyres[2])()); + }; + auto geo = make_shared(func); + return geo; + }), py::arg("mapping")) + .def(NGSPickle()) + .def("GenerateMesh", [](shared_ptr geo, + bool quads, int nx, int ny, bool flip_triangles, py::list py_bbbpts, py::list py_bbbnames, py::list py_hppts, py::list py_hpbnd) + { + if (py::len(py_bbbpts) != py::len(py_bbbnames)) + throw Exception("In SurfaceGeometry::GenerateMesh bbbpts and bbbnames do not have same lengths."); + Array> bbbpts(py::len(py_bbbpts)); + Array bbbname(py::len(py_bbbpts)); + Array> hppts(py::len(py_hppts)); + Array hpptsfac(py::len(py_hppts)); + Array hpbnd(py::len(py_hpbnd)); + Array hpbndfac(py::len(py_hpbnd)); + for(int i = 0; i(py_bbbpts[i])(); + bbbpts[i] = Point<3>(py::extract(pnt[0])(),py::extract(pnt[1])(),py::extract(pnt[2])()); + bbbname[i] = py::extract(py_bbbnames[i])(); + } + for(int i = 0; i(py_hppts[i])(); + hppts[i] = Point<3>(py::extract(pnt[0])(),py::extract(pnt[1])(),py::extract(pnt[2])()); + //hpptsfac[i] = py::len(pnt) > 3 ? py::extract(pnt[3])() : 0.0; + hpptsfac[i] = py::extract(pnt[3])(); + } + + for(int i = 0; i(py_hpbnd[i])(); + hpbnd[i] = py::extract(bnd[0])(); + hpbndfac[i] = py::extract(bnd[1])(); + } + auto mesh = make_shared(); + SetGlobalMesh (mesh); + mesh->SetGeometry(geo); + ng_geometry = geo; + auto result = geo->GenerateMesh (mesh, quads, nx, ny, flip_triangles, bbbpts, bbbname, hppts, hpptsfac, hpbnd, hpbndfac); + if(result != 0) + throw Exception("SurfaceGeometry: Meshing failed!"); + return mesh; + }, py::arg("quads")=true, py::arg("nx")=10, py::arg("ny")=10, py::arg("flip_triangles")=false, py::arg("bbbpts")=py::list(), py::arg("bbbnames")=py::list(), py::arg("hppts")=py::list(), py::arg("hpbnd")=py::list()) + ; + ; + + py::class_ (m, "ClearSolutionClass") + .def(py::init<>()) + ; + m.def("SetParallelPickling", [](bool par) { parallel_pickling = par; }); } PYBIND11_MODULE(libmesh, m) { diff --git a/libsrc/meshing/python_mesh.hpp b/libsrc/meshing/python_mesh.hpp index 6deab5d2..110c26ee 100644 --- a/libsrc/meshing/python_mesh.hpp +++ b/libsrc/meshing/python_mesh.hpp @@ -48,6 +48,9 @@ filldist: float = 0.1 delaunay: bool = True Use delaunay meshing. +delaunay2d : bool = True + Use delaunay meshing for 2d geometries. + Optimization Parameters ----------------------- @@ -109,6 +112,8 @@ inline void CreateMPfromKwargs(MeshingParameters& mp, py::kwargs kwargs, bool th mp.grading = py::cast(kwargs.attr("pop")("grading")); if(kwargs.contains("delaunay")) mp.delaunay = py::cast(kwargs.attr("pop")("delaunay")); + if(kwargs.contains("delaunay2d")) + mp.delaunay2d = py::cast(kwargs.attr("pop")("delaunay2d")); if(kwargs.contains("maxh")) mp.maxh = py::cast(kwargs.attr("pop")("maxh")); if(kwargs.contains("minh")) diff --git a/libsrc/meshing/refine.cpp b/libsrc/meshing/refine.cpp index 228ebf2b..d8d49870 100644 --- a/libsrc/meshing/refine.cpp +++ b/libsrc/meshing/refine.cpp @@ -13,8 +13,11 @@ namespace netgen void Refinement :: Refine (Mesh & mesh) { - PrintMessage (3, "Refine mesh"); + if (mesh.GetCommunicator().Rank()==0) + PrintMessage (3, "Refine mesh"); + Timer t("Refine mesh"); RegionTimer reg(t); + mesh.SetNextMajorTimeStamp(); if (ntasks > 1 && id == 0) @@ -31,6 +34,9 @@ namespace netgen mesh.mlbetweennodes = INDEX_2(PointIndex::BASE-1,PointIndex::BASE-1); } + if (mesh.level_nv.Size() == 0) + mesh.level_nv.Append (mesh.GetNV()); + INDEX_2_HASHTABLE between(mesh.GetNP() + 5); @@ -71,6 +77,32 @@ namespace netgen } break; } + case QUAD: + { + static int betw[5][3] = + { { 0, 1, 4 }, + { 1, 2, 5 }, + { 2, 3, 6 }, + { 0, 3, 7 }, + { 0, 2, 8 } }; // one diagonal of the quad. should change later to mid-point of edge mid-points + for (int j = 0; j < 5; j++) + { + auto i2 = PointIndices<2>::Sort(el[betw[j][0]],el[betw[j][1]]); + if (j == 4) + { + auto i2a = PointIndices<2>::Sort(el[0], el[2]); + auto i2b = PointIndices<2>::Sort(el[1], el[3]); + i2 = i2a[0] < i2b[0] ? i2a : i2b; + } + if (!between.Used(i2)) + { + between.Set (i2, 0); + parents.Append(i2); + } + } + break; + } + default: throw NgException ("currently refinement for quad-elements is not supported"); } @@ -119,7 +151,7 @@ namespace netgen between.Set (parents[i], mesh.GetNV()+i+PointIndex::BASE); mesh.mlbetweennodes[mesh.GetNV()+i+PointIndex::BASE] = parents[i]; } - + mesh.SetNP(mesh.GetNV() + parents.Size()); NgArray pointset(mesh.GetNP()); pointset = false; @@ -275,70 +307,76 @@ namespace netgen case QUAD6: case QUAD8: { - NgArrayMem pnums(9); - NgArrayMem pgis(9); + PointIndex pnums[9]; + PointGeomInfo pgis[9]; static int betw[5][3] = - { { 1, 2, 5 }, + { { 0, 1, 4 }, + { 1, 2, 5 }, { 2, 3, 6 }, - { 3, 4, 7 }, - { 1, 4, 8 }, - { 5, 7, 9 } }; - - for (int j = 1; j <= 4; j++) + { 0, 3, 7 }, + { 0, 2, 8 } }; + + for (int j = 0; j < 4; j++) { - pnums.Elem(j) = el.PNum(j); - pgis.Elem(j) = el.GeomInfoPi(j); + pnums[j] = el[j]; + pgis[j] = el.GeomInfoPi(j+1); } for (int j = 0; j < 5; j++) { - int pi1 = pnums.Elem(betw[j][0]); - int pi2 = pnums.Elem(betw[j][1]); + int pi1 = pnums[betw[j][0]]; + int pi2 = pnums[betw[j][1]]; INDEX_2 i2 (pi1, pi2); i2.Sort(); + + if (j == 4) + { + auto i2a = PointIndices<2>::Sort(el[0], el[2]); + auto i2b = PointIndices<2>::Sort(el[1], el[3]); + i2 = i2a[0] < i2b[0] ? i2a : i2b; + } - if (between.Used(i2)) - { - pnums.Elem(5+j) = between.Get(i2); - pgis.Elem(5+j) = surfgi.Get(pnums.Elem(4+j)); - } - else - { - Point<3> pb; - geo.PointBetween(mesh.Point (pi1), - mesh.Point (pi2), 0.5, - mesh.GetFaceDescriptor(el.GetIndex ()).SurfNr(), - el.GeomInfoPi (betw[j][0]), - el.GeomInfoPi (betw[j][1]), - pb, pgis.Elem(5+j)); + Point<3> pb; + PointGeomInfo pgi; + geo.PointBetween(mesh.Point (pi1), mesh.Point (pi2), 0.5, + mesh.GetFaceDescriptor(el.GetIndex ()).SurfNr(), + el.GeomInfoPi (betw[j][0]+1 ), + el.GeomInfoPi (betw[j][1]+1 ), + pb, pgi); - pnums.Elem(5+j) = mesh.AddPoint (pb); + pgis[4+j] = pgi; + PointIndex pinew = between.Get(i2); + pnums[4+j] = pinew; - between.Set (i2, pnums.Get(5+j)); - - if (surfgi.Size() < pnums.Elem(5+j)) - surfgi.SetSize (pnums.Elem(5+j)); - surfgi.Elem(pnums.Elem(5+j)) = pgis.Elem(5+j); - } - } + if (!pointset[pinew]) + { + pointset[pinew] = true; + mesh.Point(pinew) = pb; + } + + if (surfgi.Size() < pnums[4+j]) + surfgi.SetSize (pnums[4+j]); + surfgi.Elem(pnums[4+j]) = pgis[4+j]; + } static int reftab[4][4] = { - { 1, 5, 9, 8 }, - { 5, 2, 6, 9 }, - { 8, 9, 7, 4 }, - { 9, 6, 3, 7 } }; + { 0, 4, 8, 7 }, + { 4, 1, 5, 8 }, + { 7, 8, 6, 3 }, + { 8, 5, 2, 6 } }; + int ind = el.GetIndex(); for (int j = 0; j < 4; j++) { Element2d nel(QUAD); - for (int k = 1; k <= 4; k++) + for (int k = 0; k < 4; k++) { - nel.PNum(k) = pnums.Get(reftab[j][k-1]); - nel.GeomInfoPi(k) = pgis.Get(reftab[j][k-1]); + nel[k] = pnums[reftab[j][k]]; + nel.GeomInfoPi(k+1) = pgis[reftab[j][k]]; } nel.SetIndex(ind); @@ -735,6 +773,18 @@ namespace netgen PrintMessage (5, "have 3d elements"); mesh.ComputeNVertices(); mesh.RebuildSurfaceElementLists(); + + mesh.level_nv.Append (mesh.GetNV()); + +#ifdef PARALLEL + if (mesh.GetCommunicator().Size() > 1) + { + mesh.GetParallelTopology().IdentifyVerticesAfterRefinement(); + mesh.GetCommunicator().Barrier(); + mesh.GetParallelTopology().EnumeratePointsGlobally(); + } +#endif + PrintMessage (5, "mesh updates complete"); return; diff --git a/libsrc/meshing/smoothing3.cpp b/libsrc/meshing/smoothing3.cpp index e84a18ba..0f8652f6 100644 --- a/libsrc/meshing/smoothing3.cpp +++ b/libsrc/meshing/smoothing3.cpp @@ -1460,6 +1460,7 @@ void Mesh :: ImproveMesh (const MeshingParameters & mp, OPTIMIZEGOAL goal) static Timer trange("range"); // return ImproveMeshSequential(mp, goal); + BuildBoundaryEdges(false); (*testout) << "Improve Mesh" << "\n"; PrintMessage (3, "ImproveMesh"); diff --git a/libsrc/meshing/surfacegeom.cpp b/libsrc/meshing/surfacegeom.cpp new file mode 100644 index 00000000..14a265d3 --- /dev/null +++ b/libsrc/meshing/surfacegeom.cpp @@ -0,0 +1,490 @@ +/* *************************************************************************/ +/* File: surfacegeom.cpp */ +/* Author: Michael Neunteufel */ +/* Date: Jun. 2020 */ +/* *************************************************************************/ + +#include + +namespace netgen +{ + SurfaceGeometry :: SurfaceGeometry() + { + //identity + func = [](Point<2> p) { return Vec<3>(p[0],p[1],0.0); }; + } + + SurfaceGeometry :: SurfaceGeometry(function(Point<2>)> _func) : func(_func) + { + ; + } + + SurfaceGeometry :: SurfaceGeometry(const SurfaceGeometry& geom) : func(geom.func), eps(geom.eps) + { + ; + } + + void SurfaceGeometry :: CalcHesse(double u, double v, Vec<3>& f_uu, Vec<3>& f_vv, Vec<3>& f_uv) const + { + Point<2> p = Point<2>(u,v); + double pr = p[0]+eps; + double pl = p[0]-eps; + double prr = p[0]+2*eps; + double pll = p[0]-2*eps; + + auto dr = GetTangentVectors( pr, v ); + auto dl = GetTangentVectors( pl, v ); + auto drr = GetTangentVectors( prr, v ); + auto dll = GetTangentVectors( pll, v ); + + f_uu = (1.0/(12.0*eps)) * (8.0*dr[0]-8.0*dl[0]-drr[0]+dll[0]); + f_uv = (1.0/(12.0*eps)) * (8.0*dr[1]-8.0*dl[1]-drr[1]+dll[1]); + + pr = p[1]+eps; + pl = p[1]-eps; + prr = p[1]+2*eps; + pll = p[1]-2*eps; + + GetTangentVectors(u, pr, dr); + GetTangentVectors(u, pl, dl); + GetTangentVectors(u, prr, drr); + GetTangentVectors(u, pll, dll); + + f_vv = (1.0/(12.0*eps)) * (8.0*dr[1]-8.0*dl[1]-drr[1]+dll[1]); + } + + Array> SurfaceGeometry :: GetTangentVectors(double u, double v) const + { + Array> tang(2); + + Point<2> pru = Point<2>(u+eps,v); + Point<2> plu = Point<2>(u-eps,v); + Point<2> prru = Point<2>(u+2*eps,v); + Point<2> pllu = Point<2>(u-2*eps,v); + + Point<2> prv = Point<2>(u,v+eps); + Point<2> plv = Point<2>(u,v-eps); + Point<2> prrv = Point<2>(u,v+2*eps); + Point<2> pllv = Point<2>(u,v-2*eps); + + + tang[0] = 1/(12.0*eps)*( 8.0*func(pru) - 8.0*func(plu) - func(prru) + func(pllu) ); + tang[1] = 1/(12.0*eps)*( 8.0*func(prv) - 8.0*func(plv) - func(prrv) + func(pllv) ); + + return tang; + } + + void SurfaceGeometry :: GetTangentVectors(double u, double v, Array>& tang) const + { + + Point<2> pru = Point<2>(u+eps,v); + Point<2> plu = Point<2>(u-eps,v); + Point<2> prru = Point<2>(u+2*eps,v); + Point<2> pllu = Point<2>(u-2*eps,v); + + Point<2> prv = Point<2>(u,v+eps); + Point<2> plv = Point<2>(u,v-eps); + Point<2> prrv = Point<2>(u,v+2*eps); + Point<2> pllv = Point<2>(u,v-2*eps); + + + tang[0] = 1/(12.0*eps)*( 8.0*func(pru) - 8.0*func(plu) - func(prru) + func(pllu) ); + tang[1] = 1/(12.0*eps)*( 8.0*func(prv) - 8.0*func(plv) - func(prrv) + func(pllv) ); + } + + + Vec<3> SurfaceGeometry :: GetNormal(int surfind, const Point<3> & p, const PointGeomInfo* gi) const + { + Array> tang = GetTangentVectors(gi->u, gi->v); + auto normal = Cross(tang[0], tang[1]); + normal.Normalize(); + return normal; + } + + + PointGeomInfo SurfaceGeometry :: ProjectPoint(int surfind, Point<3> & p) const + { + throw Exception("In SurfaceGeometry::ProjectPoint"); + } + + void SurfaceGeometry :: ProjectPointEdge (int surfind, int surfind2, Point<3> & p, + EdgePointGeomInfo* gi) const + { + if (gi == nullptr) + throw Exception("In SurfaceGeometry::ProjectPointEdge: gi is nullptr"); + throw Exception("In SurfaceGeometry::ProjectPointEdge: not implemented"); + } + + bool SurfaceGeometry :: ProjectPointGI (int surfind, Point<3> & p, PointGeomInfo & gi) const + { + Array> tangs(2); + Vec<3> diff, f_uu, f_vv, f_uv; + Vec<2> r, dx; + double norm_r, det, energy=0.0, new_energy=0.0, alpha=2.0,u=0.0,v=0.0,maxerr=1e-16; + Mat<2,2> mat, inv; + int num=0, maxit=25; + double damping=0.5; + + //Solve minimization problem + // argmin_(u,v) 0.5*\| f(u,v)-p\|^2 + //via Neton's method: + // F(u,v) = ( (f(u,v)-p)*f_u(u,v), (f(u,v)-p)*f_v(u,v))^T = (0,0)^T + //Stiffness matrix + // F'(u,v) = ( f_u*f_u + (f-p)*f_uu, f_v*f_u + (f-p)*f_uv, f_v*f_u + (f-p)*f_uv, f_v*f_v + (f-p)*f_vv ) + do + { + num++; + GetTangentVectors(gi.u, gi.v,tangs); + diff = func(Point<2>(gi.u, gi.v)) - Vec<3>(p); + energy = diff.Length2(); + r = Vec<2>( diff*tangs[0], diff*tangs[1] ); + norm_r = r.Length2(); + + CalcHesse(gi.u, gi.v, f_uu, f_vv, f_uv); + + + mat(0,0) = tangs[0]*tangs[0] + diff*f_uu; + mat(1,0) = mat(0,1) = tangs[0]*tangs[1]+diff*f_uv; + mat(1,1) = tangs[1]*tangs[1]+diff*f_vv; + + CalcInverse(mat,inv); + + dx = inv*r; + + //Linesearch + alpha = 2.0; + do + { + alpha /= 2.0; + u = gi.u - min(1.0,alpha*damping*num)*dx[0]; + v = gi.v - min(1.0,alpha*damping*num)*dx[1]; + + diff = func(Point<2>(u, v)) - Vec<3>(p); + new_energy = diff.Length2(); + } + while (alpha > 1e-10 && new_energy > energy+1e-14); + if (alpha <= 1e-10) + throw Exception("In SurfaceGeometry::ProjectPointGI: Linesearch min alpha reached!"); + + gi.u = u; + gi.v = v; + + + } + while ( norm_r > maxerr && num < maxit); + + //Stay in reference domain [0,1]^2 + if (gi.u < 0 || gi.u > 1 || gi.v < 0 || gi.v > 1) + { + cout << "Warning: Projected point outside [0,1]^2: u=" << gi.u << ",v=" << gi.v <<". Setting back." << endl; + gi.u = min(max(gi.u,0.0),1.0); + gi.v = min(max(gi.v,0.0),1.0); + } + + p = Point<3>(func(Point<2>(gi.u,gi.v))); + + if (num == maxit) + { + //cout << "In SurfaceGeometry::ProjectPointGI: Newton did not converge" << endl; + throw Exception("In SurfaceGeometry::ProjectPointGI: Newton did not converge"); + } + return true; + } + + bool SurfaceGeometry :: CalcPointGeomInfo(int surfind, PointGeomInfo& gi, const Point<3> & p3) const + { + throw Exception("In SurfaceGeometry::CalcPointGeomInfo: not implemented"); + return false; + } + + void SurfaceGeometry :: PointBetweenEdge(const Point<3> & p1, const Point<3> & p2, double secpoint, int surfi1, int surfi2, const EdgePointGeomInfo & ap1, const EdgePointGeomInfo & ap2, Point<3> & newp, EdgePointGeomInfo & newgi) const + { + newgi.u = ap1.u+secpoint*(ap2.u-ap1.u); + newgi.v = ap1.v+secpoint*(ap2.v-ap1.v); + newgi.edgenr = ap1.edgenr; + newgi.body = -1; + newgi.dist = -1.0; + + newp = Point<3>(func(Point<2>(newgi.u, newgi.v))); + } + + void SurfaceGeometry :: PointBetween(const Point<3> & p1, const Point<3> & p2, double secpoint, + int surfi, + const PointGeomInfo & gi1, + const PointGeomInfo & gi2, + Point<3> & newp, PointGeomInfo & newgi) const + { + newgi.u = gi1.u+secpoint*(gi2.u-gi1.u); + newgi.v = gi1.v+secpoint*(gi2.v-gi1.v); + newgi.trignum = -1; + + newp = Point<3>(func(Point<2>(newgi.u, newgi.v))); + //newp = p1+secpoint*(p2-p1); + //ProjectPointGI(surfi, newp, newgi); + } + + int SurfaceGeometry :: GenerateMesh(shared_ptr & mesh, bool quads, int nx, int ny, bool flip_triangles, const Array>& bbbpts, const Array& bbbnames, const Array>& hppoints, const Array& hpptsfac, const Array& hpbnd, const Array& hpbndfac) + { + mesh->SetDimension(3); + + Array found(bbbpts.Size()); + found = false; + Array indbbbpts(bbbpts.Size()); + + + Array pids; + Array pgis; + for(int i=0; i <= ny; i++) + for(int j=0; j <= nx; j++) + { + PointGeomInfo pgi; + pgi.trignum = -1; + pgi.u = double(j)/nx; + pgi.v = double(i)/ny; + + Point<3> pnt = Point<3>(func(Point<2>(pgi.u,pgi.v))); + pids.Append(mesh->AddPoint(pnt)); + pgis.Append(pgi); + + for (int k = 0; k < bbbpts.Size(); k++) + { + auto diff = pnt - bbbpts[k]; + if(diff.Length2() < 1e-14) + { + found[k] = true; + indbbbpts[k] = pids[pids.Size()-1]; + } + } + + for (int k = 0; k < hppoints.Size(); k++) + { + auto diff = pnt - hppoints[k]; + if(diff.Length2() < 1e-14) + { + (*mesh)[pids[pids.Size()-1]].Singularity(hpptsfac[k]); + } + + } + } + + for (bool f : found) + if (!f) + throw Exception("In SurfaceGeometry :: GenerateMesh: bbbpts not resolved in mesh."); + + FaceDescriptor fd; + fd.SetSurfNr(1); + fd.SetDomainIn(1); + fd.SetDomainOut(0); + fd.SetBCProperty(1); + mesh->AddFaceDescriptor(fd); + + + for(int i=0; i < ny; i++) + { + for(int j=0; j < nx; j++) + { + int base = i * (nx+1) + j; + if (quads) + { + int pnum[4] = {base,base+1,base+nx+2,base+nx+1}; + Element2d el = Element2d(QUAD); + for (int i = 0; i < 4; i++) + { + el[i] = pids[pnum[i]]; + el.GeomInfoPi(i+1) = pgis[pnum[i]]; + } + el.SetIndex(1); + + mesh->AddSurfaceElement(el); + } + else + { + Array pnum1(3); + Array pnum2(3); + if (flip_triangles) + { + pnum1[0] = base; + pnum1[1] = base+1; + pnum1[2] = base+nx+2; + pnum2[0] = base; + pnum2[1] = base+nx+2; + pnum2[2] = base+nx+1; + } + else + { + pnum1[0] = base; + pnum1[1] = base+1; + pnum1[2] = base+nx+1; + pnum2[0] = base+1; + pnum2[1] = base+nx+2; + pnum2[2] = base+nx+1; + } + + Element2d el = Element2d(TRIG); + for (int i = 0; i < 3; i++) + { + el[i] = pids[pnum1[i]]; + el.GeomInfoPi(i+1) = pgis[pnum1[i]]; + } + el.SetIndex(1); + + mesh->AddSurfaceElement(el); + for (int i = 0; i < 3; i++) + { + el[i] = pids[pnum2[i]]; + el.GeomInfoPi(i+1) = pgis[pnum2[i]]; + } + mesh->AddSurfaceElement(el); + } + } + } + + Segment seg; + seg.si = 1; + seg.edgenr = 1; + seg.epgeominfo[0].edgenr = 0; + seg.epgeominfo[1].edgenr = 0; + //for hp refinement + seg.singedge_left = 0; + seg.singedge_right = 0; + for (size_t i=0; i < hpbnd.Size(); i++) + { + if (hpbnd[i] == "bottom") + { + seg.singedge_left = hpbndfac[i]; + seg.singedge_right = hpbndfac[i]; + } + } + // needed for codim2 in 3d + seg.edgenr = 1; + for(int i=0; i < nx; i++) + { + seg[0] = pids[i]; + seg[1] = pids[i+1]; + + seg.geominfo[0] = pgis[i]; + seg.geominfo[1] = pgis[i+1]; + seg.epgeominfo[0].u = pgis[i].u; + seg.epgeominfo[0].v = pgis[i].v; + seg.epgeominfo[0].edgenr = seg.edgenr; + seg.epgeominfo[1].u = pgis[i+1].u; + seg.epgeominfo[1].v = pgis[i+1].v; + seg.epgeominfo[1].edgenr = seg.edgenr; + + mesh->AddSegment(seg); + } + + seg.si = 2; + seg.edgenr = 2; + seg.singedge_left = 0; + seg.singedge_right = 0; + + for (size_t i=0; i < hpbnd.Size(); i++) + { + if (hpbnd[i] == "right") + { + seg.singedge_left = hpbndfac[i]; + seg.singedge_right = hpbndfac[i]; + } + } + + for(int i=0; iAddSegment(seg); + } + + seg.si = 3; + seg.edgenr = 3; + seg.singedge_left = 0; + seg.singedge_right = 0; + + for (size_t i=0; i < hpbnd.Size(); i++) + { + if (hpbnd[i] == "top") + { + seg.singedge_left = hpbndfac[i]; + seg.singedge_right = hpbndfac[i]; + } + } + + for(int i=0; iAddSegment(seg); + } + + seg.si = 4; + seg.edgenr = 4; + seg.singedge_left = 0; + seg.singedge_right = 0; + for (size_t i=0; i < hpbnd.Size(); i++) + { + if (hpbnd[i] == "left") + { + seg.singedge_left = hpbndfac[i]; + seg.singedge_right = hpbndfac[i]; + } + } + + + for(int i=0; iAddSegment(seg); + } + + mesh->SetCD2Name(1, "bottom"); + mesh->SetCD2Name(2, "right"); + mesh->SetCD2Name(3, "top"); + mesh->SetCD2Name(4, "left"); + + for (int i = 0; i < bbbpts.Size(); i++) + { + Element0d el; + el.pnum = indbbbpts[i]; + el.index = i+1; + mesh->pointelements.Append(el); + mesh->SetCD3Name(i+1, bbbnames[i]); + } + + mesh->Compress(); + mesh->UpdateTopology(); + + return 0; + } + +}; diff --git a/libsrc/meshing/surfacegeom.hpp b/libsrc/meshing/surfacegeom.hpp new file mode 100644 index 00000000..e3fa734d --- /dev/null +++ b/libsrc/meshing/surfacegeom.hpp @@ -0,0 +1,73 @@ +#ifndef FILE_SURFACEGEOM +#define FILE_SURFACEGEOM + +/* *************************************************************************/ +/* File: surfacegeom.hpp */ +/* Author: Michael Neunteufel */ +/* Date: Jun. 2020 */ +/* *************************************************************************/ + + +#include + + +namespace netgen +{ + + class DLL_HEADER SurfaceGeometry : public NetgenGeometry + { + function(Point<2>)> func; + double eps=1e-4; + + private: + + void CalcHesse(double u, double v, Vec<3>& f_uu, Vec<3>& f_vv, Vec<3>& f_uv) const; + public: + + SurfaceGeometry(); + SurfaceGeometry(function(Point<2>)> func); + SurfaceGeometry(const SurfaceGeometry& geom); + SurfaceGeometry& operator =(const SurfaceGeometry& geom) + { + func = geom.func; + eps = geom.eps; + return *this; + } + + Array> GetTangentVectors(double u, double v) const; + + void GetTangentVectors(double u, double v, Array>& tang) const; + + + virtual Vec<3> GetNormal(int surfind, const Point<3> & p, const PointGeomInfo* gi) const override; + + virtual PointGeomInfo ProjectPoint(int surfind, Point<3> & p) const override; + + virtual void ProjectPointEdge (int surfind, int surfind2, Point<3> & p, + EdgePointGeomInfo* gi = nullptr) const override; + + virtual bool ProjectPointGI (int surfind, Point<3> & p, PointGeomInfo & gi) const override; + + virtual bool CalcPointGeomInfo(int surfind, PointGeomInfo& gi, const Point<3> & p3) const override; + + virtual void PointBetweenEdge(const Point<3> & p1, const Point<3> & p2, double secpoint, + int surfi1, int surfi2, + const EdgePointGeomInfo & ap1, + const EdgePointGeomInfo & ap2, + Point<3> & newp, EdgePointGeomInfo & newgi) const override; + + virtual void PointBetween(const Point<3> & p1, const Point<3> & p2, double secpoint, + int surfi, + const PointGeomInfo & gi1, + const PointGeomInfo & gi2, + Point<3> & newp, PointGeomInfo & newgi) const override; + + int GenerateMesh(shared_ptr & mesh, bool quads, int nx, int ny, bool flip_triangles, const Array>& bbbpts, const Array& bbbnames, const Array>& hppoints, const Array& hpptsfac, const Array& hpbnd, const Array& hpbndfac); + + }; + +} + + + +#endif //SURFACEGEOM diff --git a/libsrc/meshing/topology.cpp b/libsrc/meshing/topology.cpp index 6fd54b8b..1a3c462e 100644 --- a/libsrc/meshing/topology.cpp +++ b/libsrc/meshing/topology.cpp @@ -3,7 +3,8 @@ namespace netgen { - + using ngcore::ParallelForRange; + using ngcore::INT; template void QuickSortRec (NgFlatArray data, @@ -54,6 +55,24 @@ namespace netgen bool MeshTopology :: NeedsUpdate() const { return (timestamp <= mesh->GetTimeStamp()); } + + void MeshTopology :: EnableTable (string name, bool set) + { + if (name == "edges") + SetBuildEdges(set); + else if (name == "faces") + SetBuildFaces(set); + else if (name == "parentedges") + SetBuildParentEdges(set); + else if (name == "parentfaces") + SetBuildParentFaces(set); + else + throw Exception ("noting known about table "+name +"\n" + "knwon are 'edges', 'faces', 'parentedges', 'parentfaces'"); + } + + + template void LoopOverEdges (const Mesh & mesh, MeshTopology & top, PointIndex v, FUNC func) @@ -332,10 +351,10 @@ namespace netgen } - void MeshTopology :: Update (TaskManager tm, Tracer tracer) + void MeshTopology :: Update (NgTaskManager tm_unused, NgTracer tracer) { - static int timer = NgProfiler::CreateTimer ("topology"); - NgProfiler::RegionTimer reg (timer); + static Timer timer("Topology::Update"); + RegionTimer reg (timer); #ifdef PARALLEL // ParallelMeshTopology & paralleltop = mesh.GetParallelTopology(); @@ -382,10 +401,9 @@ namespace netgen } */ ParallelForRange - (tm, ne, - [&] (size_t begin, size_t end) + (ne, [&] (IntRange r) { - for (ElementIndex ei = begin; ei < end; ei++) + for (ElementIndex ei : r) { const Element & el = (*mesh)[ei]; for (int j = 0; j < el.GetNV(); j++) @@ -426,10 +444,10 @@ namespace netgen } */ ParallelForRange - (tm, nse, - [&] (size_t begin, size_t end) + (nse, + [&] (IntRange r) { - for (SurfaceElementIndex ei = begin; ei < end; ei++) + for (SurfaceElementIndex ei : r) { const Element2d & el = (*mesh)[ei]; for (int j = 0; j < el.GetNV(); j++) @@ -553,9 +571,11 @@ namespace netgen cnt = 0; ParallelForRange - (tm, mesh->GetNV(), // Points().Size(), - [&] (size_t begin, size_t end) + (mesh->GetNV(), // Points().Size(), + [&] (IntRange r) { + auto begin = r.First(); + auto end = r.Next(); INDEX_CLOSED_HASHTABLE v2eht(2*max_edge_on_vertex+10); for (PointIndex v = begin+PointIndex::BASE; v < end+PointIndex::BASE; v++) @@ -600,16 +620,19 @@ namespace netgen ned += hv; } edge2vert.SetSize(ned); - + edge2segment.SetSize(ned); + edge2segment = -1; // INDEX_CLOSED_HASHTABLE v2eht(2*max_edge_on_vertex+10); // NgArray vertex2; // for (PointIndex v = PointIndex::BASE; v < nv+PointIndex::BASE; v++) ParallelForRange - (tm, mesh->GetNV(), // Points().Size(), - [&] (size_t begin, size_t end) + (mesh->GetNV(), // Points().Size(), + [&] (IntRange r) { + auto begin = r.First(); + auto end = r.Next(); INDEX_CLOSED_HASHTABLE v2eht(2*max_edge_on_vertex+10); NgArray vertex2; for (PointIndex v = begin+PointIndex::BASE; @@ -667,16 +690,292 @@ namespace netgen break; case 1: segedges[elnr].nr = edgenum; + edge2segment[edgenum] = elnr; // segedges[elnr].orient = edgedir; break; } }); } } ); + + + if (build_parent_edges) + { + static Timer t("build_hierarchy"); RegionTimer reg(t); + cnt = 0; + for (auto verts : edge2vert) cnt[verts[0]]++; + TABLE vert2edge (cnt); + for (auto i : edge2vert.Range()) + vert2edge.AddSave (edge2vert[i][0], i); + + // build edge hierarchy: + parent_edges.SetSize (ned); + parent_edges = { -1, { -1, -1, -1 } }; + + for (size_t i = 0; i < ned; i++) + { + auto verts = edge2vert[i]; // 2 vertices of edge + + if (verts[0] >= mesh->mlbetweennodes.Size()+PointIndex::BASE || + verts[1] >= mesh->mlbetweennodes.Size()+PointIndex::BASE) + continue; + + auto pa0 = mesh->mlbetweennodes[verts[0]]; // two parent vertices of v0 + auto pa1 = mesh->mlbetweennodes[verts[1]]; // two parent vertices of v1 + + // both vertices are on coarsest mesh + if (!pa0[0].IsValid() && !pa1[0].IsValid()) + continue; + + int issplitedge = 0; + if (pa0[0] == verts[1] || pa0[1] == verts[1]) + issplitedge = 1; + if (pa1[0] == verts[0] || pa1[1] == verts[0]) + issplitedge = 2; + + if (issplitedge) + { + // cout << "split edge " << endl; + // edge is obtained by splitting one edge into two parts: + auto paedge = issplitedge == 1 ? pa0 : pa1; + + if (paedge[0] > paedge[1]) + Swap (paedge[0], paedge[1]); + + for (int ednr : vert2edge[paedge[0]]) + if (auto cverts = edge2vert[ednr]; cverts[1] == paedge[1]) + { + int orient = (paedge[0] == verts[0] || paedge[1] == verts[1]) ? 1 : 0; + parent_edges[i] = { orient, { ednr, -1, -1 } }; + } + } + else + { + bool bisect_edge = false; + // edge is splitting edge in middle of triangle: + for (int j = 1; j <= 2; j++) + { + INT<2> paedge1, paedge2, paedge3; + int orient_inner = 0; + if (j == 1) + { + paedge1 = INT<2> (pa0[0], verts[1]); + paedge2 = INT<2> (pa0[1], verts[1]); + paedge3 = INT<2> (pa0[0], pa0[1]); + orient_inner = 0; + } + else + { + paedge1 = INT<2> (pa1[0], verts[0]); + paedge2 = INT<2> (pa1[1], verts[0]); + paedge3 = INT<2> (pa1[0], pa1[1]); + orient_inner = 1; + } + if (paedge1[0] > paedge1[1]) + Swap (paedge1[0], paedge1[1]); + if (paedge2[0] > paedge2[1]) + Swap (paedge2[0], paedge2[1]); + if (paedge3[0] > paedge3[1]) + Swap (paedge3[0], paedge3[1]); + + // if first vertex number is -1, then don't try to find entry in node2edge hash table + if ( paedge1[0] == PointIndex::BASE-1 || paedge2[0] == PointIndex::BASE-1 ) + continue; + + int paedgenr1=-1, paedgenr2=-1, paedgenr3=-1, orient1 = 0, orient2 = 0; + for (int ednr : vert2edge[paedge1[0]]) + if (auto cverts = edge2vert[ednr]; cverts[1] == paedge1[1]) + { + paedgenr1 = ednr; + orient1 = (paedge1[0] == verts[0] || paedge1[1] == verts[1]) ? 1 : 0; + } + for (int ednr : vert2edge[paedge2[0]]) + if (auto cverts = edge2vert[ednr]; cverts[1] == paedge2[1]) + { + paedgenr2 = ednr; + orient2 = (paedge2[0] == verts[0] || paedge2[1] == verts[1]) ? 1 : 0; + } + + for (int ednr : vert2edge[paedge3[0]]) + if (auto cverts = edge2vert[ednr]; cverts[1] == paedge3[1]) + paedgenr3 = ednr; + + if (paedgenr1 != -1 && paedgenr2 != -1){ + bisect_edge = true; + parent_edges[i] = { orient1+2*orient2+4*orient_inner, { paedgenr1, paedgenr2, paedgenr3 } }; + } + } + + if (!bisect_edge) // not a bisect edge (then a red edge) + { + INT<2> paedge1, paedge2, paedge3; + int orient1 = 0, orient2 = 0, orient3=0; + int orient_inner = 0; + paedge1 = INT<2> (pa0[0], pa0[1]); + paedge2 = INT<2> (pa1[0], pa1[1]); + // find common vertex and the thrid pa edge + if (pa0[0]==pa1[0]){// 00 + //orient1 = 0; + orient2 = 1; + if (pa0[1] (pa0[1], pa1[1]); + }else{ + //orient3 = 0; + paedge3 = INT<2> (pa1[1], pa0[1]); + } + } + else if (pa0[0]==pa1[1]){//01 + //orient1 = 0; + //orient2 = 0; + if (pa0[1] (pa0[1], pa1[0]); + }else{ + //orient3 = 0; + paedge3 = INT<2> (pa1[0], pa0[1]); + } + } + else if (pa0[1]==pa1[0]){//10 + orient1 = 1; + orient2 = 1; + if (pa0[0] (pa0[0], pa1[1]); + }else{ + //orient3 = 0; + paedge3 = INT<2> (pa1[1], pa0[0]); + } + } + else if (pa0[1]==pa1[1]){//11 + orient1 = 1; + //orient2 = 0; + if (pa0[0] (pa0[0], pa1[0]); + }else{ + //orient3 = 0; + paedge3 = INT<2> (pa1[0], pa0[0]); + } + } + + int paedgenr1=-1, paedgenr2=-1, paedgenr3=-1; + for (int ednr : vert2edge[paedge1[0]]) + if (auto cverts = edge2vert[ednr]; cverts[1] == paedge1[1]) + paedgenr1 = ednr; + for (int ednr : vert2edge[paedge2[0]]) + if (auto cverts = edge2vert[ednr]; cverts[1] == paedge2[1]) + paedgenr2 = ednr; + + for (int ednr : vert2edge[paedge3[0]]) + if (auto cverts = edge2vert[ednr]; cverts[1] == paedge3[1]) + paedgenr3 = ednr; + + parent_edges[i] = { 8+orient1+2*orient2+4*orient3, { paedgenr1, paedgenr2, paedgenr3 } }; + + //cout <<8+orient1+2*orient2+4*orient3 <<":"< paedge1, paedge2; + if (j == 1) + { + paedge1 = INT<2> (pa1[0], pa2[0]); + paedge2 = INT<2> (pa1[1], pa2[1]); + } + else + { + paedge1 = INT<2> (pa1[0], pa2[1]); + paedge2 = INT<2> (pa1[1], pa2[0]); + } + + int paedgenr1 = 0, paedgenr2 = 0; + int orient1 = 1, orient2 = 1; + + if (paedge1[0] > paedge1[1]) + { + Swap (paedge1[0], paedge1[1]); + orient1 = 0; + } + if (paedge2[0] > paedge2[1]) + { + Swap (paedge2[0], paedge2[1]); + orient2 = 0; + } + + if ( paedge1[0] == -1 || paedge2[0] == -1 ) + continue; + + if (node2edge.Used (paedge1) && node2edge.Used (paedge2)) + { + paedgenr1 = node2edge.Get (paedge1); + paedgenr2 = node2edge.Get (paedge2); + parentedges[i][0] = 2 * paedgenr1 + orient1; + parentedges[i][1] = 2 * paedgenr2 + orient2; + } + } + } + + if (parentedges[i][0] == -1) + { + // triangle split into quad+trig (from anisotropic pyramids) + for (int j = 0; j < 2; j++) + for (int k = 0; k < 2; k++) + { + INT<2> paedge (pa1[1-j], pa2[1-k]); + int orientpa = 1; + if (paedge[0] > paedge[1]) + { + Swap (paedge[0], paedge[1]); + orientpa = 0; + } + if (pa1[j] == pa2[k] && node2edge.Used(paedge)) + { + int paedgenr = node2edge.Get (paedge); + parentedges[i][0] = 2 * paedgenr + orientpa; + } + } + + } + */ + } + + } + + /* + for (int i : Range(parent_edges)) + { + auto [info, nrs] = parent_edges[i]; + cout << "edge " << i << " has " << info << ", nrs = " << nrs[0] << " " << nrs[1] << endl; + } + */ + } } + // edge hashtable:: needed for getting parent faces + ngcore::ClosedHashTable, int> v2e(nv); + if (build_parent_faces) + for (auto i : Range(edge2vert)) + { + auto edge = edge2vert[i]; + INT<2> e2(edge[0], edge[1]); + e2.Sort(); + v2e[e2] = i; + } + // generate faces if (buildfaces) { @@ -694,7 +993,7 @@ namespace netgen faces.SetSize(ne); surffaces.SetSize(nse); - + cnt = 0; for (int i = 0; i < face2vert.Size(); i++) @@ -703,7 +1002,54 @@ namespace netgen for (int i = 0; i < face2vert.Size(); i++) vert2oldface.AddSave (face2vert[i][0], i); + // find all potential intermediate faces + Array> intermediate_faces; + if (build_parent_faces) + { + for (ElementIndex ei = 0; ei < ne; ei++) + for (int i = 0; i < 4; i++) + { + Element2d face; + // cout << "element: " << (*mesh)[ei].PNums() << endl; + (*mesh)[ei].GetFace(i+1, face); + // cout << "face " << face.PNums() << endl; + INT<3,PointIndex> f3 = { face[0], face[1], face[2] }; + for (int j = 0; j < 3; j++) + { + PointIndex v = f3[j]; + if (v >= mesh->mlbetweennodes.Size()+PointIndex::BASE) + continue; + auto pa = mesh->mlbetweennodes[v]; + for (int k = 0; k < 2; k++) + if (f3.Contains(pa[k])) + { + PointIndex v0 = pa[k]; // also in face + PointIndex v1 = pa[1-k]; + PointIndex v2 = f3[0]+f3[1]+f3[2] - v - v0; + // if there is an edge connecting v1 and v2, accept + // the new face + INT<2> parentedge(v1, v2); + parentedge.Sort(); + if (v2e.Used(parentedge)){ + INT<3> cf3 = { v0, v1, v2 }; + cf3.Sort(); + // cout << "intermediate: " << cf3 << " of " << f3 << endl; + intermediate_faces.Append (cf3); + } + } + } + } + } + cnt = 0; + for (int i = 0; i < intermediate_faces.Size(); i++) + cnt[intermediate_faces[i][0]]++; + TABLE vert2intermediate(cnt); + for (int i = 0; i < intermediate_faces.Size(); i++) + vert2intermediate.AddSave (intermediate_faces[i][0], i); + // cout << "vert2intermediate = " << endl << vert2intermediate << endl; + + for (int elnr = 0; elnr < ne; elnr++) for (int j = 0; j < 6; j++) faces[elnr][j].fnr = -1; @@ -722,7 +1068,7 @@ namespace netgen // NgProfiler::StopTimer (timer2a); // NgProfiler::StartTimer (timer2b); - INDEX_3_CLOSED_HASHTABLE vert2face(2*max_face_on_vertex+10); + // INDEX_3_CLOSED_HASHTABLE vert2face(2*max_face_on_vertex+10); int oldnfa = face2vert.Size(); @@ -731,9 +1077,11 @@ namespace netgen // for (auto v : mesh.Points().Range()) // NgProfiler::StartTimer (timer2b1); ParallelForRange - (tm, mesh->GetNV(), // Points().Size(), - [&] (size_t begin, size_t end) + (mesh->GetNV(), // Points().Size(), + [&] (IntRange r) { + auto begin = r.First(); + auto end = r.Next(); INDEX_3_CLOSED_HASHTABLE vert2face(2*max_face_on_vertex+10); for (PointIndex v = begin+PointIndex::BASE; v < end+PointIndex::BASE; v++) @@ -749,6 +1097,20 @@ namespace netgen vert2face.Set (face, 33); // something } int cnti = 0; + + for (int j = 0; j < vert2intermediate[v].Size(); j++) + { + int fnr = vert2intermediate[v][j]; + INDEX_3 face (intermediate_faces[fnr][0], + intermediate_faces[fnr][1], + intermediate_faces[fnr][2]); + face.Sort(); + if (!vert2face.Used(face)) + { + cnti++; + vert2face.Set (face, 33); // something + } + } LoopOverFaces (*mesh, *this, v, [&] (INDEX_4 i4, int elnr, int j, bool volume, int facedir) { @@ -779,9 +1141,11 @@ namespace netgen // for (auto v : mesh.Points().Range()) ParallelForRange - (tm, mesh->GetNV(), // Points().Size(), - [&] (size_t begin, size_t end) + (mesh->GetNV(), // Points().Size(), + [&] (IntRange r) { + auto begin = r.First(); + auto end = r.Next(); INDEX_3_CLOSED_HASHTABLE vert2face(2*max_face_on_vertex+10); for (PointIndex v = begin+PointIndex::BASE; v < end+PointIndex::BASE; v++) @@ -799,6 +1163,25 @@ namespace netgen vert2face.Set (face, fnr); } + for (int j = 0; j < vert2intermediate[v].Size(); j++) + { + int fnr = vert2intermediate[v][j]; + INDEX_3 face (intermediate_faces[fnr][0], + intermediate_faces[fnr][1], + intermediate_faces[fnr][2]); + face.Sort(); + if (!vert2face.Used(face)) + { + INDEX_4 i4(face.I1(), face.I2(), face.I3(), 0); + face2vert[nfa] = i4; + vert2face.Set (face, nfa); + nfa++; + // cout << "adding face " << i4 << endl; + // cnti++; + // vert2face.Set (face, 33); // something + } + } + LoopOverFaces (*mesh, *this, v, [&] (INDEX_4 i4, int elnr, int j, bool volume, int facedir) { @@ -1164,11 +1547,11 @@ namespace netgen } */ ParallelForRange - (tm, ne, - [&] (size_t begin, size_t end) + (ne, + [&] (IntRange r) { NgArray hfaces; - for (ElementIndex ei = begin; ei < end; ei++) + for (ElementIndex ei : r) { GetElementFaces (ei+1, hfaces); for (auto f : hfaces) @@ -1237,6 +1620,306 @@ namespace netgen cout << cnt_err << " elements are not matching !!!" << endl; } // NgProfiler::StopTimer (timer2c); + + + if (build_parent_faces) + { + // tets only + if (id == 0) + PrintMessage (5, "build face hierarchy"); + + // cout << "f2v = " << face2vert << endl; + + ngcore::ClosedHashTable, int> v2f(nv); + for (auto i : Range(face2vert)) + { + auto face = face2vert[i]; + INT<3> f3(face[0], face[1], face[2]); + f3.Sort(); + v2f[f3] = i; + } + + // cout << "v2f:" << endl << v2f << endl; + + parent_faces.SetSize (nfa); + parent_faces = { -1, { -1, -1, -1, -1 } }; + + for (auto i : Range(nfa)) + { + INT<3,PointIndex> f3(face2vert[i][0], face2vert[i][1], face2vert[i][2]); + + + // face on coarses level ? + bool all_vert_coarse = true; + for (int k = 0; k < 3; k++) + { + PointIndex vb = f3[k]; + if (vb >= mesh->mlbetweennodes.Size()+PointIndex::BASE) + continue; + auto parents = mesh->mlbetweennodes[vb]; + if (parents[0] >= PointIndex::BASE) + all_vert_coarse = false; + } + if (all_vert_coarse) continue; + + + + // find a vertex, such that one of its parent is a trig vertex + bool issplit = false; + for (int k = 0; k < 3; k++) + { + PointIndex vb = f3[k]; // assume vb as the new bisect vert + if (vb >= mesh->mlbetweennodes.Size()+PointIndex::BASE) + continue; + auto parents = mesh->mlbetweennodes[vb]; + + // is face part of one parent face (boundary-face) ? + for (int j = 0; j < 2; j++) + { + if (f3.Contains(parents[j])) + { + PointIndex v0 = parents[j]; + PointIndex v1 = parents[1-j]; + + // the third one, on the tip + PointIndex v2 = f3[0]+f3[1]+f3[2] - v0 - vb; + + // if there is an edge connecting v1 and v2, accept + // the new face + INT<2> parentedge(v1, v2); + parentedge.Sort(); + if (v2e.Used(parentedge)){ + INT<3> parentverts(v0, v1, v2); + parentverts.Sort(); + + int classnr = 0; + if (v2 > vb) { Swap (v2, vb); classnr += 1; } + if (v0 > v1) { Swap (v0, v1); classnr += 2; } + if (v1 > v2) { Swap (v1, v2); classnr += 4; } + if (v0 > v1) { Swap (v0, v1); classnr += 8; } + + if (v2f.Used(parentverts)) + { + int pafacenr = v2f[parentverts]; + // cout << "parent-face = " << pafacenr << endl; + parent_faces[i] = { classnr, { pafacenr, -1, -1, -1 } }; + } + else + { + cout << "missing parent face: " << parentverts << endl; + } + issplit=true; + break; + } + } + } + } + + /* + // is face a new face (bisect-face) ? + if (!issplit) + for (int k = 0; k < 3; k++) + { + PointIndex vb = f3[k]; // assume vb as the new bisect vert + if (vb >= mesh->mlbetweennodes.Size()+PointIndex::BASE) + continue; + auto parents = mesh->mlbetweennodes[vb]; + + PointIndex v0 = parents[0]; + PointIndex v1 = parents[1]; + PointIndex v2 = f3[(k+1)%3]; + PointIndex v3 = f3[(k+2)%3]; + INT<3> parentedge1(v0, v2); + parentedge1.Sort(); + INT<3> parentedge2(v0, v3); + parentedge2.Sort(); + INT<3> parentedge3(v1, v2); + parentedge3.Sort(); + INT<3> parentedge4(v1, v3); + parentedge4.Sort(); + // if edges [v0,v2], [v0, v3], [v1,v2], [v1,v3] exists + // then vb is the bisecting edge + if (v2e.Used(parentedge1) && v2e.Used(parentedge2) + && v2e.Used(parentedge3) && v2e.Used(parentedge4) + ){ + int classnr; + if (k==2){// vb is the largest vert: 6 cases + // by default v0 < v1, v2 < v3 + if (v1 < v2) classnr = 0; + else if (v1 < v3 && v0 < v2) classnr = 1; + else if (v0 < v2) classnr = 2; + else if (v1 < v3) classnr = 3; + else if (v0 < v3) classnr = 4; + else classnr = 5; + }else if (k==1){// vb is the second largest vert: 3 cases + // by default v0 < v1, v3 < v2 + if (v1 < v3) classnr = 6; + else if (v0 < v3) classnr = 7; + else classnr = 8; + }else {// vb is the third largest vert: 1 case + // by default v0 < v1 < vb < v2 < v3 + classnr=9; + } + INT<3> parentverts1(v0, v2, v3); + parentverts1.Sort(); + INT<3> parentverts2(v1, v2, v3); + parentverts2.Sort(); + INT<3> parentverts3(v0, v1, v2); + parentverts3.Sort(); + INT<3> parentverts4(v0, v1, v3); + parentverts4.Sort(); + int pafacenr1=-1, pafacenr2=-1, pafacenr3=-1, pafacenr4=-1; + if (v2f.Used(parentverts1)) + { + pafacenr1 = v2f[parentverts1]; + // cout << "parent-face1 = " << pafacenr1<< endl ; + } + if (v2f.Used(parentverts2)) + { + pafacenr2 = v2f[parentverts2]; + // cout << "parent-face2 = " << pafacenr2<< endl ; + } + if (v2f.Used(parentverts3)) + { + pafacenr3 = v2f[parentverts3]; + // cout << "parent-face3 = " << pafacenr3<< endl ; + } + if (v2f.Used(parentverts4)) + { + pafacenr4 = v2f[parentverts4]; + // cout << "parent-face4 = " << pafacenr4<< endl ; + } + + if (k == 0 || k == 2) + parent_faces[i] = { classnr, { pafacenr2, pafacenr1, + pafacenr4, pafacenr3} }; + else + parent_faces[i] = { classnr, { pafacenr2, pafacenr1, + pafacenr3, pafacenr4} }; + break; + } + } + */ + + // is face a new face (bisect-face) ? + if (!issplit) + for (int k = 0; k < 3; k++) + { + PointIndex vb = f3[k]; // assume vb as the new bisect vert + if (vb >= mesh->mlbetweennodes.Size()+PointIndex::BASE) + continue; + auto parents = mesh->mlbetweennodes[vb]; + + PointIndex v0 = parents[0]; + PointIndex v1 = parents[1]; + PointIndex v2 = f3[(k+1)%3]; + PointIndex v3 = f3[(k+2)%3]; + INT<2> parentedge1(v0, v2); + parentedge1.Sort(); + INT<2> parentedge2(v0, v3); + parentedge2.Sort(); + INT<2> parentedge3(v1, v2); + parentedge3.Sort(); + INT<2> parentedge4(v1, v3); + parentedge4.Sort(); + + // if edges [v0,v2], [v0, v3], [v1,v2], [v1,v3] exists + // then vb is the bisecting edge + if (v2e.Used(parentedge1) && v2e.Used(parentedge2) + && v2e.Used(parentedge3) && v2e.Used(parentedge4)) + { + int verts[5] = { v0, v1, v2, v3, vb }; + /* + cout << "verts5: "; + for (int j = 0; j < 5; j++) + cout << verts[j] << " "; + */ + // classify permutation of verts + int classnr = 0; + for (int j = 0; j < 4; j++) + { + int maxk = 0; + for (int k = 0; k < 5-j; k++) + if (verts[k] > verts[maxk]) maxk = k; + // compress + for (int k = maxk; k < 4-j; k++) + verts[k] = verts[k+1]; + classnr = maxk + (5-j) * classnr; + } + // cout << "classnr = " << classnr << endl; + + INT<3> parentverts1(v1, v2, v3); + parentverts1.Sort(); + INT<3> parentverts2(v0, v2, v3); + parentverts2.Sort(); + INT<3> parentverts3(v0, v1, v3); + parentverts3.Sort(); + INT<3> parentverts4(v0, v1, v2); + parentverts4.Sort(); + + if (!v2f.Used(parentverts1) || !v2f.Used(parentverts2) || + !v2f.Used(parentverts3) || !v2f.Used(parentverts4)) + { + cout << "all edges are used, but not faces ????" << endl; + continue; + } + + int pafacenr1 = v2f[parentverts1]; + int pafacenr2 = v2f[parentverts2]; + int pafacenr3 = v2f[parentverts3]; + int pafacenr4 = v2f[parentverts4]; + + + parent_faces[i] = { classnr, { pafacenr1, pafacenr2, + pafacenr3, pafacenr4} }; + + break; + } + } + + auto [info, nrs] = parent_faces[i]; + if (nrs[0] == -1){ + // hacking for tet red refinements + PointIndex v0 = f3[0]; + auto pa0 = mesh->mlbetweennodes[v0]; + auto pa1 = mesh->mlbetweennodes[f3[1]]; + auto pa2 = mesh->mlbetweennodes[f3[2]]; + // v0 is a coarse vertex ==> f3 is a boundary face + if (v0==pa1[0] || v0==pa1[1]){ + if (pa1[0]==v0){// type 0: bottom left corner + INT<3> parentverts(v0, pa1[1], pa2[1]); + int pafacenr = v2f[parentverts]; + parent_faces[i] = { 16, { pafacenr, -1, -1, -1} }; + //cout << "f "< parentverts(pa1[0], v0, pa2[1]); + int pafacenr = v2f[parentverts]; + parent_faces[i] = { 17, { pafacenr, -1, -1, -1} }; + //cout << "f "< parentverts(pa1[0], pa2[0], v0); + int pafacenr = v2f[parentverts]; + parent_faces[i] = { 18, { pafacenr, -1, -1, -1} }; + //cout << "f "< parentverts(pa0[0], pa0[1], pa1[1]); + int pafacenr = v2f[parentverts]; + parent_faces[i] = { 19, { pafacenr, -1, -1, -1} }; + //cout << "f "< struct FixArray { @@ -34,13 +35,18 @@ struct T_FACE T & operator[] (size_t i) { return vals[i]; } T operator[] (size_t i) const { return vals[i]; } }; - + */ + + template + using FixArray = std::array; class MeshTopology { const Mesh * mesh; bool buildedges; bool buildfaces; + bool build_parent_edges = false; // may be changed to default = false + bool build_parent_faces = false; // may be changed to default = false NgArray edge2vert; NgArray face2vert; @@ -57,6 +63,7 @@ class MeshTopology NgArray surffaces; NgArray surf2volelement; NgArray face2surfel; + Array edge2segment; TABLE vert2element; TABLE vert2surfelement; TABLE vert2segment; @@ -75,13 +82,16 @@ public: { buildedges = be; } void SetBuildFaces (bool bf) { buildfaces = bf; } + void SetBuildParentEdges (bool bh) { build_parent_edges = bh; } + void SetBuildParentFaces (bool bh) { build_parent_faces = bh; } - bool HasEdges () const - { return buildedges; } - bool HasFaces () const - { return buildfaces; } + void EnableTable (string name, bool set); - void Update(TaskManager tm = &DummyTaskManager, Tracer tracer = &DummyTracer); + bool HasEdges () const { return buildedges; } + bool HasFaces () const { return buildfaces; } + bool HasParentEdges () const { return build_parent_edges; } + + void Update(NgTaskManager tm = &DummyTaskManager, NgTracer tracer = &DummyTracer); bool NeedsUpdate() const; @@ -160,13 +170,15 @@ public: } int GetFace2SurfaceElement (int fnr) const { return face2surfel[fnr-1]; } + + SegmentIndex GetSegmentOfEdge(int edgenr) const { return edge2segment[edgenr-1]; } void GetVertexElements (int vnr, NgArray & elements) const; NgFlatArray GetVertexElements (int vnr) const { return vert2element[vnr]; } void GetVertexSurfaceElements( int vnr, NgArray& elements ) const; - NgFlatArray GetVertexSurfaceElements (int vnr) const + NgFlatArray GetVertexSurfaceElements(PointIndex vnr) const { return vert2surfelement[vnr]; } NgFlatArray GetVertexSegments (int vnr) const @@ -178,6 +190,17 @@ public: int GetVerticesEdge ( int v1, int v2) const; void GetSegmentVolumeElements ( int segnr, NgArray & els ) const; void GetSegmentSurfaceElements ( int segnr, NgArray & els ) const; + + +private: + Array>> parent_edges; + void BuildParentEdges (); + + Array>> parent_faces; + void BuildParentFaces (); +public: + auto GetParentEdges (int enr) const { return parent_edges[enr]; } + auto GetParentFaces (int fnr) const { return parent_faces[fnr]; } }; diff --git a/libsrc/occ/CMakeLists.txt b/libsrc/occ/CMakeLists.txt index 7e7a2a65..ea5a756e 100644 --- a/libsrc/occ/CMakeLists.txt +++ b/libsrc/occ/CMakeLists.txt @@ -2,14 +2,14 @@ add_definitions(-DNGINTERFACE_EXPORTS) add_library(occ ${NG_LIB_TYPE} Partition_Inter2d.cxx Partition_Inter3d.cxx Partition_Loop.cxx Partition_Loop2d.cxx Partition_Loop3d.cxx Partition_Spliter.cxx - occconstruction.cpp occgenmesh.cpp occgeom.cpp occmeshsurf.cpp python_occ.cpp + occgenmesh.cpp occgeom.cpp occmeshsurf.cpp python_occ.cpp ) if(USE_GUI) add_library(occvis ${NG_LIB_TYPE} vsocc.cpp) target_link_libraries(occvis PUBLIC ngcore) endif(USE_GUI) -target_link_libraries(occ PUBLIC ngcore PRIVATE netgen_python) +target_link_libraries(occ PUBLIC ngcore PRIVATE "$") if(NOT WIN32) target_link_libraries( occ PRIVATE ${OCC_LIBRARIES} ) diff --git a/libsrc/occ/Partition_Loop2d.cxx b/libsrc/occ/Partition_Loop2d.cxx index 22740a58..1c3d7957 100644 --- a/libsrc/occ/Partition_Loop2d.cxx +++ b/libsrc/occ/Partition_Loop2d.cxx @@ -52,8 +52,6 @@ #include #include -#define PI 3.14159265358979323846 - //======================================================================= //function : Partition_Loop2d //purpose : diff --git a/libsrc/occ/occgenmesh.cpp b/libsrc/occ/occgenmesh.cpp index be645a14..783ba96b 100644 --- a/libsrc/occ/occgenmesh.cpp +++ b/libsrc/occ/occgenmesh.cpp @@ -244,9 +244,6 @@ namespace netgen hvalue[0] = 0; pnt = c->Value(s0); - double olddist = 0; - double dist = 0; - int tmpVal = (int)(DIVIDEEDGESECTIONS); for (int i = 1; i <= tmpVal; i++) @@ -259,9 +256,6 @@ namespace netgen //(*testout) << "mesh.GetH(Point3d(pnt.X(), pnt.Y(), pnt.Z())) " << mesh.GetH(Point3d(pnt.X(), pnt.Y(), pnt.Z())) // << " pnt.Distance(oldpnt) " << pnt.Distance(oldpnt) << endl; - - olddist = dist; - dist = pnt.Distance(oldpnt); } // nsubedges = int(ceil(hvalue[DIVIDEEDGESECTIONS])); @@ -365,6 +359,7 @@ namespace netgen { TopoDS_Face face = TopoDS::Face(exp1.Current()); int facenr = geom.fmap.FindIndex(face); + if(facenr < 1) continue; if (face2solid[0][facenr-1] == 0) face2solid[0][facenr-1] = solidnr; @@ -382,8 +377,7 @@ namespace netgen int facenr = 0; - int edgenr = 0; - + int edgenr = mesh.GetNSeg(); (*testout) << "faces = " << geom.fmap.Extent() << endl; int curr = 0; @@ -449,6 +443,8 @@ namespace netgen continue; } + if(geom.emap.FindIndex(edge) < 1) continue; + if (geom.vmap.FindIndex(TopExp::FirstVertex (edge)) == geom.vmap.FindIndex(TopExp::LastVertex (edge))) { @@ -515,6 +511,9 @@ namespace netgen if (!exists) pnums[i] = mesh.AddPoint (mp[i-1]); } + if(geom.enames.Size() && geom.enames[curr-1] != "") + mesh.SetCD2Name(geomedgenr, geom.enames[curr-1]); + (*testout) << "NP = " << mesh.GetNP() << endl; //(*testout) << pnums[pnums.Size()-1] << endl; diff --git a/libsrc/occ/occgeom.cpp b/libsrc/occ/occgeom.cpp index 20c8e303..cc5b9f65 100644 --- a/libsrc/occ/occgeom.cpp +++ b/libsrc/occ/occgeom.cpp @@ -17,12 +17,14 @@ #include "ShapeFix_FixSmallFace.hxx" #include "Partition_Spliter.hxx" #include "BRepAlgoAPI_Fuse.hxx" +#include "Interface_InterfaceModel.hxx" #include "XSControl_WorkSession.hxx" #include "XSControl_TransferReader.hxx" #include "StepRepr_RepresentationItem.hxx" #include "StepBasic_ProductDefinitionRelationship.hxx" - +#include "Transfer_TransientProcess.hxx" +#include "TransferBRep.hxx" #ifndef _Standard_Version_HeaderFile #include #endif @@ -38,6 +40,14 @@ namespace netgen { + OCCGeometry::OCCGeometry(const TopoDS_Shape& _shape) + { + shape = _shape; + changed = true; + BuildFMap(); + CalcBoundingBox(); + } + string STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * aReader) { const Handle(XSControl_WorkSession)& theSession = aReader->Reader().WS(); @@ -167,8 +177,91 @@ namespace netgen } + void OCCGeometry :: GlueGeometry() + { + PrintMessage(1, "OCC Glue Geometry"); + /* + // + BRep_Builder builder; + TopoDS_Shape my_fuse; + int cnt = 0; + for (TopExp_Explorer exp_solid(shape, TopAbs_SOLID); exp_solid.More(); exp_solid.Next()) + { + cout << "cnt = " << cnt << endl; + if (cnt == 0) + my_fuse = exp_solid.Current(); + else + // my_fuse = BRepAlgoAPI_Fuse (my_fuse, exp_solid.Current()); + my_fuse = QANewModTopOpe_Glue::QANewModTopOpe_Glue(my_fuse, exp_solid.Current()); + cnt++; + } + cout << "remove" << endl; + // for (int i = 1; i <= somap.Size(); i++) + // builder.Remove (shape, somap(i)); + cout << "now add" << endl; + // builder.Add (shape, my_fuse); + shape = my_fuse; + cout << "build fmap" << endl; + BuildFMap(); + */ + // from + // https://www.opencascade.com/doc/occt-7.4.0/overview/html/occt_user_guides__boolean_operations.html + BOPAlgo_Builder aBuilder; + + // Setting arguments + TopTools_ListOfShape aLSObjects; + for (TopExp_Explorer exp_solid(shape, TopAbs_SOLID); exp_solid.More(); exp_solid.Next()) + aLSObjects.Append (exp_solid.Current()); + aBuilder.SetArguments(aLSObjects); + + // Setting options for GF + // Set parallel processing mode (default is false) + // Standard_Boolean bRunParallel = Standard_True; + // aBuilder.SetRunParallel(bRunParallel); + + // Set Fuzzy value (default is Precision::Confusion()) + // Standard_Real aFuzzyValue = 1.e-5; + // aBuilder.SetFuzzyValue(aFuzzyValue); + + // Set safe processing mode (default is false) + // Standard_Boolean bSafeMode = Standard_True; + // aBuilder.SetNonDestructive(bSafeMode); + + // Set Gluing mode for coinciding arguments (default is off) + // BOPAlgo_GlueEnum aGlue = BOPAlgo_GlueShift; + // aBuilder.SetGlue(aGlue); + + // Disabling/Enabling the check for inverted solids (default is true) + // Standard Boolean bCheckInverted = Standard_False; + // aBuilder.SetCheckInverted(bCheckInverted); + + // Set OBB usage (default is false) + // Standard_Boolean bUseOBB = Standard_True; + // aBuilder.SetUseOBB(buseobb); + + // Perform the operation + aBuilder.Perform(); + // Check for the errors +#if OCC_VERSION_HEX >= 0x070200 + if (aBuilder.HasErrors()) + { + cout << "builder has errors" << endl; + return; + } + // Check for the warnings + if (aBuilder.HasWarnings()) + { + // treatment of the warnings + ; + } +#endif + // result of the operation + shape = aBuilder.Shape(); + BuildFMap(); + } + void OCCGeometry :: HealGeometry () { int nrc = 0, nrcs = 0, @@ -189,7 +282,7 @@ namespace netgen double surfacecont = 0; { - Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape; + Handle(ShapeBuild_ReShape) rebuild = new ShapeBuild_ReShape; rebuild->Apply(shape); for (exp1.Init (shape, TopAbs_EDGE); exp1.More(); exp1.Next()) { @@ -220,7 +313,7 @@ namespace netgen cout << endl << "- repairing faces" << endl; Handle(ShapeFix_Face) sff; - Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape; + Handle(ShapeBuild_ReShape) rebuild = new ShapeBuild_ReShape; rebuild->Apply(shape); @@ -277,7 +370,7 @@ namespace netgen { - Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape; + Handle(ShapeBuild_ReShape) rebuild = new ShapeBuild_ReShape; rebuild->Apply(shape); for (exp1.Init (shape, TopAbs_EDGE); exp1.More(); exp1.Next()) { @@ -294,7 +387,7 @@ namespace netgen cout << endl << "- fixing small edges" << endl; Handle(ShapeFix_Wire) sfw; - Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape; + Handle(ShapeBuild_ReShape) rebuild = new ShapeBuild_ReShape; rebuild->Apply(shape); @@ -361,7 +454,7 @@ namespace netgen { BuildFMap(); - Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape; + Handle(ShapeBuild_ReShape) rebuild = new ShapeBuild_ReShape; rebuild->Apply(shape); for (exp1.Init (shape, TopAbs_EDGE); exp1.More(); exp1.Next()) @@ -389,7 +482,7 @@ namespace netgen { - Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape; + Handle(ShapeBuild_ReShape) rebuild = new ShapeBuild_ReShape; rebuild->Apply(shape); for (exp1.Init (shape, TopAbs_EDGE); exp1.More(); exp1.Next()) { @@ -515,7 +608,7 @@ namespace netgen { - Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape; + Handle(ShapeBuild_ReShape) rebuild = new ShapeBuild_ReShape; rebuild->Apply(shape); for (exp1.Init (shape, TopAbs_EDGE); exp1.More(); exp1.Next()) { @@ -560,7 +653,7 @@ namespace netgen TopoDS_Solid solid = TopoDS::Solid(exp0.Current()); TopoDS_Solid newsolid = solid; BRepLib::OrientClosedSolid (newsolid); - Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape; + Handle(ShapeBuild_ReShape) rebuild = new ShapeBuild_ReShape; // rebuild->Apply(shape); rebuild->Replace(solid, newsolid); TopoDS_Shape newshape = rebuild->Apply(shape, TopAbs_COMPSOLID);//, 1); @@ -983,7 +1076,7 @@ namespace netgen TopoDS_Solid solid = TopoDS::Solid(exp0.Current()); TopoDS_Solid newsolid = solid; BRepLib::OrientClosedSolid (newsolid); - Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape; + Handle(ShapeBuild_ReShape) rebuild = new ShapeBuild_ReShape; rebuild->Replace(solid, newsolid); TopoDS_Shape newshape = rebuild->Apply(shape, TopAbs_SHAPE, 1); @@ -1315,10 +1408,10 @@ namespace netgen static Timer timer_getnames("LoadOCC-get names"); // Initiate a dummy XCAF Application to handle the STEP XCAF Document - static Handle_XCAFApp_Application dummy_app = XCAFApp_Application::GetApplication(); + static Handle(XCAFApp_Application) dummy_app = XCAFApp_Application::GetApplication(); // Create an XCAF Document to contain the STEP file itself - Handle_TDocStd_Document step_doc; + Handle(TDocStd_Document) step_doc; // Check if a STEP File is already open under this handle, if so, close it to prevent // Segmentation Faults when trying to create a new document @@ -1348,8 +1441,8 @@ namespace netgen timer_transfer.Stop(); // Read in the shape(s) and the colours present in the STEP File - Handle_XCAFDoc_ShapeTool step_shape_contents = XCAFDoc_DocumentTool::ShapeTool(step_doc->Main()); - Handle_XCAFDoc_ColorTool step_colour_contents = XCAFDoc_DocumentTool::ColorTool(step_doc->Main()); + Handle(XCAFDoc_ShapeTool) step_shape_contents = XCAFDoc_DocumentTool::ShapeTool(step_doc->Main()); + Handle(XCAFDoc_ColorTool) step_colour_contents = XCAFDoc_DocumentTool::ColorTool(step_doc->Main()); TDF_LabelSequence step_shapes; step_shape_contents->GetShapes(step_shapes); @@ -1379,45 +1472,89 @@ namespace netgen PrintContents (occgeo); string name; TopExp_Explorer exp0,exp1; + + + + std::map shape_names; + { + static Timer t("file shape_names"); RegionTimer r(t); + // code inspired from + // https://www.opencascade.com/content/reading-step-entity-id-slow + const Handle(XSControl_WorkSession) workSession = reader.Reader().WS(); + const Handle(Interface_InterfaceModel) model = workSession->Model(); + const Handle(XSControl_TransferReader) transferReader = workSession->TransferReader(); + Handle(Transfer_TransientProcess) transProc = transferReader->TransientProcess(); + + Standard_Integer nb = model->NbEntities(); + for (Standard_Integer i = 1; i < nb; i++) + { + Handle(Standard_Transient) entity = model->Value(i); + + // if (!entity->DynamicType()->SubType("StepShape_OpenShell")) continue; + + Handle(StepRepr_RepresentationItem) SRRI = + Handle(StepRepr_RepresentationItem)::DownCast(entity); + + if (SRRI.IsNull()) { + // cout << "no StepRepr_RepresentationItem found in " << entity->DynamicType()->Name(); + continue; + } + Handle(TCollection_HAsciiString) hName = SRRI->Name(); + string shapeName = hName->ToCString(); + + // cout << "STEP " << i << " " << entity->DynamicType()->Name() << ", shapename = " << shapeName; + Handle(Transfer_Binder) binder; + if (!transProc->IsBound(SRRI)) { + // cout << "found unbound entity " << shapeName; + continue; + } + binder = transProc->Find(SRRI); + TopoDS_Shape shape = TransferBRep::ShapeResult(binder); + // if (!shape.IsNull()) + shape_names[shape.TShape()] = shapeName; + /* + if (!shape.IsNull()) + cout << " shapetype = " << shape.ShapeType() << endl; + else + cout << "is-Null" << endl; + */ + } + // for (auto pair : shape_names) + // cout << "name = " << pair.second << endl; + } + timer_getnames.Start(); for (exp0.Init(occgeo->shape, TopAbs_SOLID); exp0.More(); exp0.Next()) { TopoDS_Solid solid = TopoDS::Solid(exp0.Current()); - name = STEP_GetEntityName(solid,&reader); + // name = STEP_GetEntityName(solid,&reader); + // cout << "solidname = " << name << ", mapname = " << shape_names[solid.TShape()] << endl; + name = shape_names[solid.TShape()]; if (name == "") - name = string("domain_") + ToString(occgeo->snames.Size()); + name = string("domain_") + ToString(occgeo->snames.Size()); occgeo->snames.Append(name); } + for (exp0.Init(occgeo->shape, TopAbs_FACE); exp0.More(); exp0.Next()) { TopoDS_Face face = TopoDS::Face(exp0.Current()); - name = STEP_GetEntityName(face,&reader); + // name = STEP_GetEntityName(face,&reader); + // cout << "getname = " << name << ", mapname = " << shape_names[face.TShape()] << endl; + name = shape_names[face.TShape()]; if (name == "") - name = string("bc_") + ToString(occgeo->fnames.Size()); + name = string("bc_") + ToString(occgeo->fnames.Size()); occgeo->fnames.Append(name); -// for (exp1.Init(face, TopAbs_EDGE); exp1.More(); exp1.Next()) -// { -// TopoDS_Edge edge = TopoDS::Edge(exp1.Current()); -// name = STEP_GetEntityName(edge,&reader); -// occgeo->enames.Append(name); -// } + for (exp1.Init(face, TopAbs_EDGE); exp1.More(); exp1.Next()) + { + TopoDS_Edge edge = TopoDS::Edge(exp1.Current()); + // name = STEP_GetEntityName(edge,&reader); + // cout << "getname = " << name << ", mapname = " << shape_names[edge.TShape()] << endl; + name = shape_names[edge.TShape()]; + occgeo->enames.Append(name); + } } - timer_getnames.Stop(); - // Gerhard BEGIN -// cout << "Solid Names: "<snames.Size();i++) -// cout << occgeo->snames[i] << endl; -// cout << " " <fnames.Size();i++) -// cout << occgeo->fnames[i] << endl; -// cout << " " <enames.Size();i++) -// cout << occgeo->enames[i] << endl; -// cout << " " <Main()); - Handle_XCAFDoc_ColorTool iges_colour_contents = XCAFDoc_DocumentTool::ColorTool(iges_doc->Main()); + Handle(XCAFDoc_ShapeTool) iges_shape_contents = XCAFDoc_DocumentTool::ShapeTool(iges_doc->Main()); + Handle(XCAFDoc_ColorTool) iges_colour_contents = XCAFDoc_DocumentTool::ColorTool(iges_doc->Main()); TDF_LabelSequence iges_shapes; iges_shape_contents->GetShapes(iges_shapes); @@ -1530,7 +1667,7 @@ namespace netgen // Fixed a bug in the OpenCascade XDE Colour handling when // opening BREP Files, since BREP Files have no colour data. // Hence, the face_colours Handle needs to be created as a NULL handle. - occgeo->face_colours = Handle_XCAFDoc_ColorTool(); + occgeo->face_colours = Handle(XCAFDoc_ColorTool)(); occgeo->face_colours.Nullify(); occgeo->changed = 1; occgeo->BuildFMap(); diff --git a/libsrc/occ/occgeom.hpp b/libsrc/occ/occgeom.hpp index 69d906ec..fa0e4f2e 100644 --- a/libsrc/occ/occgeom.hpp +++ b/libsrc/occ/occgeom.hpp @@ -31,38 +31,16 @@ #include "Poly_Triangle.hxx" #include "GProp_GProps.hxx" #include "BRepGProp.hxx" -#include "Geom_Surface.hxx" -#include "TopExp.hxx" #include "gp_Pnt.hxx" #include "TopoDS.hxx" #include "TopoDS_Solid.hxx" #include "TopExp_Explorer.hxx" #include "TopTools_ListIteratorOfListOfShape.hxx" -#include "BRep_Tool.hxx" -#include "Geom_Curve.hxx" -#include "Geom2d_Curve.hxx" -#include "Geom_Surface.hxx" -#include "GeomAPI_ProjectPointOnSurf.hxx" -#include "GeomAPI_ProjectPointOnCurve.hxx" #include "TopoDS_Wire.hxx" #include "BRepTools_WireExplorer.hxx" -#include "BRepTools.hxx" #include "TopTools_IndexedMapOfShape.hxx" -#include "TopExp.hxx" -#include "BRepBuilderAPI_MakeVertex.hxx" -#include "BRepBuilderAPI_MakeShell.hxx" -#include "BRepBuilderAPI_MakeSolid.hxx" -#include "BRepOffsetAPI_Sewing.hxx" #include "BRepLProp_CLProps.hxx" -#include "BRepLProp_SLProps.hxx" -#include "BRepAdaptor_Surface.hxx" #include "BRepAdaptor_Curve.hxx" -#include "Poly_Triangulation.hxx" -#include "Poly_Array1OfTriangle.hxx" -#include "TColgp_Array1OfPnt2d.hxx" -#include "Poly_Triangle.hxx" -#include "GProp_GProps.hxx" -#include "BRepGProp.hxx" #include "TopoDS_Shape.hxx" #include "TopoDS_Face.hxx" #include "IGESToBRep_Reader.hxx" @@ -78,7 +56,7 @@ #include "Bnd_Box.hxx" #include "ShapeAnalysis.hxx" #include "ShapeBuild_ReShape.hxx" - +#include "BOPAlgo_Builder.hxx" // Philippose - 29/01/2009 // OpenCascade XDE Support @@ -206,7 +184,7 @@ namespace netgen void Print (ostream & ost) const; }; - class OCCGeometry : public NetgenGeometry + class DLL_HEADER OCCGeometry : public NetgenGeometry { Point<3> center; OCCParameters occparam; @@ -216,12 +194,12 @@ namespace netgen TopTools_IndexedMapOfShape fmap, emap, vmap, somap, shmap, wmap; NgArray fsingular, esingular, vsingular; Box<3> boundingbox; - NgArray fnames, /*enames,*/ snames; + NgArray fnames, enames, snames; // Philippose - 29/01/2009 // OpenCascade XDE Support // XCAF Handle to make the face colours available to the rest of // the system - Handle_XCAFDoc_ColorTool face_colours; + Handle(XCAFDoc_ColorTool) face_colours; mutable int changed; mutable NgArray facemeshstatus; @@ -261,6 +239,8 @@ namespace netgen vmap.Clear(); } + OCCGeometry(const TopoDS_Shape& _shape); + Mesh::GEOM_TYPE GetGeomType() const override { return Mesh::GEOM_OCC; } @@ -276,7 +256,7 @@ namespace netgen void FinalizeMesh(Mesh& mesh) const override; - DLL_HEADER void Save (string filename) const override; + void Save (string filename) const override; void DoArchive(Archive& ar) override; @@ -298,7 +278,7 @@ namespace netgen const PointGeomInfo & gi2, Point<3> & newp, PointGeomInfo & newgi) const override; - DLL_HEADER void BuildFMap(); + void BuildFMap(); Box<3> GetBoundingBox() const { return boundingbox; } @@ -323,8 +303,8 @@ namespace netgen return OCCSurface (TopoDS::Face(fmap(surfi)), PLANESPACE); } - DLL_HEADER void CalcBoundingBox (); - DLL_HEADER void BuildVisualizationMesh (double deflection); + void CalcBoundingBox (); + void BuildVisualizationMesh (double deflection); void RecursiveTopologyTree (const TopoDS_Shape & sh, stringstream & str, @@ -332,17 +312,18 @@ namespace netgen bool free, const char * lname); - DLL_HEADER void GetTopologyTree (stringstream & str); + void GetTopologyTree (stringstream & str); - DLL_HEADER void PrintNrShapes (); + void PrintNrShapes (); - DLL_HEADER void CheckIrregularEntities (stringstream & str); + void CheckIrregularEntities (stringstream & str); - DLL_HEADER void SewFaces(); + void SewFaces(); - DLL_HEADER void MakeSolid(); + void MakeSolid(); - DLL_HEADER void HealGeometry(); + void HealGeometry(); + void GlueGeometry(); // Philippose - 15/01/2009 // Sets the maximum mesh size for a given face @@ -435,13 +416,12 @@ namespace netgen vvispar[i-1].Lowlight(); } - DLL_HEADER void GetUnmeshedFaceInfo (stringstream & str); - DLL_HEADER void GetNotDrawableFaces (stringstream & str); - DLL_HEADER bool ErrorInSurfaceMeshing (); + void GetUnmeshedFaceInfo (stringstream & str); + void GetNotDrawableFaces (stringstream & str); + bool ErrorInSurfaceMeshing (); // void WriteOCC_STL(char * filename); - // DLL_HEADER virtual int GenerateMesh (shared_ptr & mesh, MeshingParameters & mparam); private: bool FastProject (int surfi, Point<3> & ap, double& u, double& v) const; }; diff --git a/libsrc/occ/occpkg.cpp b/libsrc/occ/occpkg.cpp index a4f019a7..62d0fe0b 100644 --- a/libsrc/occ/occpkg.cpp +++ b/libsrc/occ/occpkg.cpp @@ -519,7 +519,8 @@ namespace netgen stringstream str; occgeometry->GetTopologyTree (str); - char* cstr = (char*)str.str().c_str(); + auto txt = str.str(); + char* cstr = (char*) txt.c_str(); (*testout) << cstr << endl; diff --git a/libsrc/occ/python_occ.cpp b/libsrc/occ/python_occ.cpp index 837ecdb0..a887c0f2 100644 --- a/libsrc/occ/python_occ.cpp +++ b/libsrc/occ/python_occ.cpp @@ -50,6 +50,8 @@ DLL_HEADER void ExportNgOCC(py::module &m) m.attr("occ_version") = OCC_VERSION_COMPLETE; py::class_, NetgenGeometry> (m, "OCCGeometry", R"raw_string(Use LoadOCCGeometry to load the geometry from a *.step file.)raw_string") .def(py::init<>()) + .def(py::init(), py::arg("shape"), + "Create Netgen OCCGeometry from existing TopoDS_Shape") .def(py::init([] (const string& filename) { shared_ptr geo; @@ -66,6 +68,7 @@ DLL_HEADER void ExportNgOCC(py::module &m) }), py::arg("filename"), "Load OCC geometry from step, brep or iges file") .def(NGSPickle()) + .def("Glue", &OCCGeometry::GlueGeometry) .def("Heal",[](OCCGeometry & self, double tolerance, bool fixsmalledges, bool fixspotstripfaces, bool sewfaces, bool makesolids, bool splitpartitions) { self.tolerance = tolerance; diff --git a/libsrc/stlgeom/CMakeLists.txt b/libsrc/stlgeom/CMakeLists.txt index f8ce2fd3..8efefbca 100644 --- a/libsrc/stlgeom/CMakeLists.txt +++ b/libsrc/stlgeom/CMakeLists.txt @@ -1,3 +1,4 @@ +add_definitions(-DNGINTERFACE_EXPORTS) add_library(stl ${NG_LIB_TYPE} meshstlsurface.cpp stlgeom.cpp stlgeomchart.cpp stlgeommesh.cpp stlline.cpp stltool.cpp stltopology.cpp python_stl.cpp @@ -8,11 +9,11 @@ if(NOT WIN32) install( TARGETS stl ${NG_INSTALL_DIR}) endif(NOT WIN32) -target_link_libraries( stl PUBLIC ngcore PRIVATE netgen_python ) +target_link_libraries( stl PUBLIC ngcore PRIVATE "$" ) if(USE_GUI) add_library(stlvis ${NG_LIB_TYPE} vsstl.cpp) - target_link_libraries(stlvis PRIVATE netgen_python PUBLIC ngcore) + target_link_libraries(stlvis PRIVATE "$" PUBLIC ngcore) if(NOT WIN32) target_link_libraries( stlvis PUBLIC stl ) install( TARGETS stlvis ${NG_INSTALL_DIR}) diff --git a/libsrc/stlgeom/python_stl.cpp b/libsrc/stlgeom/python_stl.cpp index accfb8d6..74b2c3f4 100644 --- a/libsrc/stlgeom/python_stl.cpp +++ b/libsrc/stlgeom/python_stl.cpp @@ -6,10 +6,6 @@ #include #include "../meshing/python_mesh.hpp" -#ifdef WIN32 - #define DLL_HEADER __declspec(dllexport) -#endif - using namespace netgen; namespace netgen { @@ -125,7 +121,7 @@ void CreateSTLParametersFromKwargs(STLParameters& stlparam, py::dict kwargs) } -DLL_HEADER void ExportSTL(py::module & m) +NGCORE_API_EXPORT void ExportSTL(py::module & m) { py::class_, NetgenGeometry> (m,"STLGeometry") .def(py::init<>()) diff --git a/libsrc/stlgeom/vsstl.cpp b/libsrc/stlgeom/vsstl.cpp index b8ef117f..63202fdb 100644 --- a/libsrc/stlgeom/vsstl.cpp +++ b/libsrc/stlgeom/vsstl.cpp @@ -1216,13 +1216,10 @@ void VisualSceneSTLMeshing :: MouseDblClick (int px, int py) #ifdef NG_PYTHON -#ifdef WIN32 - #define DLL_HEADER __declspec(dllexport) -#endif - #include <../general/ngpython.hpp> +#include -DLL_HEADER void ExportSTLVis(py::module &m) +NGCORE_API_EXPORT void ExportSTLVis(py::module &m) { using namespace netgen; diff --git a/libsrc/stlgeom/vsstl.hpp b/libsrc/stlgeom/vsstl.hpp index ea1a5b72..f36dcac2 100644 --- a/libsrc/stlgeom/vsstl.hpp +++ b/libsrc/stlgeom/vsstl.hpp @@ -10,30 +10,30 @@ namespace netgen { - class VisualSceneSTLGeometry : public VisualScene + class DLL_HEADER VisualSceneSTLGeometry : public VisualScene { NgArray trilists; class STLGeometry * stlgeometry; public: - DLL_HEADER VisualSceneSTLGeometry (); - DLL_HEADER virtual ~VisualSceneSTLGeometry (); - void SetGeometry (class STLGeometry * astlgeometry) { stlgeometry = astlgeometry; } + VisualSceneSTLGeometry (); + virtual ~VisualSceneSTLGeometry (); + void SetGeometry (class STLGeometry * astlgeometry) { stlgeometry = astlgeometry; } - DLL_HEADER virtual void BuildScene (int zoomall = 0); - DLL_HEADER virtual void DrawScene (); + virtual void BuildScene (int zoomall = 0); + virtual void DrawScene (); }; - class VisualSceneSTLMeshing : public VisualScene + class DLL_HEADER VisualSceneSTLMeshing : public VisualScene { NgArray trilists; int selecttrig, nodeofseltrig; class STLGeometry * stlgeometry; public: - DLL_HEADER VisualSceneSTLMeshing (); - DLL_HEADER virtual ~VisualSceneSTLMeshing (); + VisualSceneSTLMeshing (); + virtual ~VisualSceneSTLMeshing (); void SetGeometry (class STLGeometry * astlgeometry) { stlgeometry = astlgeometry; } diff --git a/libsrc/visualization/CMakeLists.txt b/libsrc/visualization/CMakeLists.txt index a5604fb6..2a83c0e0 100644 --- a/libsrc/visualization/CMakeLists.txt +++ b/libsrc/visualization/CMakeLists.txt @@ -9,7 +9,7 @@ endif(USE_GUI) add_library(visual ${NG_LIB_TYPE} ${LIB_VISUAL_SOURCES}) -target_link_libraries( visual PUBLIC ngcore PRIVATE netgen_python ${MPI_CXX_LIBRARIES} ${OPENGL_LIBRARIES} ) +target_link_libraries( visual PUBLIC ngcore PRIVATE "$" ${MPI_CXX_LIBRARIES} ${OPENGL_LIBRARIES} ) install( TARGETS visual ${NG_INSTALL_DIR}) install(FILES diff --git a/libsrc/visualization/mvdraw.hpp b/libsrc/visualization/mvdraw.hpp index cae7c792..a39e3acd 100644 --- a/libsrc/visualization/mvdraw.hpp +++ b/libsrc/visualization/mvdraw.hpp @@ -209,6 +209,8 @@ namespace netgen void BuildBadelList(); void BuildIdentifiedList(); void BuildDomainSurfList(); + + bool Unproject (int px, int py, Point<3> &p); }; DLL_HEADER extern VisualSceneMesh vsmesh; diff --git a/libsrc/visualization/soldata.hpp b/libsrc/visualization/soldata.hpp index 74143fc0..6a5c5626 100644 --- a/libsrc/visualization/soldata.hpp +++ b/libsrc/visualization/soldata.hpp @@ -6,15 +6,6 @@ namespace netgen { using namespace std; - /* -#if defined __AVX512F__ - typedef __m512 tAVX; - typedef __m512d tAVXd; -#elif defined __AVX__ - typedef __m256 tAVX; - typedef __m256d tAVXd; -#endif - */ class SolutionData { @@ -101,17 +92,15 @@ namespace netgen return res; } -#ifdef __SSE__ virtual bool GetMultiSurfValue (size_t selnr, size_t facetnr, size_t npts, - const ngsimd::tAVXd * xref, - const ngsimd::tAVXd * x, - const ngsimd::tAVXd * dxdxref, - ngsimd::tAVXd * values) + const SIMD * xref, + const SIMD * x, + const SIMD * dxdxref, + SIMD * values) { cerr << "GetMultiSurfVaue not overloaded for SIMD" << endl; return false; } -#endif virtual bool GetSegmentValue (int segnr, double xref, double * values) { return false; } @@ -141,6 +130,7 @@ namespace netgen class DLL_HEADER UserVisualizationObject { public: + virtual ~UserVisualizationObject() { ; } virtual void Draw () = 0; }; diff --git a/libsrc/visualization/vsmesh.cpp b/libsrc/visualization/vsmesh.cpp index 10a56905..af7ea300 100644 --- a/libsrc/visualization/vsmesh.cpp +++ b/libsrc/visualization/vsmesh.cpp @@ -3128,9 +3128,7 @@ namespace netgen - - - void VisualSceneMesh :: MouseDblClick (int px, int py) + bool VisualSceneMesh :: Unproject (int px, int py, Point<3> &p) { shared_ptr mesh = GetMesh(); @@ -3150,20 +3148,63 @@ namespace netgen int hy = viewport[3]-py; GLfloat pz; + + if(lock) + { + lock->UnLock(); + delete lock; + lock = NULL; + } + // cout << "x, y = " << px << ", " << hy << endl; glReadPixels (px, hy, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &pz); + + if(pz>=1.0) + return false; + if(pz<=0.0) + return false; + // cout << "pz = " << pz << endl; gluUnProject(px, hy, pz, transformationmat, projection, viewport, &result[0], &result[1], &result[2]); - if (pz < 1.0) - cout << "point : " << result[0] << ", " << result[1] << ", " << result[2] << endl; - + p = Point<3>{result[0], result[1], result[2]}; + return true; + } - if (user_me_handler && pz < 1.0) + + void VisualSceneMesh :: MouseDblClick (int px, int py) + { + Point<3> p; + bool found_point = Unproject(px, py, p); + + if(selelement!=-1) { - if (selelement != -1) - user_me_handler -> DblClick (selelement-1, result[0], result[1], result[2]); + const Element2d & sel = GetMesh()->SurfaceElement(selelement); + + cout << "select element " << selelement + << " on face " << sel.GetIndex() << endl; + cout << "Nodes: "; + for (int i = 1; i <= sel.GetNP(); i++) + cout << sel.PNum(i) << " "; + cout << endl; + + cout << "selected point " << selpoint + << ", pos = " << GetMesh()->Point (selpoint) + << endl; + + cout << "seledge = " << seledge << endl; + + } + + if(found_point) + { + cout << "point : " << p << endl; + if (user_me_handler) + { + if (selelement != -1) + user_me_handler -> DblClick (selelement-1, p[0], p[1], p[2]); + } } selecttimestamp = NextTimeStamp(); @@ -3401,6 +3442,7 @@ namespace netgen if (vispar.clipping.enable) { + glEnable(GL_CLIP_PLANE0); Vec<3> n(clipplane[0], clipplane[1], clipplane[2]); double len = Abs(n); double mu = -clipplane[3] / (len*len); @@ -3473,23 +3515,12 @@ namespace netgen { const Element2d & sel = mesh->SurfaceElement(minname); - - cout << "select element " << minname - << " on face " << sel.GetIndex() << endl; - cout << "Nodes: "; - for (i = 1; i <= sel.GetNP(); i++) - cout << sel.PNum(i) << " "; - cout << endl; - selelement = minname; selface = mesh->SurfaceElement(minname).GetIndex(); locpi = (locpi % sel.GetNP()) + 1; selpoint2 = selpoint; selpoint = sel.PNum(locpi); - cout << "selected point " << selpoint - << ", pos = " << mesh->Point (selpoint) - << endl; for (i = 1; i <= mesh->GetNSeg(); i++) { @@ -3498,7 +3529,6 @@ namespace netgen (seg[1] == selpoint && seg[0] == selpoint2) ) { seledge = seg.edgenr; - cout << "seledge = " << seledge << endl; } } } diff --git a/libsrc/visualization/vssolution.cpp b/libsrc/visualization/vssolution.cpp index c39156ea..0af2f5ad 100644 --- a/libsrc/visualization/vssolution.cpp +++ b/libsrc/visualization/vssolution.cpp @@ -24,12 +24,12 @@ namespace netgen extern VisualSceneMesh vsmesh; - void AddUserVisualizationObject (UserVisualizationObject * vis) + DLL_HEADER void AddUserVisualizationObject (UserVisualizationObject * vis) { // vssolution.AddUserVisualizationObject (vis); GetVSSolution().AddUserVisualizationObject (vis); } - void DeleteUserVisualizationObject (UserVisualizationObject * vis) + DLL_HEADER void DeleteUserVisualizationObject (UserVisualizationObject * vis) { // vssolution.AddUserVisualizationObject (vis); GetVSSolution().DeleteUserVisualizationObject (vis); @@ -4754,9 +4754,166 @@ namespace netgen void VisualSceneSolution :: MouseDblClick (int px, int py) { - vsmesh.SetClippingPlane(); - // vsmesh.BuildFilledList(); - vsmesh.MouseDblClick(px,py); + auto mesh = GetMesh(); + auto dim = mesh->GetDimension(); + + auto formatComplex = [](double real, double imag) + { + return ToString(real) + (imag < 0 ? "" : "+") + ToString(imag) + "j"; + }; + + auto printScalValue = [&formatComplex] + (SolData & sol, int comp, double value, double imag=0., bool iscomplex=false) + { + if(sol.components>1) + { + if(comp==0) + cout << "func(" << sol.name << ")"; + else + cout << sol.name << "["+ToString(comp)+"]"; + } + else + cout << sol.name; + cout << " = " << (iscomplex ? formatComplex(value, imag) : ToString(value)) << endl; + }; + + auto printVecValue = [&formatComplex] + (SolData & sol, FlatArray values) + { + if(sol.iscomplex) + { + cout << sol.name << " = ( " << formatComplex(values[0], values[1]); + for(int i = 2; i < values.Size(); i+=2) + cout << ", " << formatComplex(values[i], values[i+1]); + cout << " )" << endl; + } + else + { + cout << sol.name << " = ( " << values[0]; + for(int i = 1; i < values.Size(); i++) + cout << ", " << values[i]; + cout << " )" << endl; + } + }; + + // Check if clipping plane is drawn at current mouse cursor position + if(dim==3 && clipsolution && vispar.clipping.enable) + { + GLint viewport[4]; + GLdouble projection[16]; + glGetDoublev(GL_PROJECTION_MATRIX, &projection[0]); + + glGetIntegerv(GL_VIEWPORT, &viewport[0]); + + int hy = viewport[3]-py; + + // manually intersect the view vector with the clipping plane (also working if clipping vectors are shown) + Point<3> p_clipping_plane; + gluUnProject(px, hy, 1.0, transformationmat, projection, viewport, + &p_clipping_plane[0], &p_clipping_plane[1], &p_clipping_plane[2]); + + Point<3> eye; + gluUnProject( (viewport[2]-viewport[0])/2 , (viewport[3]-viewport[1])/2, + 0.0, transformationmat, projection, viewport, &eye[0], &eye[1], &eye[2]); + + Vec<3> n{vispar.clipping.normal}; + n.Normalize(); + Vec<3> view = p_clipping_plane-eye; + + // check if we look at the clipping plane from the right direction + if(n*view > 1e-8) + { + double lam = vispar.clipping.dist - Vec<3>{eye}*n; + lam /= n*view; + p_clipping_plane = eye + lam*view; + + double lami[3]; + if(auto el3d = mesh->GetElementOfPoint( p_clipping_plane, lami )) + { + cout << endl << "Selected point " << p_clipping_plane << " on clipping plane" << endl; + + bool have_scal_func = scalfunction!=-1 && soldata[scalfunction]->draw_volume; + bool have_vec_func = vecfunction!=-1 && soldata[vecfunction]->draw_volume; + + if(have_scal_func) + { + auto & sol = *soldata[scalfunction]; + double val; + double imag = 0; + int rcomponent = scalcomp; + int comp = scalcomp; + if(sol.iscomplex && rcomponent != 0) + { + rcomponent = 2 * ((rcomponent-1)/2) + 1; + GetValue(&sol, el3d-1, lami[0], lami[1], lami[2], rcomponent+1, + imag); + comp = (scalcomp-1)/2 + 1; + } + GetValue(&sol, el3d-1, lami[0], lami[1], lami[2], rcomponent, val); + printScalValue(sol, comp, val, imag, sol.iscomplex && comp > 0); + } + if(vecfunction!=-1 && soldata[vecfunction]->draw_volume) + { + auto & sol = *soldata[vecfunction]; + ArrayMem values(sol.components); + GetValues(&sol, el3d-1, lami[0], lami[1], lami[2], &values[0]); + printVecValue(sol, values); + } + return; + } + } + } + + // no point on clipping plane found -> continue searching for surface element + + Point<3> p; + bool found_point = vsmesh.Unproject(px, py, p); + if(!found_point) + return; + + if(selelement==0) + return; + + double lami[3] = {0.0, 0.0, 0.0}; + // Check if unprojected Point is close to surface element (eps of 1e-3 due to z-Buffer accuracy) + bool found_2del = false; + if(mesh->PointContainedIn2DElement(p, lami, selelement, false && fabs(lami[2])<1e-3)) + { + // Found it, use coordinates of point projected to surface element + mesh->GetCurvedElements().CalcSurfaceTransformation({1.0-lami[0]-lami[1], lami[0]}, selelement-1, p); + found_2del = true; + } + cout << endl << "Selected point " << p << " on surface" << endl; + + if(!found_2del) + return; + + bool have_scal_func = scalfunction!=-1 && soldata[scalfunction]->draw_surface; + bool have_vec_func = vecfunction!=-1 && soldata[vecfunction]->draw_surface; + + if(have_scal_func) + { + auto & sol = *soldata[scalfunction]; + double val; + double imag = 0; + int rcomponent = scalcomp; + int comp = scalcomp; + if(sol.iscomplex && rcomponent != 0) + { + rcomponent = 2 * ((rcomponent-1)/2) + 1; + GetSurfValue(&sol, selelement-1, -1, 1.0-lami[0]-lami[1], lami[0], rcomponent+1, imag); + comp = (scalcomp-1)/2 + 1; + } + GetSurfValue(&sol, selelement-1, -1, 1.0-lami[0]-lami[1], lami[0], rcomponent, val); + printScalValue(sol, comp, val, imag, sol.iscomplex && comp > 0); + } + if(have_vec_func) + { + auto & sol = *soldata[vecfunction]; + ArrayMem values(sol.components); + GetSurfValues(&sol, selelement-1, -1, 1.0-lami[0]-lami[1], lami[0], &values[0]); + printVecValue(sol, values); + } } diff --git a/ng/CMakeLists.txt b/ng/CMakeLists.txt index 67f42ea0..235ad46c 100644 --- a/ng/CMakeLists.txt +++ b/ng/CMakeLists.txt @@ -4,13 +4,11 @@ else() add_definitions(-DINTERNAL_TCL_DEFAULT=0) endif() -set(netgen_sources ngappinit.cpp onetcl.cpp) if(WIN32) - # add icon to netgen executable - enable_language(RC) - set(netgen_sources ${netgen_sources} ../windows/netgen.rc) - # Don't use ccache here due to incompatibility with the resource compiler - set_directory_properties(PROPERTIES RULE_LAUNCH_COMPILE "") + # add icon and version info to netgen executable + enable_language(RC) + # Don't use ccache here due to incompatibility with the resource compiler + set_directory_properties(PROPERTIES RULE_LAUNCH_COMPILE "") endif(WIN32) if(USE_GUI) @@ -22,7 +20,10 @@ if(USE_GUI) ../libsrc/occ/occpkg.cpp ../libsrc/occ/vsocc.cpp ) - add_executable(netgen ngappinit.cpp onetcl.cpp) + add_executable(netgen ngappinit.cpp) + if(WIN32) + target_sources(netgen PRIVATE ../windows/netgen.rc) + endif(WIN32) target_link_libraries( gui PUBLIC nglib ) target_link_libraries( gui PRIVATE ${LIBTOGL} ${ZLIB_LIBRARIES} ${JPEG_LIBRARIES} ${FFMPEG_LIBRARIES} ${X11_Xmu_LIB} ${X11_X11_LIB} ${OCC_LIBRARIES} ) @@ -46,7 +47,7 @@ if(USE_GUI) if(WIN32) set_target_properties( gui PROPERTIES OUTPUT_NAME libgui ) endif(WIN32) - target_link_libraries( gui PRIVATE netgen_python ) + target_link_libraries( gui PRIVATE "$" ) endif(USE_GUI) @@ -60,7 +61,7 @@ if(USE_PYTHON) endif() add_library(ngpy SHARED netgenpy.cpp) - target_link_libraries( ngpy PUBLIC nglib PRIVATE netgen_python ) + target_link_libraries( ngpy PUBLIC nglib PRIVATE "$" ) if(APPLE) set_target_properties( ngpy PROPERTIES SUFFIX ".so") elseif(WIN32) diff --git a/ng/dialog.tcl b/ng/dialog.tcl index 8c0bf730..1d7a65c2 100644 --- a/ng/dialog.tcl +++ b/ng/dialog.tcl @@ -1436,7 +1436,7 @@ proc viewingoptionsdialog { } { #pack $f.f1 -pady 5 -anchor center ttk::label $f.center.lab1 -text "SpecPoint Veclen" ttk::entry $f.center.ent1 -width 5 -textvariable viewoptions.specpointvlen -validate focus \ - -validatecommand "my_validate %W 0 1e9 %P 1" \ + -validatecommand "my_validate %W 0 1e9 %P 4" \ -invalidcommand "my_invalid %W" grid $f.center.ent1 $f.center.lab1 -sticky nw -padx 4 diff --git a/ng/gui.cpp b/ng/gui.cpp index 680a3417..49e970bc 100644 --- a/ng/gui.cpp +++ b/ng/gui.cpp @@ -1,29 +1,21 @@ #include #include #include - -#ifdef WIN32 - #define DLL_HEADER_IMPORT __declspec(dllimport) - #define DLL_HEADER_EXPORT __declspec(dllexport) -#else - #define DLL_HEADER_IMPORT - #define DLL_HEADER_EXPORT -#endif - +#include namespace netgen { - DLL_HEADER_EXPORT Flags parameters; + NGCORE_API_EXPORT Flags parameters; } -DLL_HEADER_EXPORT bool nodisplay = false; +NGCORE_API_EXPORT bool nodisplay = false; extern "C" int Ng_Init (Tcl_Interp * interp); extern "C" int Ng_Vis_Init (Tcl_Interp * interp); extern "C" void Ng_TclCmd(string); // tcl package dynamic load -extern "C" int DLL_HEADER_EXPORT Gui_Init (Tcl_Interp * interp) +extern "C" int NGCORE_API_EXPORT Gui_Init (Tcl_Interp * interp) { if (Ng_Init(interp) == TCL_ERROR) { cerr << "Problem in Ng_Init: " << endl; diff --git a/ng/menustat.tcl b/ng/menustat.tcl index 6f870a81..329d59bd 100644 --- a/ng/menustat.tcl +++ b/ng/menustat.tcl @@ -233,6 +233,7 @@ loadmeshinifile; {"TET format" {.tet} } {"STL format" {.stl .stlb} } {"Pro/ENGINEER neutral format" {.fnf} } + {"CFD General Notation System" {.cgns} } } set file [tk_getOpenFile -filetypes $types ] if {$file != ""} { diff --git a/ng/netgenpy.cpp b/ng/netgenpy.cpp index 2b68e408..08aca5db 100644 --- a/ng/netgenpy.cpp +++ b/ng/netgenpy.cpp @@ -2,27 +2,21 @@ #include #include <../general/ngpython.hpp> +#include -#ifdef WIN32 -#define DLL_HEADER __declspec(dllimport) -#else -#define DLL_HEADER -#endif - - -void DLL_HEADER ExportNetgenMeshing(py::module &m); -void DLL_HEADER ExportMeshVis(py::module &m); -void DLL_HEADER ExportCSG(py::module &m); -void DLL_HEADER ExportCSGVis(py::module &m); -void DLL_HEADER ExportGeom2d(py::module &m); -void DLL_HEADER ExportSTL(py::module &m); -void DLL_HEADER ExportSTLVis(py::module &m); +void NGCORE_API_IMPORT ExportNetgenMeshing(py::module &m); +void NGCORE_API_IMPORT ExportMeshVis(py::module &m); +void NGCORE_API_IMPORT ExportCSG(py::module &m); +void NGCORE_API_IMPORT ExportCSGVis(py::module &m); +void NGCORE_API_IMPORT ExportGeom2d(py::module &m); +void NGCORE_API_IMPORT ExportSTL(py::module &m); +void NGCORE_API_IMPORT ExportSTLVis(py::module &m); #ifdef OCCGEOMETRY -void DLL_HEADER ExportNgOCC(py::module &m); +void NGCORE_API_IMPORT ExportNgOCC(py::module &m); #endif // OCCGEOMETRY namespace netgen { - std::vector DLL_HEADER Snapshot( int w, int h ); + std::vector NGCORE_API_IMPORT Snapshot( int w, int h ); } PYBIND11_MODULE(libngpy, ngpy) diff --git a/ng/ngappinit.cpp b/ng/ngappinit.cpp index 7480ef5a..1e66f89d 100644 --- a/ng/ngappinit.cpp +++ b/ng/ngappinit.cpp @@ -11,10 +11,6 @@ #include extern void ParallelRun(); -namespace netgen -{ - MPI_Comm mesh_comm; -} #endif #include "../libsrc/interface/writeuser.hpp" @@ -82,7 +78,7 @@ int main(int argc, char ** argv) if ( netgen::id == 0 ) { - cout << "NETGEN-" << PACKAGE_VERSION << endl; + cout << "NETGEN-" << netgen::netgen_version << endl; cout << "Developed by Joachim Schoeberl at" << endl << "2010-xxxx Vienna University of Technology" << endl @@ -206,7 +202,7 @@ int main(int argc, char ** argv) cout << "using internal Tcl-script" << endl; // connect to one string - extern const char * ngscript[]; + DLL_HEADER extern const char * ngscript[]; const char ** hcp = ngscript; int len = 0; while (*hcp) diff --git a/ng/ngpkg.cpp b/ng/ngpkg.cpp index afd4d0e1..52279b19 100644 --- a/ng/ngpkg.cpp +++ b/ng/ngpkg.cpp @@ -1155,7 +1155,7 @@ namespace netgen // Use an array to support creation of boundary // layers for multiple surfaces in the future... - NgArray surfid; + Array surfid; int surfinp = 0; int prismlayers = 1; double hfirst = 0.01; @@ -1172,8 +1172,8 @@ namespace netgen cout << "Number of surfaces entered = " << surfid.Size() << endl; cout << "Selected surfaces are:" << endl; - for(int i = 1; i <= surfid.Size(); i++) - cout << "Surface " << i << ": " << surfid.Elem(i) << endl; + for(auto i : Range(surfid)) + cout << "Surface " << i << ": " << surfid[i] << endl; cout << endl << "Enter number of prism layers: "; cin >> prismlayers; @@ -1189,9 +1189,14 @@ namespace netgen BoundaryLayerParameters blp; blp.surfid = surfid; - blp.prismlayers = prismlayers; - blp.hfirst = blp.hfirst; - blp.growthfactor = growthfactor; + for(auto i : Range(prismlayers)) + { + auto layer = i+1; + if(growthfactor == 1) + blp.heights.Append(layer * hfirst); + else + blp.heights.Append(hfirst * (pow(growthfactor, (layer+1))-1)/(growthfactor-1)); + } GenerateBoundaryLayer (*mesh, blp); return TCL_OK; } diff --git a/ng/onetcl.cpp b/ng/onetcl.cpp index 8e3513c5..c104b543 100644 --- a/ng/onetcl.cpp +++ b/ng/onetcl.cpp @@ -1,4 +1,5 @@ -const char * ngscript[] = {"" +#include +DLL_HEADER const char * ngscript[] = {"" ,"catch {lappend auto_path $env(NETGENDIR) }\n" ,"catch {lappend auto_path $env(NETGENDIR)/../lib }\n" ,"if {[catch {Ng_GetCommandLineParameter batchmode} result ]} {\n" @@ -867,6 +868,7 @@ const char * ngscript[] = {"" ,"{\"TET format\" {.tet} }\n" ,"{\"STL format\" {.stl .stlb} }\n" ,"{\"Pro/ENGINEER neutral format\" {.fnf} }\n" +,"{\"CFD General Notation System\" {.cgns} }\n" ,"}\n" ,"set file [tk_getOpenFile -filetypes $types ]\n" ,"if {$file != \"\"} {\n" @@ -2314,7 +2316,7 @@ const char * ngscript[] = {"" ,"grid $f.center.ent $f.center.btn -sticky nw -padx 4\n" ,"ttk::label $f.center.lab1 -text \"SpecPoint Veclen\"\n" ,"ttk::entry $f.center.ent1 -width 5 -textvariable viewoptions.specpointvlen -validate focus \\\n" -,"-validatecommand \"my_validate %W 0 1e9 %P 1\" \\\n" +,"-validatecommand \"my_validate %W 0 1e9 %P 4\" \\\n" ,"-invalidcommand \"my_invalid %W\"\n" ,"grid $f.center.ent1 $f.center.lab1 -sticky nw -padx 4\n" ,"set f $w.nb.misc\n" diff --git a/ng/onetcl.py b/ng/onetcl.py index dfdedbd8..d8559855 100644 --- a/ng/onetcl.py +++ b/ng/onetcl.py @@ -19,7 +19,8 @@ for f in fnames: # write a cpp file containing the result of ng.tcl onetclcpp = open("onetcl.cpp",'w') -onetclcpp.write('const char * ngscript[] = {""'+'\n'); +onetclcpp.write('#include \n'); +onetclcpp.write('DLL_HEADER const char * ngscript[] = {""'+'\n'); # make sure to remove comments (and if lines with comments end with '\' also the next line(s) ) skip_next = False # flag to indicate that the next line should be removed diff --git a/nglib/CMakeLists.txt b/nglib/CMakeLists.txt index 8fa7444b..a7765e59 100644 --- a/nglib/CMakeLists.txt +++ b/nglib/CMakeLists.txt @@ -31,7 +31,7 @@ endif(NOT WIN32) # target_link_libraries(nglib PRIVATE gen la gprim PUBLIC ngcore) target_link_libraries(nglib PUBLIC ngcore) -target_link_libraries( nglib PRIVATE ${OCC_LIBRARIES} ${MPI_CXX_LIBRARIES} ${OPENGL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${X11_Xmu_LIB} ${JPEG_LIBRARIES} ${MKL_LIBRARIES} ${ZLIB_LIBRARIES} ${OCC_LIBRARIES} ) +target_link_libraries( nglib PRIVATE ${OCC_LIBRARIES} ${MPI_CXX_LIBRARIES} ${OPENGL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${X11_Xmu_LIB} ${JPEG_LIBRARIES} ${MKL_LIBRARIES} ${ZLIB_LIBRARIES} ${OCC_LIBRARIES} netgen_cgns ) if(USE_OCC AND NOT WIN32) target_link_libraries(nglib PUBLIC occ) diff --git a/nglib/nglib.cpp b/nglib/nglib.cpp index bcff69d0..9eb1e8c3 100644 --- a/nglib/nglib.cpp +++ b/nglib/nglib.cpp @@ -43,11 +43,6 @@ namespace netgen { #ifdef PARALLEL #include -namespace netgen -{ - // int id = 0, ntasks = 1; - MPI_Comm mesh_comm; -} #endif @@ -83,7 +78,7 @@ namespace nglib // initialize, deconstruct Netgen library: - DLL_HEADER void Ng_Init () + NGLIB_API void Ng_Init () { mycout = &cout; myerr = &cerr; @@ -95,7 +90,7 @@ namespace nglib // Clean-up functions before ending usage of nglib - DLL_HEADER void Ng_Exit () + NGLIB_API void Ng_Exit () { ; } @@ -104,7 +99,7 @@ namespace nglib // Create a new netgen mesh object - DLL_HEADER Ng_Mesh * Ng_NewMesh () + NGLIB_API Ng_Mesh * Ng_NewMesh () { Mesh * mesh = new Mesh; mesh->AddFaceDescriptor (FaceDescriptor (1, 1, 0, 1)); @@ -115,7 +110,7 @@ namespace nglib // Delete an existing netgen mesh object - DLL_HEADER void Ng_DeleteMesh (Ng_Mesh * mesh) + NGLIB_API void Ng_DeleteMesh (Ng_Mesh * mesh) { if(mesh != NULL) { @@ -134,7 +129,7 @@ namespace nglib // Save a netgen mesh in the native VOL format - DLL_HEADER void Ng_SaveMesh(Ng_Mesh * mesh, const char* filename) + NGLIB_API void Ng_SaveMesh(Ng_Mesh * mesh, const char* filename) { ((Mesh*)mesh)->Save(filename); } @@ -143,7 +138,7 @@ namespace nglib // Load a netgen native VOL mesh from a given file - DLL_HEADER Ng_Mesh * Ng_LoadMesh(const char* filename) + NGLIB_API Ng_Mesh * Ng_LoadMesh(const char* filename) { Mesh * mesh = new Mesh; mesh->Load(filename); @@ -154,7 +149,7 @@ namespace nglib // Merge another mesh file into the currently loaded one - DLL_HEADER Ng_Result Ng_MergeMesh( Ng_Mesh* mesh, const char* filename) + NGLIB_API Ng_Result Ng_MergeMesh( Ng_Mesh* mesh, const char* filename) { Ng_Result status = NG_OK; @@ -195,7 +190,7 @@ namespace nglib // Merge another mesh file into the currently loaded one - DLL_HEADER Ng_Result Ng_MergeMesh( Ng_Mesh* mesh1, Ng_Mesh* mesh2) + NGLIB_API Ng_Result Ng_MergeMesh( Ng_Mesh* mesh1, Ng_Mesh* mesh2) { return NG_ERROR; } @@ -204,7 +199,7 @@ namespace nglib // Manually add a point to an existing mesh object - DLL_HEADER void Ng_AddPoint (Ng_Mesh * mesh, double * x) + NGLIB_API void Ng_AddPoint (Ng_Mesh * mesh, double * x) { Mesh * m = (Mesh*)mesh; m->AddPoint (Point3d (x[0], x[1], x[2])); @@ -214,7 +209,7 @@ namespace nglib // Manually add a surface element of a given type to an existing mesh object - DLL_HEADER void Ng_AddSurfaceElement (Ng_Mesh * mesh, Ng_Surface_Element_Type et, + NGLIB_API void Ng_AddSurfaceElement (Ng_Mesh * mesh, Ng_Surface_Element_Type et, int * pi) { Mesh * m = (Mesh*)mesh; @@ -230,7 +225,7 @@ namespace nglib // Manually add a volume element of a given type to an existing mesh object - DLL_HEADER void Ng_AddVolumeElement (Ng_Mesh * mesh, Ng_Volume_Element_Type et, + NGLIB_API void Ng_AddVolumeElement (Ng_Mesh * mesh, Ng_Volume_Element_Type et, int * pi) { Mesh * m = (Mesh*)mesh; @@ -247,7 +242,7 @@ namespace nglib // Obtain the number of points in the mesh - DLL_HEADER int Ng_GetNP (Ng_Mesh * mesh) + NGLIB_API int Ng_GetNP (Ng_Mesh * mesh) { return ((Mesh*)mesh) -> GetNP(); } @@ -256,7 +251,7 @@ namespace nglib // Obtain the number of surface elements in the mesh - DLL_HEADER int Ng_GetNSE (Ng_Mesh * mesh) + NGLIB_API int Ng_GetNSE (Ng_Mesh * mesh) { return ((Mesh*)mesh) -> GetNSE(); } @@ -265,7 +260,7 @@ namespace nglib // Obtain the number of volume elements in the mesh - DLL_HEADER int Ng_GetNE (Ng_Mesh * mesh) + NGLIB_API int Ng_GetNE (Ng_Mesh * mesh) { return ((Mesh*)mesh) -> GetNE(); } @@ -274,7 +269,7 @@ namespace nglib // Return point coordinates of a given point index in the mesh - DLL_HEADER void Ng_GetPoint (Ng_Mesh * mesh, int num, double * x) + NGLIB_API void Ng_GetPoint (Ng_Mesh * mesh, int num, double * x) { const Point3d & p = ((Mesh*)mesh)->Point(num); x[0] = p.X(); @@ -286,7 +281,7 @@ namespace nglib // Return the surface element at a given index "pi" - DLL_HEADER Ng_Surface_Element_Type + NGLIB_API Ng_Surface_Element_Type Ng_GetSurfaceElement (Ng_Mesh * mesh, int num, int * pi) { const Element2d & el = ((Mesh*)mesh)->SurfaceElement(num); @@ -317,7 +312,7 @@ namespace nglib // Return the volume element at a given index "pi" - DLL_HEADER Ng_Volume_Element_Type + NGLIB_API Ng_Volume_Element_Type Ng_GetVolumeElement (Ng_Mesh * mesh, int num, int * pi) { const Element & el = ((Mesh*)mesh)->VolumeElement(num); @@ -340,7 +335,7 @@ namespace nglib // Set a global limit on the maximum mesh size allowed - DLL_HEADER void Ng_RestrictMeshSizeGlobal (Ng_Mesh * mesh, double h) + NGLIB_API void Ng_RestrictMeshSizeGlobal (Ng_Mesh * mesh, double h) { ((Mesh*)mesh) -> SetGlobalH (h); } @@ -349,7 +344,7 @@ namespace nglib // Set a local limit on the maximum mesh size allowed around the given point - DLL_HEADER void Ng_RestrictMeshSizePoint (Ng_Mesh * mesh, double * p, double h) + NGLIB_API void Ng_RestrictMeshSizePoint (Ng_Mesh * mesh, double * p, double h) { ((Mesh*)mesh) -> RestrictLocalH (Point3d (p[0], p[1], p[2]), h); } @@ -358,7 +353,7 @@ namespace nglib // Set a local limit on the maximum mesh size allowed within a given box region - DLL_HEADER void Ng_RestrictMeshSizeBox (Ng_Mesh * mesh, double * pmin, double * pmax, double h) + NGLIB_API void Ng_RestrictMeshSizeBox (Ng_Mesh * mesh, double * pmin, double * pmax, double h) { for (double x = pmin[0]; x < pmax[0]; x += h) for (double y = pmin[1]; y < pmax[1]; y += h) @@ -370,7 +365,7 @@ namespace nglib // Generates volume mesh from an existing surface mesh - DLL_HEADER Ng_Result Ng_GenerateVolumeMesh (Ng_Mesh * mesh, Ng_Meshing_Parameters * mp) + NGLIB_API Ng_Result Ng_GenerateVolumeMesh (Ng_Mesh * mesh, Ng_Meshing_Parameters * mp) { Mesh * m = (Mesh*)mesh; @@ -393,7 +388,7 @@ namespace nglib /* ------------------ 2D Meshing Functions ------------------------- */ - DLL_HEADER void Ng_AddPoint_2D (Ng_Mesh * mesh, double * x) + NGLIB_API void Ng_AddPoint_2D (Ng_Mesh * mesh, double * x) { Mesh * m = (Mesh*)mesh; @@ -403,7 +398,7 @@ namespace nglib - DLL_HEADER void Ng_AddBoundarySeg_2D (Ng_Mesh * mesh, int pi1, int pi2) + NGLIB_API void Ng_AddBoundarySeg_2D (Ng_Mesh * mesh, int pi1, int pi2) { Mesh * m = (Mesh*)mesh; @@ -416,7 +411,7 @@ namespace nglib - DLL_HEADER int Ng_GetNP_2D (Ng_Mesh * mesh) + NGLIB_API int Ng_GetNP_2D (Ng_Mesh * mesh) { Mesh * m = (Mesh*)mesh; return m->GetNP(); @@ -425,7 +420,7 @@ namespace nglib - DLL_HEADER int Ng_GetNE_2D (Ng_Mesh * mesh) + NGLIB_API int Ng_GetNE_2D (Ng_Mesh * mesh) { Mesh * m = (Mesh*)mesh; return m->GetNSE(); @@ -434,7 +429,7 @@ namespace nglib - DLL_HEADER int Ng_GetNSeg_2D (Ng_Mesh * mesh) + NGLIB_API int Ng_GetNSeg_2D (Ng_Mesh * mesh) { Mesh * m = (Mesh*)mesh; return m->GetNSeg(); @@ -443,7 +438,7 @@ namespace nglib - DLL_HEADER void Ng_GetPoint_2D (Ng_Mesh * mesh, int num, double * x) + NGLIB_API void Ng_GetPoint_2D (Ng_Mesh * mesh, int num, double * x) { Mesh * m = (Mesh*)mesh; @@ -455,7 +450,7 @@ namespace nglib - DLL_HEADER Ng_Surface_Element_Type + NGLIB_API Ng_Surface_Element_Type Ng_GetElement_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum) { const Element2d & el = ((Mesh*)mesh)->SurfaceElement(num); @@ -490,7 +485,7 @@ namespace nglib - DLL_HEADER void Ng_GetSegment_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum) + NGLIB_API void Ng_GetSegment_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum) { const Segment & seg = ((Mesh*)mesh)->LineSegment(num); pi[0] = seg[0]; @@ -503,7 +498,7 @@ namespace nglib - DLL_HEADER Ng_Geometry_2D * Ng_LoadGeometry_2D (const char * filename) + NGLIB_API Ng_Geometry_2D * Ng_LoadGeometry_2D (const char * filename) { SplineGeometry2d * geom = new SplineGeometry2d(); geom -> Load (filename); @@ -511,7 +506,7 @@ namespace nglib } - DLL_HEADER Ng_Result Ng_GenerateMesh_2D (Ng_Geometry_2D * geom, + NGLIB_API Ng_Result Ng_GenerateMesh_2D (Ng_Geometry_2D * geom, Ng_Mesh ** mesh, Ng_Meshing_Parameters * mp) { @@ -532,7 +527,7 @@ namespace nglib - DLL_HEADER void Ng_HP_Refinement (Ng_Geometry_2D * geom, + NGLIB_API void Ng_HP_Refinement (Ng_Geometry_2D * geom, Ng_Mesh * mesh, int levels) { @@ -543,7 +538,7 @@ namespace nglib - DLL_HEADER void Ng_HP_Refinement (Ng_Geometry_2D * geom, + NGLIB_API void Ng_HP_Refinement (Ng_Geometry_2D * geom, Ng_Mesh * mesh, int levels, double parameter) { @@ -558,7 +553,7 @@ namespace nglib NgArray > readedges; //only before init stlgeometry // loads geometry from STL file - DLL_HEADER Ng_STL_Geometry * Ng_STL_LoadGeometry (const char * filename, int binary) + NGLIB_API Ng_STL_Geometry * Ng_STL_LoadGeometry (const char * filename, int binary) { int i; STLGeometry geom; @@ -608,7 +603,7 @@ namespace nglib // generate new STL Geometry - DLL_HEADER Ng_STL_Geometry * Ng_STL_NewGeometry () + NGLIB_API Ng_STL_Geometry * Ng_STL_NewGeometry () { return (Ng_STL_Geometry*)(void*)new STLGeometry; } @@ -617,7 +612,7 @@ namespace nglib // after adding triangles (and edges) initialize - DLL_HEADER Ng_Result Ng_STL_InitSTLGeometry (Ng_STL_Geometry * geom) + NGLIB_API Ng_Result Ng_STL_InitSTLGeometry (Ng_STL_Geometry * geom) { STLGeometry* geo = (STLGeometry*)geom; geo->InitSTLGeometry(readtrias); @@ -642,7 +637,7 @@ namespace nglib // automatically generates edges: - DLL_HEADER Ng_Result Ng_STL_MakeEdges (Ng_STL_Geometry * geom, + NGLIB_API Ng_Result Ng_STL_MakeEdges (Ng_STL_Geometry * geom, Ng_Mesh* mesh, Ng_Meshing_Parameters * mp) { @@ -688,7 +683,7 @@ namespace nglib // generates mesh, empty mesh be already created. - DLL_HEADER Ng_Result Ng_STL_GenerateSurfaceMesh (Ng_STL_Geometry * geom, + NGLIB_API Ng_Result Ng_STL_GenerateSurfaceMesh (Ng_STL_Geometry * geom, Ng_Mesh* mesh, Ng_Meshing_Parameters * mp) { @@ -750,7 +745,7 @@ namespace nglib // fills STL Geometry // positive orientation // normal vector may be null-pointer - DLL_HEADER void Ng_STL_AddTriangle (Ng_STL_Geometry * geom, + NGLIB_API void Ng_STL_AddTriangle (Ng_STL_Geometry * geom, double * p1, double * p2, double * p3, double * nv) { @@ -769,7 +764,7 @@ namespace nglib } // add (optional) edges: - DLL_HEADER void Ng_STL_AddEdge (Ng_STL_Geometry * geom, + NGLIB_API void Ng_STL_AddEdge (Ng_STL_Geometry * geom, double * p1, double * p2) { readedges.Append(Point3d(p1[0],p1[1],p1[2])); @@ -782,7 +777,7 @@ namespace nglib #ifdef OCCGEOMETRY // --------------------- OCC Geometry / Meshing Utility Functions ------------------- // Create new OCC Geometry Object - DLL_HEADER Ng_OCC_Geometry * Ng_OCC_NewGeometry () + NGLIB_API Ng_OCC_Geometry * Ng_OCC_NewGeometry () { return (Ng_OCC_Geometry*)(void*)new OCCGeometry; } @@ -791,7 +786,7 @@ namespace nglib // Delete the OCC Geometry Object - DLL_HEADER Ng_Result Ng_OCC_DeleteGeometry(Ng_OCC_Geometry * geom) + NGLIB_API Ng_Result Ng_OCC_DeleteGeometry(Ng_OCC_Geometry * geom) { if (geom != NULL) { @@ -807,7 +802,7 @@ namespace nglib // Loads geometry from STEP File - DLL_HEADER Ng_OCC_Geometry * Ng_OCC_Load_STEP (const char * filename) + NGLIB_API Ng_OCC_Geometry * Ng_OCC_Load_STEP (const char * filename) { // Call the STEP File Load function. Note.. the geometry class // is created and instantiated within the load function @@ -820,7 +815,7 @@ namespace nglib // Loads geometry from IGES File - DLL_HEADER Ng_OCC_Geometry * Ng_OCC_Load_IGES (const char * filename) + NGLIB_API Ng_OCC_Geometry * Ng_OCC_Load_IGES (const char * filename) { // Call the IGES File Load function. Note.. the geometry class // is created and instantiated within the load function @@ -833,7 +828,7 @@ namespace nglib // Loads geometry from BREP File - DLL_HEADER Ng_OCC_Geometry * Ng_OCC_Load_BREP (const char * filename) + NGLIB_API Ng_OCC_Geometry * Ng_OCC_Load_BREP (const char * filename) { // Call the BREP File Load function. Note.. the geometry class // is created and instantiated within the load function @@ -847,7 +842,7 @@ namespace nglib // Locally limit the size of the mesh to be generated at various points // based on the topology of the geometry - DLL_HEADER Ng_Result Ng_OCC_SetLocalMeshSize (Ng_OCC_Geometry * geom, + NGLIB_API Ng_Result Ng_OCC_SetLocalMeshSize (Ng_OCC_Geometry * geom, Ng_Mesh * mesh, Ng_Meshing_Parameters * mp) { @@ -875,7 +870,7 @@ namespace nglib // Mesh the edges and add Face descriptors to prepare for surface meshing - DLL_HEADER Ng_Result Ng_OCC_GenerateEdgeMesh (Ng_OCC_Geometry * geom, + NGLIB_API Ng_Result Ng_OCC_GenerateEdgeMesh (Ng_OCC_Geometry * geom, Ng_Mesh * mesh, Ng_Meshing_Parameters * mp) { @@ -901,7 +896,7 @@ namespace nglib // Mesh the edges and add Face descriptors to prepare for surface meshing - DLL_HEADER Ng_Result Ng_OCC_GenerateSurfaceMesh (Ng_OCC_Geometry * geom, + NGLIB_API Ng_Result Ng_OCC_GenerateSurfaceMesh (Ng_OCC_Geometry * geom, Ng_Mesh * mesh, Ng_Meshing_Parameters * mp) { @@ -951,7 +946,7 @@ namespace nglib // Extract the face map from the OCC geometry // The face map basically gives an index to each face in the geometry, // which can be used to access a specific face - DLL_HEADER Ng_Result Ng_OCC_GetFMap(Ng_OCC_Geometry * geom, + NGLIB_API Ng_Result Ng_OCC_GetFMap(Ng_OCC_Geometry * geom, Ng_OCC_TopTools_IndexedMapOfShape * FMap) { OCCGeometry* occgeom = (OCCGeometry*)geom; @@ -978,7 +973,7 @@ namespace nglib // ------------------ Begin - Meshing Parameters related functions ------------------ // Constructor for the local nglib meshing parameters class - DLL_HEADER Ng_Meshing_Parameters :: Ng_Meshing_Parameters() + NGLIB_API Ng_Meshing_Parameters :: Ng_Meshing_Parameters() { uselocalh = 1; @@ -1019,7 +1014,7 @@ namespace nglib // Reset the local meshing parameters to the default values - DLL_HEADER void Ng_Meshing_Parameters :: Reset_Parameters() + NGLIB_API void Ng_Meshing_Parameters :: Reset_Parameters() { uselocalh = 1; @@ -1060,7 +1055,7 @@ namespace nglib // - DLL_HEADER void Ng_Meshing_Parameters :: Transfer_Parameters() + NGLIB_API void Ng_Meshing_Parameters :: Transfer_Parameters() { mparam.uselocalh = uselocalh; @@ -1093,7 +1088,7 @@ namespace nglib // ------------------ Begin - Second Order Mesh generation functions ---------------- - DLL_HEADER void Ng_Generate_SecondOrder(Ng_Mesh * mesh) + NGLIB_API void Ng_Generate_SecondOrder(Ng_Mesh * mesh) { Refinement ref(*((Mesh*) mesh)->GetGeometry()); ref.MakeSecondOrder(*(Mesh*) mesh); @@ -1102,7 +1097,7 @@ namespace nglib - DLL_HEADER void Ng_2D_Generate_SecondOrder(Ng_Geometry_2D * geom, + NGLIB_API void Ng_2D_Generate_SecondOrder(Ng_Geometry_2D * geom, Ng_Mesh * mesh) { ( (SplineGeometry2d*)geom ) -> GetRefinement().MakeSecondOrder( * (Mesh*) mesh ); @@ -1111,7 +1106,7 @@ namespace nglib - DLL_HEADER void Ng_STL_Generate_SecondOrder(Ng_STL_Geometry * geom, + NGLIB_API void Ng_STL_Generate_SecondOrder(Ng_STL_Geometry * geom, Ng_Mesh * mesh) { ((STLGeometry*)geom)->GetRefinement().MakeSecondOrder(*(Mesh*) mesh); @@ -1120,7 +1115,7 @@ namespace nglib - DLL_HEADER void Ng_CSG_Generate_SecondOrder (Ng_CSG_Geometry * geom, + NGLIB_API void Ng_CSG_Generate_SecondOrder (Ng_CSG_Geometry * geom, Ng_Mesh * mesh) { ((CSGeometry*)geom)->GetRefinement().MakeSecondOrder(*(Mesh*) mesh); @@ -1130,7 +1125,7 @@ namespace nglib #ifdef OCCGEOMETRY - DLL_HEADER void Ng_OCC_Generate_SecondOrder (Ng_OCC_Geometry * geom, + NGLIB_API void Ng_OCC_Generate_SecondOrder (Ng_OCC_Geometry * geom, Ng_Mesh * mesh) { ((OCCGeometry*)geom )->GetRefinement().MakeSecondOrder(*(Mesh*) mesh); @@ -1142,7 +1137,7 @@ namespace nglib // ------------------ Begin - Uniform Mesh Refinement functions --------------------- - DLL_HEADER void Ng_Uniform_Refinement (Ng_Mesh * mesh) + NGLIB_API void Ng_Uniform_Refinement (Ng_Mesh * mesh) { Refinement ref(*((Mesh*)mesh)->GetGeometry()); ref.Refine ( * (Mesh*) mesh ); @@ -1151,7 +1146,7 @@ namespace nglib - DLL_HEADER void Ng_2D_Uniform_Refinement (Ng_Geometry_2D * geom, + NGLIB_API void Ng_2D_Uniform_Refinement (Ng_Geometry_2D * geom, Ng_Mesh * mesh) { ( (SplineGeometry2d*)geom ) -> GetRefinement().Refine ( * (Mesh*) mesh ); @@ -1160,7 +1155,7 @@ namespace nglib - DLL_HEADER void Ng_STL_Uniform_Refinement (Ng_STL_Geometry * geom, + NGLIB_API void Ng_STL_Uniform_Refinement (Ng_STL_Geometry * geom, Ng_Mesh * mesh) { ( (STLGeometry*)geom ) -> GetRefinement().Refine ( * (Mesh*) mesh ); @@ -1169,7 +1164,7 @@ namespace nglib - DLL_HEADER void Ng_CSG_Uniform_Refinement (Ng_CSG_Geometry * geom, + NGLIB_API void Ng_CSG_Uniform_Refinement (Ng_CSG_Geometry * geom, Ng_Mesh * mesh) { ( (CSGeometry*)geom ) -> GetRefinement().Refine ( * (Mesh*) mesh ); @@ -1179,7 +1174,7 @@ namespace nglib #ifdef OCCGEOMETRY - DLL_HEADER void Ng_OCC_Uniform_Refinement (Ng_OCC_Geometry * geom, + NGLIB_API void Ng_OCC_Uniform_Refinement (Ng_OCC_Geometry * geom, Ng_Mesh * mesh) { ( (OCCGeometry*)geom ) -> GetRefinement().Refine ( * (Mesh*) mesh ); @@ -1196,7 +1191,7 @@ namespace netgen { char geomfilename[255]; - DLL_HEADER void MyError2 (const char * ch) + NGLIB_API void MyError2 (const char * ch) { cerr << ch; } @@ -1205,7 +1200,7 @@ namespace netgen //Destination for messages, errors, ... - DLL_HEADER void Ng_PrintDest2(const char * s) + NGLIB_API void Ng_PrintDest2(const char * s) { #ifdef PARALLEL int id = 0; @@ -1217,7 +1212,7 @@ namespace netgen /* - DLL_HEADER double GetTime () + NGLIB_API double GetTime () { return 0; } diff --git a/nglib/nglib.h b/nglib/nglib.h index 286db0cb..511c62bf 100644 --- a/nglib/nglib.h +++ b/nglib/nglib.h @@ -23,15 +23,15 @@ // Philippose - 14.02.2009 // Modifications for creating a DLL in Windows -#ifndef DLL_HEADER +#ifndef NGLIB_API #ifdef WIN32 #ifdef NGLIB_EXPORTS || nglib_EXPORTS - #define DLL_HEADER __declspec(dllexport) + #define NGLIB_API __declspec(dllexport) #else - #define DLL_HEADER __declspec(dllimport) + #define NGLIB_API __declspec(dllimport) #endif #else - #define DLL_HEADER __attribute__((visibility("default"))) + #define NGLIB_API __attribute__((visibility("default"))) #endif #endif @@ -156,7 +156,7 @@ public: - #check_overlap: 1 - #check_overlapping_boundary: 1 */ - DLL_HEADER Ng_Meshing_Parameters(); + NGLIB_API Ng_Meshing_Parameters(); @@ -166,7 +166,7 @@ public: This member function resets all the meshing parameters of the object to the default values */ - DLL_HEADER void Reset_Parameters(); + NGLIB_API void Reset_Parameters(); @@ -177,7 +177,7 @@ public: defined in the local meshing parameters structure of nglib into the internal meshing parameters structure used by the Netgen core */ - DLL_HEADER void Transfer_Parameters(); + NGLIB_API void Transfer_Parameters(); }; @@ -194,7 +194,7 @@ public: program before beginning to use the other Netgen specific functions. */ -DLL_HEADER void Ng_Init (); +NGLIB_API void Ng_Init (); /*! \brief Exit the Netgen meshing kernel in a clean manner @@ -202,7 +202,7 @@ DLL_HEADER void Ng_Init (); Use this function to exit the meshing sub-system in a clean and orderly manner. */ -DLL_HEADER void Ng_Exit (); +NGLIB_API void Ng_Exit (); /*! \brief Create a new (and empty) Netgen Mesh Structure @@ -215,7 +215,7 @@ DLL_HEADER void Ng_Exit (); \return Ng_Mesh Pointer to a Netgen Mesh type #Ng_Mesh */ -DLL_HEADER Ng_Mesh * Ng_NewMesh (); +NGLIB_API Ng_Mesh * Ng_NewMesh (); /*! \brief Delete an existing Netgen Mesh Structure @@ -226,7 +226,7 @@ DLL_HEADER Ng_Mesh * Ng_NewMesh (); \param mesh Pointer to an existing Netgen Mesh structure of type #Ng_Mesh */ -DLL_HEADER void Ng_DeleteMesh (Ng_Mesh * mesh); +NGLIB_API void Ng_DeleteMesh (Ng_Mesh * mesh); /*! \brief Save a Netgen Mesh to disk @@ -243,7 +243,7 @@ DLL_HEADER void Ng_DeleteMesh (Ng_Mesh * mesh); name of the file to which the mesh should be saved */ -DLL_HEADER void Ng_SaveMesh(Ng_Mesh * mesh, const char* filename); +NGLIB_API void Ng_SaveMesh(Ng_Mesh * mesh, const char* filename); /*! \brief Load a Netgen VOL Mesh from disk into memory @@ -256,7 +256,7 @@ DLL_HEADER void Ng_SaveMesh(Ng_Mesh * mesh, const char* filename); \return Ng_Mesh Pointer to a Netgen Mesh type #Ng_Mesh containing the mesh loaded from disk */ -DLL_HEADER Ng_Mesh * Ng_LoadMesh(const char* filename); +NGLIB_API Ng_Mesh * Ng_LoadMesh(const char* filename); /*! \brief Merge a Netgen VOL Mesh from disk into an existing mesh in memory @@ -269,7 +269,7 @@ DLL_HEADER Ng_Mesh * Ng_LoadMesh(const char* filename); name of the file to load \return Ng_Result Status of the merge operation */ -DLL_HEADER Ng_Result Ng_MergeMesh(Ng_Mesh * mesh, const char* filename); +NGLIB_API Ng_Result Ng_MergeMesh(Ng_Mesh * mesh, const char* filename); /*! \brief Merge one Netgen Mesh into another Netgen Mesh in the case @@ -286,7 +286,7 @@ DLL_HEADER Ng_Result Ng_MergeMesh(Ng_Mesh * mesh, const char* filename); the parent mesh \return Ng_Result Status of the merge operation */ -DLL_HEADER Ng_Result Ng_MergeMesh(Ng_Mesh * mesh1, Ng_Mesh * mesh2); +NGLIB_API Ng_Result Ng_MergeMesh(Ng_Mesh * mesh1, Ng_Mesh * mesh2); // ------------------------------------------------------------------ @@ -310,7 +310,7 @@ DLL_HEADER Ng_Result Ng_MergeMesh(Ng_Mesh * mesh1, Ng_Mesh * mesh2); - x[1] = Y co-ordinate - x[2] = Z co-ordinate */ -DLL_HEADER void Ng_AddPoint (Ng_Mesh * mesh, double * x); +NGLIB_API void Ng_AddPoint (Ng_Mesh * mesh, double * x); /*! \brief Add a surface element to a given Netgen Mesh Structure @@ -333,7 +333,7 @@ DLL_HEADER void Ng_AddPoint (Ng_Mesh * mesh, double * x); \param pi Pointer to an array of integers containing the indices of the points which constitute the surface element being added */ -DLL_HEADER void Ng_AddSurfaceElement (Ng_Mesh * mesh, Ng_Surface_Element_Type et, int * pi); +NGLIB_API void Ng_AddSurfaceElement (Ng_Mesh * mesh, Ng_Surface_Element_Type et, int * pi); /*! \brief Add a volume element to a given Netgen Mesh Structure @@ -357,7 +357,7 @@ DLL_HEADER void Ng_AddSurfaceElement (Ng_Mesh * mesh, Ng_Surface_Element_Type et points which constitute the volume element being added */ -DLL_HEADER void Ng_AddVolumeElement (Ng_Mesh * mesh, Ng_Volume_Element_Type et, int * pi); +NGLIB_API void Ng_AddVolumeElement (Ng_Mesh * mesh, Ng_Volume_Element_Type et, int * pi); // ------------------------------------------------------------------ @@ -384,7 +384,7 @@ DLL_HEADER void Ng_AddVolumeElement (Ng_Mesh * mesh, Ng_Volume_Element_Type et, \param h Variable of type double, specifying the maximum allowable mesh size */ -DLL_HEADER void Ng_RestrictMeshSizeGlobal (Ng_Mesh * mesh, double h); +NGLIB_API void Ng_RestrictMeshSizeGlobal (Ng_Mesh * mesh, double h); /*! \brief Locally restrict the mesh element size at the given point @@ -408,7 +408,7 @@ DLL_HEADER void Ng_RestrictMeshSizeGlobal (Ng_Mesh * mesh, double h); \param h Variable of type double, specifying the maximum allowable mesh size at that point */ -DLL_HEADER void Ng_RestrictMeshSizePoint (Ng_Mesh * mesh, double * p, double h); +NGLIB_API void Ng_RestrictMeshSizePoint (Ng_Mesh * mesh, double * p, double h); /*! \brief Locally restrict the mesh element size within a specified box @@ -439,7 +439,7 @@ DLL_HEADER void Ng_RestrictMeshSizePoint (Ng_Mesh * mesh, double * p, double h); \param h Variable of type double, specifying the maximum allowable mesh size at that point */ -DLL_HEADER void Ng_RestrictMeshSizeBox (Ng_Mesh * mesh, double * pmin, double * pmax, double h); +NGLIB_API void Ng_RestrictMeshSizeBox (Ng_Mesh * mesh, double * pmin, double * pmax, double h); // ------------------------------------------------------------------ @@ -470,7 +470,7 @@ DLL_HEADER void Ng_RestrictMeshSizeBox (Ng_Mesh * mesh, double * pmin, double * details regarding the return value can be found in the description of #Ng_Result */ -DLL_HEADER Ng_Result Ng_GenerateVolumeMesh (Ng_Mesh * mesh, Ng_Meshing_Parameters * mp); +NGLIB_API Ng_Result Ng_GenerateVolumeMesh (Ng_Mesh * mesh, Ng_Meshing_Parameters * mp); // ------------------------------------------------------------------ @@ -489,7 +489,7 @@ DLL_HEADER Ng_Result Ng_GenerateVolumeMesh (Ng_Mesh * mesh, Ng_Meshing_Parameter \return Integer Data-type with the number of points in the Mesh */ -DLL_HEADER int Ng_GetNP (Ng_Mesh * mesh); +NGLIB_API int Ng_GetNP (Ng_Mesh * mesh); /*! \brief Returns the Number of Surface Elements present in the specified Mesh @@ -503,7 +503,7 @@ DLL_HEADER int Ng_GetNP (Ng_Mesh * mesh); \return Integer Data-type with the number of surface elements in the Mesh */ -DLL_HEADER int Ng_GetNSE (Ng_Mesh * mesh); +NGLIB_API int Ng_GetNSE (Ng_Mesh * mesh); /*! \brief Returns the Number of Volume Elements present in the specified Mesh @@ -517,7 +517,7 @@ DLL_HEADER int Ng_GetNSE (Ng_Mesh * mesh); \return Integer Data-type with the number of volume elements in the Mesh */ -DLL_HEADER int Ng_GetNE (Ng_Mesh * mesh); +NGLIB_API int Ng_GetNE (Ng_Mesh * mesh); // ------------------------------------------------------------------ @@ -531,15 +531,15 @@ DLL_HEADER int Ng_GetNE (Ng_Mesh * mesh); // Return the Point Coordinates of a specified Point // The x, y and z co-ordinates are returned in the array pointer as // x[0] = x ; x[1] = y ; x[2] = z -DLL_HEADER void Ng_GetPoint (Ng_Mesh * mesh, int num, double * x); +NGLIB_API void Ng_GetPoint (Ng_Mesh * mesh, int num, double * x); // return surface and volume element in pi -DLL_HEADER Ng_Surface_Element_Type +NGLIB_API Ng_Surface_Element_Type Ng_GetSurfaceElement (Ng_Mesh * mesh, int num, int * pi); -DLL_HEADER Ng_Volume_Element_Type +NGLIB_API Ng_Volume_Element_Type Ng_GetVolumeElement (Ng_Mesh * mesh, int num, int * pi); // ------------------------------------------------------------------ @@ -554,34 +554,34 @@ Ng_GetVolumeElement (Ng_Mesh * mesh, int num, int * pi); // feeds points and boundary to mesh -DLL_HEADER void Ng_AddPoint_2D (Ng_Mesh * mesh, double * x); -DLL_HEADER void Ng_AddBoundarySeg_2D (Ng_Mesh * mesh, int pi1, int pi2); +NGLIB_API void Ng_AddPoint_2D (Ng_Mesh * mesh, double * x); +NGLIB_API void Ng_AddBoundarySeg_2D (Ng_Mesh * mesh, int pi1, int pi2); // ask for number of points, elements and boundary segments -DLL_HEADER int Ng_GetNP_2D (Ng_Mesh * mesh); -DLL_HEADER int Ng_GetNE_2D (Ng_Mesh * mesh); -DLL_HEADER int Ng_GetNSeg_2D (Ng_Mesh * mesh); +NGLIB_API int Ng_GetNP_2D (Ng_Mesh * mesh); +NGLIB_API int Ng_GetNE_2D (Ng_Mesh * mesh); +NGLIB_API int Ng_GetNSeg_2D (Ng_Mesh * mesh); // return point coordinates -DLL_HEADER void Ng_GetPoint_2D (Ng_Mesh * mesh, int num, double * x); +NGLIB_API void Ng_GetPoint_2D (Ng_Mesh * mesh, int num, double * x); // return 2d elements -DLL_HEADER Ng_Surface_Element_Type +NGLIB_API Ng_Surface_Element_Type Ng_GetElement_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum = NULL); // return 2d boundary segment -DLL_HEADER void Ng_GetSegment_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum = NULL); +NGLIB_API void Ng_GetSegment_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum = NULL); // load 2d netgen spline geometry -DLL_HEADER Ng_Geometry_2D * Ng_LoadGeometry_2D (const char * filename); +NGLIB_API Ng_Geometry_2D * Ng_LoadGeometry_2D (const char * filename); // generate 2d mesh, mesh is allocated by function -DLL_HEADER Ng_Result Ng_GenerateMesh_2D (Ng_Geometry_2D * geom, +NGLIB_API Ng_Result Ng_GenerateMesh_2D (Ng_Geometry_2D * geom, Ng_Mesh ** mesh, Ng_Meshing_Parameters * mp); -DLL_HEADER void Ng_HP_Refinement (Ng_Geometry_2D * geom, +NGLIB_API void Ng_HP_Refinement (Ng_Geometry_2D * geom, Ng_Mesh * mesh, int levels); @@ -595,35 +595,35 @@ DLL_HEADER void Ng_HP_Refinement (Ng_Geometry_2D * geom, // loads geometry from STL file -DLL_HEADER Ng_STL_Geometry * Ng_STL_LoadGeometry (const char * filename, int binary = 0); +NGLIB_API Ng_STL_Geometry * Ng_STL_LoadGeometry (const char * filename, int binary = 0); // generate new STL Geometry -DLL_HEADER Ng_STL_Geometry * Ng_STL_NewGeometry (); +NGLIB_API Ng_STL_Geometry * Ng_STL_NewGeometry (); // fills STL Geometry // positive orientation // normal vector may be null-pointer -DLL_HEADER void Ng_STL_AddTriangle (Ng_STL_Geometry * geom, +NGLIB_API void Ng_STL_AddTriangle (Ng_STL_Geometry * geom, double * p1, double * p2, double * p3, double * nv = NULL); // add (optional) edges : -DLL_HEADER void Ng_STL_AddEdge (Ng_STL_Geometry * geom, +NGLIB_API void Ng_STL_AddEdge (Ng_STL_Geometry * geom, double * p1, double * p2); // after adding triangles (and edges) initialize -DLL_HEADER Ng_Result Ng_STL_InitSTLGeometry (Ng_STL_Geometry * geom); +NGLIB_API Ng_Result Ng_STL_InitSTLGeometry (Ng_STL_Geometry * geom); // automatically generates edges: -DLL_HEADER Ng_Result Ng_STL_MakeEdges (Ng_STL_Geometry * geom, +NGLIB_API Ng_Result Ng_STL_MakeEdges (Ng_STL_Geometry * geom, Ng_Mesh* mesh, Ng_Meshing_Parameters * mp); // generates mesh, empty mesh must be already created. -DLL_HEADER Ng_Result Ng_STL_GenerateSurfaceMesh (Ng_STL_Geometry * geom, +NGLIB_API Ng_Result Ng_STL_GenerateSurfaceMesh (Ng_STL_Geometry * geom, Ng_Mesh * mesh, Ng_Meshing_Parameters * mp); @@ -638,10 +638,10 @@ DLL_HEADER Ng_Result Ng_STL_GenerateSurfaceMesh (Ng_STL_Geometry * geom, typedef void * Ng_ACIS_Geometry; // loads geometry from STL file -DLL_HEADER Ng_ACIS_Geometry * Ng_ACIS_LoadGeometry (const char * filename); +NGLIB_API Ng_ACIS_Geometry * Ng_ACIS_LoadGeometry (const char * filename); // generates mesh, empty mesh must be already created. -DLL_HEADER Ng_Result Ng_ACIS_GenerateSurfaceMesh (Ng_ACIS_Geometry * geom, +NGLIB_API Ng_Result Ng_ACIS_GenerateSurfaceMesh (Ng_ACIS_Geometry * geom, Ng_Mesh * mesh, Ng_Meshing_Parameters * mp); @@ -657,37 +657,37 @@ DLL_HEADER Ng_Result Ng_ACIS_GenerateSurfaceMesh (Ng_ACIS_Geometry * geom, // ********************************************************** // Create new OCC Geometry Object -DLL_HEADER Ng_OCC_Geometry * Ng_OCC_NewGeometry (); +NGLIB_API Ng_OCC_Geometry * Ng_OCC_NewGeometry (); // Delete an OCC Geometry Object -DLL_HEADER Ng_Result Ng_OCC_DeleteGeometry (Ng_OCC_Geometry * geom); +NGLIB_API Ng_Result Ng_OCC_DeleteGeometry (Ng_OCC_Geometry * geom); // Loads geometry from STEP file -DLL_HEADER Ng_OCC_Geometry * Ng_OCC_Load_STEP (const char * filename); +NGLIB_API Ng_OCC_Geometry * Ng_OCC_Load_STEP (const char * filename); // Loads geometry from IGES file -DLL_HEADER Ng_OCC_Geometry * Ng_OCC_Load_IGES (const char * filename); +NGLIB_API Ng_OCC_Geometry * Ng_OCC_Load_IGES (const char * filename); // Loads geometry from BREP file -DLL_HEADER Ng_OCC_Geometry * Ng_OCC_Load_BREP (const char * filename); +NGLIB_API Ng_OCC_Geometry * Ng_OCC_Load_BREP (const char * filename); // Set the local mesh size based on geometry / topology -DLL_HEADER Ng_Result Ng_OCC_SetLocalMeshSize (Ng_OCC_Geometry * geom, +NGLIB_API Ng_Result Ng_OCC_SetLocalMeshSize (Ng_OCC_Geometry * geom, Ng_Mesh * mesh, Ng_Meshing_Parameters * mp); // Mesh the edges and add Face descriptors to prepare for surface meshing -DLL_HEADER Ng_Result Ng_OCC_GenerateEdgeMesh (Ng_OCC_Geometry * geom, +NGLIB_API Ng_Result Ng_OCC_GenerateEdgeMesh (Ng_OCC_Geometry * geom, Ng_Mesh * mesh, Ng_Meshing_Parameters * mp); // Mesh the surfaces of an OCC geometry -DLL_HEADER Ng_Result Ng_OCC_GenerateSurfaceMesh (Ng_OCC_Geometry * geom, +NGLIB_API Ng_Result Ng_OCC_GenerateSurfaceMesh (Ng_OCC_Geometry * geom, Ng_Mesh * mesh, Ng_Meshing_Parameters * mp); // Get the face map of an already loaded OCC geometry -DLL_HEADER Ng_Result Ng_OCC_GetFMap(Ng_OCC_Geometry * geom, +NGLIB_API Ng_Result Ng_OCC_GetFMap(Ng_OCC_Geometry * geom, Ng_OCC_TopTools_IndexedMapOfShape * FMap); #endif // OCCGEOMETRY @@ -699,22 +699,22 @@ DLL_HEADER Ng_Result Ng_OCC_GetFMap(Ng_OCC_Geometry * geom, // ********************************************************** // uniform mesh refinement -DLL_HEADER void Ng_Uniform_Refinement (Ng_Mesh * mesh); +NGLIB_API void Ng_Uniform_Refinement (Ng_Mesh * mesh); // uniform mesh refinement with geometry adaption: -DLL_HEADER void Ng_2D_Uniform_Refinement (Ng_Geometry_2D * geom, +NGLIB_API void Ng_2D_Uniform_Refinement (Ng_Geometry_2D * geom, Ng_Mesh * mesh); -DLL_HEADER void Ng_STL_Uniform_Refinement (Ng_STL_Geometry * geom, +NGLIB_API void Ng_STL_Uniform_Refinement (Ng_STL_Geometry * geom, Ng_Mesh * mesh); -DLL_HEADER void Ng_CSG_Uniform_Refinement (Ng_CSG_Geometry * geom, +NGLIB_API void Ng_CSG_Uniform_Refinement (Ng_CSG_Geometry * geom, Ng_Mesh * mesh); #ifdef OCCGEOMETRY -DLL_HEADER void Ng_OCC_Uniform_Refinement (Ng_OCC_Geometry * geom, +NGLIB_API void Ng_OCC_Uniform_Refinement (Ng_OCC_Geometry * geom, Ng_Mesh * mesh); #endif @@ -725,22 +725,22 @@ DLL_HEADER void Ng_OCC_Uniform_Refinement (Ng_OCC_Geometry * geom, // ********************************************************** // convert mesh to second order -DLL_HEADER void Ng_Generate_SecondOrder (Ng_Mesh * mesh); +NGLIB_API void Ng_Generate_SecondOrder (Ng_Mesh * mesh); // convert mesh to second order with geometry adaption: -DLL_HEADER void Ng_2D_Generate_SecondOrder (Ng_Geometry_2D * geom, +NGLIB_API void Ng_2D_Generate_SecondOrder (Ng_Geometry_2D * geom, Ng_Mesh * mesh); -DLL_HEADER void Ng_STL_Generate_SecondOrder (Ng_STL_Geometry * geom, +NGLIB_API void Ng_STL_Generate_SecondOrder (Ng_STL_Geometry * geom, Ng_Mesh * mesh); -DLL_HEADER void Ng_CSG_Generate_SecondOrder (Ng_CSG_Geometry * geom, +NGLIB_API void Ng_CSG_Generate_SecondOrder (Ng_CSG_Geometry * geom, Ng_Mesh * mesh); #ifdef OCCGEOMETRY -DLL_HEADER void Ng_OCC_Generate_SecondOrder (Ng_OCC_Geometry * geom, +NGLIB_API void Ng_OCC_Generate_SecondOrder (Ng_OCC_Geometry * geom, Ng_Mesh * mesh); #endif diff --git a/nglib/parallelfunc.cpp b/nglib/parallelfunc.cpp index 0e4398e2..0a0a445a 100644 --- a/nglib/parallelfunc.cpp +++ b/nglib/parallelfunc.cpp @@ -27,18 +27,12 @@ void Parallel_Exit(); namespace netgen { extern AutoPtr mesh; // extern VisualSceneMesh vsmesh; - extern DLL_HEADER MeshingParameters mparam; + extern NGLIB_API MeshingParameters mparam; } using namespace netgen; using netgen::RegisterUserFormats; -namespace netgen -{ - // int id, ntasks; - MPI_Comm mesh_comm; -} - void ParallelRun() { diff --git a/py_tutorials/csg2d.py b/py_tutorials/csg2d.py new file mode 100644 index 00000000..f8050725 --- /dev/null +++ b/py_tutorials/csg2d.py @@ -0,0 +1,37 @@ +from random import random, seed +from ngsolve import Draw, Mesh + +import netgen +from pyngcore import * +from netgen.geom2d import * + +seed(4) + +g = CSG2d() +outer = Rectangle((0, 0), (1, 1), "outer","outer") +inner = Solid2d() + +for i in range(30): + cx = random() + cy = random() + r = 0.03+0.05*random() + print("Add Circle", i, cx, cy, r, flush = True) + circle = Circle((cx, cy), r, "circle"+str(i), "circle"+str(i)) + inner += circle + outer -= circle + + +g.Add(inner) +g.Add(outer) +geo = g.GenerateSplineGeometry() + +m = geo.GenerateMesh(maxh=0.1) + +try: + from ngsolve import Draw, Mesh + Draw(geo) + mesh = Mesh(m) + mesh.Curve(3) + Draw(mesh) +except: + pass diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index bcbf0a35..47910190 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -9,13 +9,13 @@ install(FILES # build stub files for pybind11 packages if(BUILD_STUB_FILES) -find_program(PYBIND11_STUBS NAMES pybind11-stubgen) -if(PYBIND11_STUBS) - message("-- Found pybind11-stubgen: ${PYBIND11_STUBS}") - install(CODE "execute_process(COMMAND ${PYBIND11_STUBS} --no-setup-py netgen)") - install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../stubs/netgen-stubs/ DESTINATION ${NG_INSTALL_DIR_PYTHON}/netgen/ COMPONENT netgen) -else(PYBIND11_STUBS) +execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import pybind11_stubgen; print(pybind11_stubgen.__file__)" OUTPUT_VARIABLE stubgen_path RESULT_VARIABLE pybind11_stubgen) +if(pybind11_stubgen AND NOT ${pybind11_stubgen} EQUAL 0) message(WARNING "pybind11-stubgen not found, if you want to create stub files for better autocompletion support install it with pip.") -endif(PYBIND11_STUBS) +else() + message("-- Found pybind11-stubgen: ${stubgen_path}") + install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pybind11_stubgen --no-setup-py netgen)") + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../stubs/netgen-stubs/ DESTINATION ${NG_INSTALL_DIR_PYTHON}/netgen/ COMPONENT netgen) +endif() endif(BUILD_STUB_FILES) diff --git a/python/__init__.py b/python/__init__.py index e7c23d66..4d352232 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -5,7 +5,10 @@ _netgen_bin_dir=os.path.realpath(os.path.join(os.path.dirname(__file__),'..','@N _netgen_lib_dir=os.path.realpath(os.path.join(os.path.dirname(__file__),'..','@NETGEN_PYTHON_RPATH@')) if sys.platform.startswith('win'): - os.environ['PATH'] += ';'+os.path.realpath(os.path.join(os.path.dirname(__file__),'../../../bin')) + if sys.version >= '3.8': + os.add_dll_directory(_netgen_bin_dir) + else: + os.environ['PATH'] += ';'+_netgen_bin_dir del sys del os @@ -22,3 +25,5 @@ def Redraw(*args, **kwargs): cnt += 1 except: pass + + diff --git a/python/geom2d.py b/python/geom2d.py index c683e035..3bd69d14 100644 --- a/python/geom2d.py +++ b/python/geom2d.py @@ -1,5 +1,6 @@ -from .libngpy._geom2d import SplineGeometry +from .libngpy._geom2d import SplineGeometry, Solid2d, CSG2d, Rectangle, Circle, EdgeInfo, PointInfo from .meshing import meshsize +import math as math unit_square = SplineGeometry() _pnts = [ (0,0), (1,0), (1,1), (0,1) ] @@ -137,4 +138,41 @@ SplineGeometry.AddSegment = lambda *args, **kwargs : SplineGeometry.Append(*args SplineGeometry.AddPoint = lambda *args, **kwargs : SplineGeometry.AppendPoint(*args, **kwargs) SplineGeometry.CreatePML = CreatePML +bc = lambda s : EdgeInfo(bc=s) +maxh = lambda h : EdgeInfo(maxh=h) +def cp(p_or_px, py_or_none = None): + if py_or_none is None: + return EdgeInfo(control_point=p) + else: + return EdgeInfo(control_point=(p_or_px,py_or_none)) + +def Ellipse(center, a, b, bc="ellipse", mat="ellipse"): + """Creates ellipse centered at point center with principle axis a and b. + + Parameters + --------- + center : Vec2 + center of ellipse + a : Vec2 + first principle axis, needs to be perpendicular to b + b : Vec2 + second principle axis, needs to be perpendicular to a + bc : string + boundary name + mat : string + material name + """ + if abs(a[0]*b[0] + a[1]*b[1]) > 1e-12: + raise Exception("In Ellipse: principle axis a and b are not perpendicular") + + ellipse = Circle( center=(0,0), radius=1.0, mat=mat, bc=bc ) + + alpha = math.pi/2-math.atan2(a[0],a[1]) + r_a = math.sqrt(a[0]**2+a[1]**2) + r_b = math.sqrt(b[0]**2+b[1]**2) + ellipse.Scale( (r_a,r_b) ) + ellipse.Rotate( alpha/math.pi*180, center=(0,0) ) + ellipse.Move( center ) + + return ellipse diff --git a/python/meshing.py b/python/meshing.py index 788f248a..9b912b66 100644 --- a/python/meshing.py +++ b/python/meshing.py @@ -59,3 +59,6 @@ class _MeshsizeObject: optsteps3d=5) meshsize = _MeshsizeObject() + + +clearsol = ClearSolutionClass() diff --git a/tests/build_debug.sh b/tests/build_debug.sh index fe650b8e..e779b381 100755 --- a/tests/build_debug.sh +++ b/tests/build_debug.sh @@ -1,6 +1,13 @@ cd mkdir -p build/netgen cd build/netgen -cmake ../../src/netgen -DUSE_CCACHE=ON -DBUILD_TYPE=DEBUG -DENABLE_UNIT_TESTS=ON -DUSE_OCC=ON +cmake \ + -DUSE_CCACHE=ON \ + -DBUILD_TYPE=DEBUG \ + -DENABLE_UNIT_TESTS=ON \ + -DUSE_OCC=ON \ + -DCHECK_RANGE=ON \ + -DUSE_CGNS=ON \ + ../../src/netgen make -j12 make install diff --git a/tests/build_mpi.sh b/tests/build_mpi.sh index 190423ac..0e26af94 100644 --- a/tests/build_mpi.sh +++ b/tests/build_mpi.sh @@ -1,6 +1,10 @@ cd mkdir -p build/netgen cd build/netgen -cmake ../../src/netgen -DUSE_CCACHE=ON -DUSE_MPI=ON -DENABLE_UNIT_TESTS=ON +cmake ../../src/netgen \ + -DCHECK_RANGE=ON \ + -DUSE_CCACHE=ON \ + -DUSE_MPI=ON \ + -DENABLE_UNIT_TESTS=ON make -j12 make install diff --git a/tests/build_ngsolve.sh b/tests/build_ngsolve.sh index a25a628a..d452d2da 100755 --- a/tests/build_ngsolve.sh +++ b/tests/build_ngsolve.sh @@ -3,6 +3,7 @@ git clone https://github.com/NGSolve/ngsolve.git mkdir -p ~/build/ngsolve cd ~/build/ngsolve cmake \ + -DCHECK_RANGE=ON \ -DUSE_MKL=ON \ -DUSE_CCACHE=ON \ -DNETGEN_DIR=/opt/netgen \ diff --git a/tests/catch/CMakeLists.txt b/tests/catch/CMakeLists.txt index c3a6525d..12a665df 100644 --- a/tests/catch/CMakeLists.txt +++ b/tests/catch/CMakeLists.txt @@ -27,6 +27,7 @@ endmacro() add_unit_test(archive archive.cpp) add_unit_test(array array.cpp) +add_unit_test(ranges ranges.cpp) add_unit_test(symboltable symboltable.cpp) add_unit_test(utils utils.cpp) add_unit_test(version version.cpp) diff --git a/tests/catch/ranges.cpp b/tests/catch/ranges.cpp new file mode 100644 index 00000000..b4559de0 --- /dev/null +++ b/tests/catch/ranges.cpp @@ -0,0 +1,18 @@ + +#include "catch.hpp" + +#include +#include + +using namespace ngcore; + +TEST_CASE("ranges") +{ + Array a { 3, -1, 10, -5 }; + Array positive { 3, 10 }; + int i = 0; + for(auto pos_val : a | filter([](auto val) { return val >= 0; })) + { + CHECK(pos_val == positive[i++]); + } +} diff --git a/tests/dockerfile b/tests/dockerfile index 3415481f..152aab13 100644 --- a/tests/dockerfile +++ b/tests/dockerfile @@ -1,5 +1,6 @@ -FROM ubuntu:19.10 +FROM ubuntu:20.10 ENV DEBIAN_FRONTEND=noninteractive MAINTAINER Matthias Hochsteger -RUN apt-get update && apt-get -y install python3 libpython3-dev libxmu-dev tk-dev tcl-dev cmake git g++ libglu1-mesa-dev ccache python3-pytest python3-numpy python3-tk clang-tidy python3-distutils clang libocct-data-exchange-dev +RUN apt-get update && apt-get -y install python3 python3-pip libpython3-dev libxmu-dev tk-dev tcl-dev cmake git g++ libglu1-mesa-dev ccache python3-pytest python3-numpy python3-tk clang-tidy python3-distutils clang libocct-data-exchange-dev libcgns-dev libhdf5-dev +RUN python3 -m pip install pytest-check ADD . /root/src/netgen diff --git a/tests/dockerfile_mpi b/tests/dockerfile_mpi index ddb7e51b..a25a96b9 100644 --- a/tests/dockerfile_mpi +++ b/tests/dockerfile_mpi @@ -1,5 +1,6 @@ -FROM ubuntu:18.04 +FROM ubuntu:20.04 ENV DEBIAN_FRONTEND=noninteractive MAINTAINER Matthias Hochsteger -RUN apt-get update && apt-get -y install python3 libpython3-dev libxmu-dev tk-dev tcl-dev cmake git g++ libglu1-mesa-dev ccache python3-pytest python3-numpy python3-tk clang-tidy python3-distutils clang libopenmpi-dev openmpi-bin gfortran +RUN apt-get update && apt-get -y install python3 libpython3-dev python3-pip libxmu-dev tk-dev tcl-dev cmake git g++ libglu1-mesa-dev ccache python3-numpy python3-tk python3-mpi4py clang-tidy python3-distutils clang libopenmpi-dev openmpi-bin gfortran +RUN python3 -m pip install pytest-mpi pytest-check pytest ADD . /root/src/netgen diff --git a/tests/pytest/CMakeLists.txt b/tests/pytest/CMakeLists.txt index bc6b8b26..26c7d22f 100644 --- a/tests/pytest/CMakeLists.txt +++ b/tests/pytest/CMakeLists.txt @@ -2,4 +2,7 @@ if(USE_PYTHON) add_test(NAME pytest COMMAND ${PYTHON_EXECUTABLE} -m pytest WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) add_custom_target(pytest ${PYTHON_EXECUTABLE} -m pytest WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) set_tests_properties ( pytest PROPERTIES TIMEOUT 1800 ) + if(USE_MPI AND USE_MPI4PY) + add_test(NAME pytest-mpi COMMAND ${MPIEXEC_EXECUTABLE} --allow-run-as-root -np 4 ${PYTHON_EXECUTABLE} -m pytest --with-mpi test_mpi4py.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + endif(USE_MPI AND USE_MPI4PY) endif(USE_PYTHON) diff --git a/tests/pytest/results.json b/tests/pytest/results.json index b9f3f047..45b04c68 100644 --- a/tests/pytest/results.json +++ b/tests/pytest/results.json @@ -110,7 +110,7 @@ { "angles_tet": [ 14.829, - 145.34 + 146.41 ], "angles_trig": [ 16.491, @@ -118,14 +118,14 @@ ], "ne1d": 94, "ne2d": 114, - "ne3d": 121, - "quality_histogram": "[0, 0, 0, 0, 1, 2, 4, 9, 17, 14, 11, 12, 10, 7, 11, 4, 2, 14, 3, 0]", - "total_badness": 227.92261008 + "ne3d": 122, + "quality_histogram": "[0, 0, 0, 0, 1, 2, 4, 9, 19, 14, 12, 10, 10, 7, 11, 4, 2, 14, 3, 0]", + "total_badness": 231.46662286 }, { "angles_tet": [ - 16.335, - 152.61 + 15.88, + 154.64 ], "angles_trig": [ 20.0, @@ -133,9 +133,9 @@ ], "ne1d": 136, "ne2d": 222, - "ne3d": 348, - "quality_histogram": "[0, 0, 0, 0, 0, 4, 3, 5, 4, 9, 18, 23, 27, 46, 55, 62, 55, 20, 16, 1]", - "total_badness": 514.05343802 + "ne3d": 352, + "quality_histogram": "[0, 0, 0, 0, 0, 5, 5, 7, 4, 7, 14, 26, 24, 54, 64, 61, 47, 18, 14, 2]", + "total_badness": 527.329265 }, { "angles_tet": [ @@ -293,33 +293,33 @@ }, { "angles_tet": [ - 15.271, - 159.02 + 16.89, + 158.0 ], "angles_trig": [ - 14.076, - 146.64 + 16.739, + 133.14 ], "ne1d": 32, "ne2d": 220, - "ne3d": 642, - "quality_histogram": "[0, 0, 0, 0, 4, 22, 41, 45, 52, 48, 42, 42, 63, 64, 57, 33, 39, 45, 37, 8]", - "total_badness": 1182.99704 + "ne3d": 551, + "quality_histogram": "[0, 0, 0, 0, 0, 3, 4, 16, 23, 34, 48, 43, 50, 61, 70, 53, 51, 48, 37, 10]", + "total_badness": 860.81905284 }, { "angles_tet": [ - 2.7569, - 173.39 + 5.6575, + 169.44 ], "angles_trig": [ - 7.6422, - 156.83 + 7.092, + 155.41 ], "ne1d": 48, "ne2d": 428, - "ne3d": 811, - "quality_histogram": "[0, 8, 32, 34, 32, 51, 46, 63, 86, 81, 63, 72, 59, 44, 46, 24, 27, 24, 15, 4]", - "total_badness": 2131.9115363 + "ne3d": 763, + "quality_histogram": "[0, 1, 12, 30, 35, 44, 39, 49, 82, 100, 68, 81, 55, 44, 49, 23, 22, 19, 8, 2]", + "total_badness": 1832.2349397 }, { "angles_tet": [ @@ -462,18 +462,18 @@ "cubeandring.geo": [ { "angles_tet": [ - 4.093, - 171.18 + 5.2065, + 170.27 ], "angles_trig": [ - 12.541, - 150.46 + 10.96, + 154.62 ], "ne1d": 262, "ne2d": 726, - "ne3d": 2186, - "quality_histogram": "[0, 4, 11, 34, 76, 101, 133, 102, 83, 52, 61, 79, 114, 196, 255, 258, 257, 227, 114, 29]", - "total_badness": 4154.7434704 + "ne3d": 2167, + "quality_histogram": "[0, 4, 17, 35, 75, 117, 114, 112, 77, 51, 58, 86, 115, 177, 248, 293, 239, 204, 118, 27]", + "total_badness": 4176.9284057 }, { "angles_tet": [ @@ -488,12 +488,12 @@ "ne2d": 164, "ne3d": 252, "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 4, 2, 3, 11, 30, 31, 34, 40, 38, 33, 17, 7, 1]", - "total_badness": 369.95189199 + "total_badness": 369.95189237 }, { "angles_tet": [ - 20.8, - 137.89 + 14.206, + 159.84 ], "angles_trig": [ 21.077, @@ -501,24 +501,24 @@ ], "ne1d": 190, "ne2d": 300, - "ne3d": 631, - "quality_histogram": "[0, 0, 0, 0, 1, 0, 0, 3, 8, 27, 48, 60, 69, 107, 85, 89, 64, 48, 20, 2]", - "total_badness": 943.22810332 + "ne3d": 632, + "quality_histogram": "[0, 0, 0, 0, 2, 0, 0, 4, 8, 26, 45, 62, 80, 96, 90, 84, 65, 46, 22, 2]", + "total_badness": 947.91494351 }, { "angles_tet": [ - 5.1018, - 167.98 + 5.9887, + 161.33 ], "angles_trig": [ - 12.696, + 13.133, 150.46 ], "ne1d": 262, "ne2d": 726, - "ne3d": 2080, - "quality_histogram": "[0, 2, 6, 20, 50, 80, 113, 90, 72, 47, 38, 75, 95, 179, 262, 272, 286, 224, 135, 34]", - "total_badness": 3667.9320382 + "ne3d": 2060, + "quality_histogram": "[0, 2, 5, 15, 46, 103, 106, 104, 71, 36, 48, 67, 99, 165, 253, 287, 287, 195, 136, 35]", + "total_badness": 3642.1604728 }, { "angles_tet": [ @@ -533,7 +533,7 @@ "ne2d": 1412, "ne3d": 7670, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 2, 7, 36, 112, 284, 477, 845, 1308, 1555, 1517, 1168, 359]", - "total_badness": 9545.7928464 + "total_badness": 9545.7933664 }, { "angles_tet": [ @@ -633,14 +633,14 @@ 154.37 ], "angles_trig": [ - 19.374, + 19.317, 128.1 ], "ne1d": 428, "ne2d": 926, - "ne3d": 1071, - "quality_histogram": "[0, 0, 0, 0, 0, 1, 3, 22, 48, 36, 110, 131, 98, 115, 161, 162, 68, 66, 30, 20]", - "total_badness": 1667.9770545 + "ne3d": 1086, + "quality_histogram": "[0, 0, 0, 0, 0, 1, 3, 22, 44, 37, 108, 133, 101, 117, 167, 161, 67, 71, 33, 21]", + "total_badness": 1684.0903817 } ], "cubemcyl.geo": [ @@ -661,18 +661,18 @@ }, { "angles_tet": [ - 20.47, - 141.07 + 19.437, + 141.17 ], "angles_trig": [ - 17.455, - 137.29 + 17.771, + 130.84 ], "ne1d": 64, "ne2d": 642, - "ne3d": 3261, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 2, 17, 24, 62, 115, 220, 353, 475, 545, 510, 445, 318, 143, 32]", - "total_badness": 4608.5480688 + "ne3d": 3305, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 11, 21, 62, 135, 236, 368, 452, 577, 512, 439, 311, 151, 29]", + "total_badness": 4674.6678046 }, { "angles_tet": [ @@ -845,33 +845,33 @@ }, { "angles_tet": [ - 19.3, - 149.07 + 24.676, + 151.98 ], "angles_trig": [ - 23.799, - 122.37 + 24.811, + 126.7 ], "ne1d": 24, "ne2d": 66, - "ne3d": 71, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 2, 2, 4, 9, 1, 5, 3, 3, 3, 2, 9, 24, 3, 1]", - "total_badness": 108.79228828 + "ne3d": 70, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 0, 6, 5, 5, 2, 6, 5, 3, 5, 14, 17, 1, 0]", + "total_badness": 105.64076027 }, { "angles_tet": [ - 14.383, - 158.73 + 8.4923, + 161.34 ], "angles_trig": [ - 12.296, - 144.06 + 20.122, + 127.45 ], "ne1d": 36, "ne2d": 152, - "ne3d": 515, - "quality_histogram": "[0, 0, 0, 1, 2, 12, 25, 45, 55, 55, 63, 44, 38, 41, 26, 30, 42, 16, 13, 7]", - "total_badness": 981.48109509 + "ne3d": 358, + "quality_histogram": "[0, 0, 1, 0, 0, 2, 5, 11, 21, 19, 22, 22, 31, 29, 35, 39, 57, 37, 17, 10]", + "total_badness": 559.67849284 }, { "angles_tet": [ @@ -941,14 +941,14 @@ 167.27 ], "angles_trig": [ - 10.973, + 13.416, 150.52 ], "ne1d": 48, "ne2d": 142, - "ne3d": 162, - "quality_histogram": "[0, 0, 2, 7, 26, 32, 17, 2, 0, 2, 0, 1, 2, 5, 6, 15, 15, 13, 17, 0]", - "total_badness": 437.74460755 + "ne3d": 150, + "quality_histogram": "[0, 0, 0, 5, 27, 32, 16, 4, 0, 2, 1, 1, 4, 5, 3, 17, 16, 7, 10, 0]", + "total_badness": 409.78409193 }, { "angles_tet": [ @@ -1014,33 +1014,33 @@ }, { "angles_tet": [ - 4.2159, - 171.02 + 5.6074, + 169.95 ], "angles_trig": [ - 7.6756, - 159.68 + 7.5945, + 159.99 ], "ne1d": 0, "ne2d": 192, - "ne3d": 957, - "quality_histogram": "[0, 21, 76, 109, 128, 111, 100, 84, 77, 55, 62, 31, 32, 22, 10, 15, 8, 10, 4, 2]", - "total_badness": 3582.099151 + "ne3d": 748, + "quality_histogram": "[0, 0, 30, 62, 87, 77, 80, 61, 72, 38, 54, 43, 34, 27, 27, 20, 18, 10, 7, 1]", + "total_badness": 2287.1659209 }, { "angles_tet": [ - 19.919, - 134.24 + 19.777, + 131.46 ], "angles_trig": [ - 19.054, - 114.7 + 20.139, + 112.74 ], "ne1d": 0, "ne2d": 394, "ne3d": 597, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 2, 6, 29, 49, 60, 87, 92, 86, 82, 48, 33, 15, 8]", - "total_badness": 899.55007686 + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 2, 6, 29, 41, 61, 90, 98, 81, 81, 55, 29, 17, 7]", + "total_badness": 896.10180497 }, { "angles_tet": [ @@ -1091,18 +1091,18 @@ "ellipticcone.geo": [ { "angles_tet": [ - 17.698, + 17.699, 148.03 ], "angles_trig": [ - 23.433, + 23.943, 122.76 ], "ne1d": 174, "ne2d": 1562, - "ne3d": 5188, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 1, 9, 31, 100, 197, 340, 544, 700, 933, 940, 757, 460, 175]", - "total_badness": 6822.0657356 + "ne3d": 5212, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 1, 8, 32, 89, 187, 328, 534, 719, 937, 969, 775, 458, 174]", + "total_badness": 6834.3167615 }, { "angles_tet": [ @@ -1121,33 +1121,33 @@ }, { "angles_tet": [ - 16.571, + 16.597, 149.96 ], "angles_trig": [ - 18.137, + 18.553, 135.0 ], "ne1d": 130, "ne2d": 864, - "ne3d": 1653, - "quality_histogram": "[0, 0, 0, 0, 0, 2, 9, 29, 44, 46, 61, 116, 147, 195, 200, 234, 270, 172, 100, 28]", - "total_badness": 2389.4865072 + "ne3d": 1652, + "quality_histogram": "[0, 0, 0, 0, 0, 2, 12, 28, 40, 57, 56, 113, 146, 183, 206, 245, 268, 166, 103, 27]", + "total_badness": 2393.1339621 }, { "angles_tet": [ - 21.001, - 144.04 + 21.766, + 144.03 ], "angles_trig": [ - 25.698, + 25.696, 119.99 ], "ne1d": 174, "ne2d": 1562, - "ne3d": 5007, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 2, 10, 50, 105, 242, 409, 648, 901, 1007, 887, 539, 206]", - "total_badness": 6381.0431998 + "ne3d": 5072, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 4, 11, 54, 93, 228, 440, 641, 935, 1026, 892, 544, 204]", + "total_badness": 6463.5570559 }, { "angles_tet": [ @@ -1166,18 +1166,18 @@ }, { "angles_tet": [ - 20.184, + 19.932, 144.83 ], "angles_trig": [ - 21.582, + 21.622, 126.14 ], "ne1d": 432, "ne2d": 9544, - "ne3d": 69863, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 7, 30, 69, 222, 646, 1562, 3675, 7035, 11242, 14646, 15515, 11810, 3403]", - "total_badness": 85648.446235 + "ne3d": 69846, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 7, 30, 71, 221, 652, 1560, 3667, 7028, 11234, 14630, 15524, 11810, 3411]", + "total_badness": 85625.275421 } ], "ellipticcyl.geo": [ @@ -1258,18 +1258,110 @@ }, { "angles_tet": [ - 21.427, + 21.266, 143.66 ], "angles_trig": [ - 23.929, - 119.81 + 23.958, + 119.8 ], "ne1d": 388, "ne2d": 6142, - "ne3d": 54709, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 2, 17, 61, 251, 727, 2119, 4819, 8218, 11623, 13209, 10213, 3450]", - "total_badness": 65887.909834 + "ne3d": 54704, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 2, 17, 66, 248, 717, 2138, 4854, 8219, 11632, 13173, 10210, 3428]", + "total_badness": 65897.969985 + } + ], + "extrusion.geo": [ + { + "angles_tet": [ + 6.6841, + 171.53 + ], + "angles_trig": [ + 11.293, + 152.07 + ], + "ne1d": 172, + "ne2d": 286, + "ne3d": 241, + "quality_histogram": "[0, 0, 7, 56, 39, 22, 0, 0, 0, 0, 2, 0, 5, 18, 18, 29, 21, 10, 9, 5]", + "total_badness": 775.80779693 + }, + { + "angles_tet": [ + 16.097, + 160.17 + ], + "angles_trig": [ + 15.327, + 149.09 + ], + "ne1d": 104, + "ne2d": 152, + "ne3d": 124, + "quality_histogram": "[0, 0, 0, 0, 10, 19, 39, 26, 9, 3, 1, 1, 5, 1, 3, 2, 3, 2, 0, 0]", + "total_badness": 353.53219387 + }, + { + "angles_tet": [ + 11.505, + 165.94 + ], + "angles_trig": [ + 15.054, + 147.98 + ], + "ne1d": 134, + "ne2d": 196, + "ne3d": 167, + "quality_histogram": "[0, 0, 0, 1, 3, 35, 33, 8, 5, 21, 11, 9, 10, 11, 7, 3, 5, 3, 1, 1]", + "total_badness": 417.63980201 + }, + { + "angles_tet": [ + 6.6841, + 171.53 + ], + "angles_trig": [ + 11.293, + 152.07 + ], + "ne1d": 172, + "ne2d": 286, + "ne3d": 241, + "quality_histogram": "[0, 0, 7, 56, 39, 22, 0, 0, 0, 0, 2, 0, 5, 18, 18, 29, 21, 10, 9, 5]", + "total_badness": 775.80779693 + }, + { + "angles_tet": [ + 17.691, + 140.88 + ], + "angles_trig": [ + 18.812, + 116.06 + ], + "ne1d": 276, + "ne2d": 570, + "ne3d": 646, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 4, 17, 30, 51, 51, 55, 81, 69, 74, 73, 72, 52, 12, 5]", + "total_badness": 1020.0235117 + }, + { + "angles_tet": [ + 14.402, + 155.5 + ], + "angles_trig": [ + 24.071, + 119.03 + ], + "ne1d": 442, + "ne2d": 1220, + "ne3d": 2802, + "quality_histogram": "[0, 0, 0, 0, 0, 2, 5, 2, 4, 12, 25, 55, 147, 298, 311, 503, 457, 536, 342, 103]", + "total_badness": 3603.431162 } ], "fichera.geo": [ @@ -1284,9 +1376,9 @@ ], "ne1d": 50, "ne2d": 38, - "ne3d": 35, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 3, 8, 4, 10, 2, 0, 0, 2]", - "total_badness": 50.263302236 + "ne3d": 36, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 4, 7, 6, 8, 1, 1, 0, 1]", + "total_badness": 53.038414986 }, { "angles_tet": [ @@ -1329,9 +1421,9 @@ ], "ne1d": 50, "ne2d": 38, - "ne3d": 35, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 3, 8, 4, 10, 2, 0, 0, 2]", - "total_badness": 50.263302236 + "ne3d": 36, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 4, 7, 6, 8, 1, 1, 0, 1]", + "total_badness": 53.038414986 }, { "angles_tet": [ @@ -1367,7 +1459,7 @@ "frame.step": [ { "angles_tet": [ - 2.9116, + 2.9158, 171.1 ], "angles_trig": [ @@ -1376,9 +1468,9 @@ ], "ne1d": 10108, "ne2d": 30160, - "ne3d": 152872, - "quality_histogram": "[0, 3, 1, 3, 6, 18, 67, 161, 549, 1308, 2949, 5900, 10578, 16512, 21632, 25909, 26394, 22816, 14332, 3734]", - "total_badness": 202698.59306 + "ne3d": 152932, + "quality_histogram": "[0, 3, 1, 3, 6, 20, 60, 151, 543, 1312, 2940, 5874, 10445, 16338, 21809, 26075, 26405, 22854, 14351, 3742]", + "total_badness": 202658.67088 }, { "angles_tet": [ @@ -1391,13 +1483,13 @@ ], "ne1d": 5988, "ne2d": 11102, - "ne3d": 29165, - "quality_histogram": "[3, 4, 7, 13, 24, 41, 117, 235, 718, 979, 1564, 2453, 3072, 3948, 4334, 4219, 3348, 2423, 1351, 312]", - "total_badness": 43278.945822 + "ne3d": 29140, + "quality_histogram": "[3, 4, 3, 10, 18, 43, 114, 248, 669, 1026, 1563, 2467, 3073, 3886, 4389, 4179, 3406, 2403, 1317, 319]", + "total_badness": 43201.580215 }, { "angles_tet": [ - 2.1657, + 2.1788, 174.11 ], "angles_trig": [ @@ -1406,16 +1498,16 @@ ], "ne1d": 9622, "ne2d": 23964, - "ne3d": 80724, - "quality_histogram": "[1, 16, 3, 19, 17, 40, 93, 222, 516, 1095, 2417, 4595, 7465, 10279, 12687, 13138, 11850, 9165, 5675, 1431]", - "total_badness": 111634.58051 + "ne3d": 80884, + "quality_histogram": "[2, 15, 4, 18, 16, 39, 98, 222, 478, 1114, 2389, 4532, 7510, 10291, 12685, 13114, 12137, 9102, 5641, 1477]", + "total_badness": 111769.66842 } ], "hinge.stl": [ { "angles_tet": [ - 20.946, - 144.42 + 16.835, + 148.75 ], "angles_trig": [ 18.101, @@ -1423,24 +1515,24 @@ ], "ne1d": 456, "ne2d": 1220, - "ne3d": 1986, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 7, 20, 39, 70, 124, 179, 243, 300, 298, 263, 259, 143, 41]", - "total_badness": 2750.7798523 + "ne3d": 1985, + "quality_histogram": "[0, 0, 0, 0, 0, 1, 0, 9, 20, 38, 65, 129, 172, 240, 303, 297, 268, 263, 138, 42]", + "total_badness": 2749.9093553 }, { "angles_tet": [ - 7.6272, + 7.7862, 161.84 ], "angles_trig": [ - 9.1007, + 9.6143, 148.89 ], "ne1d": 298, "ne2d": 610, - "ne3d": 817, - "quality_histogram": "[0, 0, 1, 11, 8, 5, 21, 16, 43, 41, 72, 88, 107, 100, 84, 87, 52, 50, 28, 3]", - "total_badness": 1409.8967045 + "ne3d": 789, + "quality_histogram": "[0, 0, 1, 10, 9, 4, 22, 15, 40, 43, 68, 84, 103, 96, 83, 85, 48, 49, 25, 4]", + "total_badness": 1364.9012739 }, { "angles_tet": [ @@ -1453,9 +1545,9 @@ ], "ne1d": 370, "ne2d": 856, - "ne3d": 1135, - "quality_histogram": "[0, 0, 0, 2, 4, 5, 14, 26, 39, 57, 79, 117, 135, 136, 153, 151, 97, 67, 45, 8]", - "total_badness": 1799.6066426 + "ne3d": 1134, + "quality_histogram": "[0, 0, 0, 2, 4, 5, 14, 25, 38, 56, 78, 119, 136, 137, 152, 151, 98, 66, 45, 8]", + "total_badness": 1795.6519937 }, { "angles_tet": [ @@ -1468,9 +1560,9 @@ ], "ne1d": 516, "ne2d": 1574, - "ne3d": 2598, - "quality_histogram": "[0, 0, 0, 0, 0, 4, 3, 4, 26, 48, 95, 172, 225, 326, 383, 383, 339, 325, 216, 49]", - "total_badness": 3605.8538311 + "ne3d": 2592, + "quality_histogram": "[0, 0, 0, 0, 0, 4, 3, 4, 24, 49, 98, 174, 224, 322, 378, 396, 333, 317, 216, 50]", + "total_badness": 3599.8559259 }, { "angles_tet": [ @@ -1483,24 +1575,24 @@ ], "ne1d": 722, "ne2d": 2866, - "ne3d": 6697, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 2, 26, 30, 56, 167, 331, 645, 892, 1039, 1173, 1196, 881, 258]", - "total_badness": 8588.7853201 + "ne3d": 6672, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 1, 24, 30, 67, 169, 331, 632, 870, 1040, 1173, 1200, 874, 260]", + "total_badness": 8558.9452401 }, { "angles_tet": [ 20.701, - 142.89 + 141.98 ], "angles_trig": [ 22.443, - 124.89 + 122.07 ], "ne1d": 1862, "ne2d": 19474, - "ne3d": 136597, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 12, 54, 288, 870, 2618, 6513, 13052, 21346, 28986, 31108, 23898, 7851]", - "total_badness": 166108.75934 + "ne3d": 136547, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 8, 60, 274, 868, 2525, 6456, 12982, 21266, 29083, 31162, 24018, 7844]", + "total_badness": 165951.25229 } ], "lense.in2d": [ @@ -1513,8 +1605,8 @@ 0.0, 0.0 ], - "ne1d": 84, - "ne2d": 436, + "ne1d": 81, + "ne2d": 442, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -1529,7 +1621,7 @@ 0.0 ], "ne1d": 86, - "ne2d": 476, + "ne2d": 464, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -1544,7 +1636,7 @@ 0.0 ], "ne1d": 86, - "ne2d": 490, + "ne2d": 466, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -1558,8 +1650,8 @@ 0.0, 0.0 ], - "ne1d": 84, - "ne2d": 436, + "ne1d": 81, + "ne2d": 442, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -1573,8 +1665,8 @@ 0.0, 0.0 ], - "ne1d": 84, - "ne2d": 460, + "ne1d": 82, + "ne2d": 441, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -1588,8 +1680,8 @@ 0.0, 0.0 ], - "ne1d": 88, - "ne2d": 496, + "ne1d": 86, + "ne2d": 472, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -1690,18 +1782,18 @@ "manyholes.geo": [ { "angles_tet": [ - 14.385, + 14.551, 155.18 ], "angles_trig": [ - 13.429, + 16.38, 141.4 ], "ne1d": 5886, "ne2d": 48052, - "ne3d": 178770, - "quality_histogram": "[0, 0, 0, 0, 0, 5, 15, 82, 303, 822, 2352, 6216, 10998, 19000, 27368, 30676, 31225, 26902, 18298, 4508]", - "total_badness": 233986.23978 + "ne3d": 178844, + "quality_histogram": "[0, 0, 0, 0, 0, 5, 11, 71, 288, 829, 2312, 6198, 10948, 18856, 27414, 30640, 31333, 26927, 18474, 4538]", + "total_badness": 233920.54177 }, { "angles_tet": [ @@ -1709,19 +1801,19 @@ 149.67 ], "angles_trig": [ - 14.887, + 15.246, 137.87 ], "ne1d": 2746, "ne2d": 13866, - "ne3d": 29396, - "quality_histogram": "[0, 0, 0, 0, 14, 15, 38, 137, 372, 851, 1456, 2307, 3270, 4307, 4196, 3746, 3316, 2685, 1975, 711]", - "total_badness": 42211.73971 + "ne3d": 29391, + "quality_histogram": "[0, 0, 0, 0, 13, 14, 37, 136, 380, 846, 1453, 2331, 3276, 4281, 4196, 3749, 3317, 2686, 1967, 709]", + "total_badness": 42208.382479 }, { "angles_tet": [ 11.183, - 158.43 + 153.89 ], "angles_trig": [ 12.194, @@ -1729,9 +1821,9 @@ ], "ne1d": 4106, "ne2d": 27994, - "ne3d": 70594, - "quality_histogram": "[0, 0, 0, 1, 34, 79, 184, 352, 684, 1479, 2553, 4149, 6716, 9292, 10342, 10582, 9852, 7683, 4825, 1787]", - "total_badness": 98915.787673 + "ne3d": 70789, + "quality_histogram": "[0, 0, 0, 1, 31, 72, 171, 344, 668, 1440, 2602, 4089, 6675, 9290, 10472, 10751, 9903, 7620, 4876, 1784]", + "total_badness": 99059.754776 } ], "manyholes2.geo": [ @@ -1746,9 +1838,9 @@ ], "ne1d": 10202, "ne2d": 55380, - "ne3d": 128326, - "quality_histogram": "[0, 0, 0, 0, 4, 29, 80, 239, 718, 1909, 4473, 7696, 11673, 17533, 18499, 18373, 17274, 15234, 10928, 3664]", - "total_badness": 176317.23115 + "ne3d": 128239, + "quality_histogram": "[0, 0, 0, 0, 4, 28, 79, 241, 721, 1932, 4433, 7722, 11698, 17421, 18591, 18340, 17271, 15154, 10934, 3670]", + "total_badness": 176223.54068 } ], "matrix.geo": [ @@ -1763,14 +1855,14 @@ ], "ne1d": 174, "ne2d": 1198, - "ne3d": 5066, - "quality_histogram": "[0, 0, 11, 118, 169, 57, 61, 111, 95, 184, 293, 368, 508, 651, 617, 577, 496, 429, 246, 75]", - "total_badness": 8799.2034431 + "ne3d": 5070, + "quality_histogram": "[0, 0, 11, 117, 166, 59, 60, 113, 98, 185, 297, 374, 498, 658, 620, 577, 494, 424, 240, 79]", + "total_badness": 8804.2621534 }, { "angles_tet": [ - 7.9601, - 167.83 + 9.3063, + 165.3 ], "angles_trig": [ 7.9174, @@ -1778,9 +1870,9 @@ ], "ne1d": 106, "ne2d": 610, - "ne3d": 1654, - "quality_histogram": "[0, 1, 12, 50, 83, 156, 195, 155, 160, 124, 137, 141, 133, 102, 67, 34, 32, 43, 24, 5]", - "total_badness": 4104.7339693 + "ne3d": 1659, + "quality_histogram": "[0, 1, 13, 49, 81, 155, 190, 149, 159, 132, 135, 145, 137, 107, 66, 37, 33, 40, 26, 4]", + "total_badness": 4094.4605262 }, { "angles_tet": [ @@ -1795,7 +1887,7 @@ "ne2d": 830, "ne3d": 2488, "quality_histogram": "[0, 0, 3, 37, 71, 155, 161, 102, 158, 211, 284, 276, 249, 203, 195, 139, 108, 79, 42, 15]", - "total_badness": 5146.3098744 + "total_badness": 5146.3098762 }, { "angles_tet": [ @@ -1808,14 +1900,14 @@ ], "ne1d": 174, "ne2d": 1198, - "ne3d": 5005, - "quality_histogram": "[0, 0, 7, 100, 165, 53, 56, 108, 103, 165, 278, 336, 503, 574, 623, 631, 520, 438, 264, 81]", - "total_badness": 8524.8161998 + "ne3d": 5012, + "quality_histogram": "[0, 0, 7, 101, 161, 60, 53, 107, 93, 172, 281, 320, 519, 570, 616, 653, 509, 445, 260, 85]", + "total_badness": 8527.4907589 }, { "angles_tet": [ 12.758, - 147.69 + 148.17 ], "angles_trig": [ 15.825, @@ -1823,14 +1915,14 @@ ], "ne1d": 248, "ne2d": 2324, - "ne3d": 16276, - "quality_histogram": "[0, 0, 0, 0, 0, 5, 22, 54, 116, 179, 301, 610, 975, 1512, 2111, 2585, 2741, 2659, 1833, 573]", - "total_badness": 21483.905479 + "ne3d": 16371, + "quality_histogram": "[0, 0, 0, 0, 0, 5, 22, 53, 115, 192, 297, 621, 1022, 1479, 2137, 2601, 2778, 2625, 1890, 534]", + "total_badness": 21627.026306 }, { "angles_tet": [ 18.203, - 145.26 + 145.38 ], "angles_trig": [ 17.821, @@ -1838,9 +1930,9 @@ ], "ne1d": 418, "ne2d": 5968, - "ne3d": 101047, - "quality_histogram": "[0, 0, 0, 0, 0, 1, 5, 8, 52, 104, 356, 989, 2551, 5548, 10164, 16045, 20725, 22251, 16920, 5328]", - "total_badness": 124081.88321 + "ne3d": 101113, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 4, 7, 51, 102, 349, 993, 2550, 5563, 10196, 16090, 20698, 22258, 16911, 5341]", + "total_badness": 124155.81178 } ], "ortho.geo": [ @@ -1938,18 +2030,18 @@ "part1.stl": [ { "angles_tet": [ - 13.213, - 147.22 + 14.209, + 147.23 ], "angles_trig": [ 19.94, - 128.03 + 127.49 ], "ne1d": 170, "ne2d": 448, - "ne3d": 1260, - "quality_histogram": "[0, 0, 0, 0, 0, 2, 2, 9, 17, 21, 37, 69, 106, 152, 209, 198, 165, 149, 98, 26]", - "total_badness": 1750.899754 + "ne3d": 1261, + "quality_histogram": "[0, 0, 0, 0, 0, 1, 3, 8, 15, 25, 35, 78, 97, 157, 211, 199, 161, 144, 100, 27]", + "total_badness": 1752.4668505 }, { "angles_tet": [ @@ -1962,43 +2054,43 @@ ], "ne1d": 134, "ne2d": 288, - "ne3d": 528, - "quality_histogram": "[0, 0, 0, 2, 4, 2, 4, 3, 16, 24, 36, 41, 54, 70, 68, 73, 59, 47, 24, 1]", - "total_badness": 813.79298254 + "ne3d": 524, + "quality_histogram": "[0, 0, 0, 2, 4, 2, 4, 7, 16, 28, 38, 46, 52, 62, 72, 70, 55, 40, 24, 2]", + "total_badness": 820.9371699 }, { "angles_tet": [ 21.121, - 136.02 + 143.15 ], "angles_trig": [ - 24.392, + 24.417, 116.27 ], "ne1d": 194, "ne2d": 594, - "ne3d": 1699, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 3, 16, 27, 60, 126, 203, 248, 264, 314, 243, 151, 43]", - "total_badness": 2248.5537373 + "ne3d": 1693, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 5, 3, 28, 53, 113, 172, 250, 278, 293, 284, 173, 40]", + "total_badness": 2213.1235108 }, { "angles_tet": [ - 22.052, - 141.32 + 22.044, + 141.38 ], "angles_trig": [ - 25.868, - 119.75 + 25.297, + 120.13 ], "ne1d": 266, "ne2d": 986, - "ne3d": 4101, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 5, 12, 29, 53, 158, 292, 530, 683, 826, 812, 568, 133]", - "total_badness": 5160.0364225 + "ne3d": 4106, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 5, 11, 28, 57, 139, 316, 492, 706, 817, 813, 570, 151]", + "total_badness": 5157.0222907 }, { "angles_tet": [ - 23.306, + 23.336, 138.08 ], "angles_trig": [ @@ -2007,9 +2099,9 @@ ], "ne1d": 674, "ne2d": 6854, - "ne3d": 82748, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 3, 17, 121, 417, 1362, 3669, 7623, 12797, 17700, 19603, 14878, 4557]", - "total_badness": 100231.59467 + "ne3d": 82721, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 3, 16, 118, 414, 1347, 3658, 7624, 12768, 17708, 19580, 14908, 4576]", + "total_badness": 100175.28811 } ], "period.geo": [ @@ -2019,34 +2111,34 @@ 150.16 ], "angles_trig": [ - 18.679, - 133.12 + 18.736, + 133.1 ], "ne1d": 344, "ne2d": 1136, - "ne3d": 3263, - "quality_histogram": "[0, 0, 0, 0, 1, 4, 15, 26, 53, 90, 178, 259, 351, 445, 457, 447, 406, 297, 185, 49]", - "total_badness": 4746.5378667 + "ne3d": 3269, + "quality_histogram": "[0, 0, 0, 0, 2, 4, 13, 24, 57, 92, 181, 260, 351, 430, 482, 454, 417, 280, 179, 43]", + "total_badness": 4762.4191481 }, { "angles_tet": [ - 9.7417, - 164.57 + 12.301, + 162.28 ], "angles_trig": [ - 12.303, - 141.6 + 14.714, + 141.01 ], "ne1d": 160, "ne2d": 286, - "ne3d": 568, - "quality_histogram": "[0, 0, 0, 2, 9, 12, 20, 25, 31, 69, 59, 78, 60, 40, 38, 40, 34, 33, 14, 4]", - "total_badness": 1044.7873254 + "ne3d": 598, + "quality_histogram": "[0, 0, 0, 0, 3, 2, 13, 17, 35, 62, 59, 74, 66, 57, 56, 49, 39, 43, 19, 4]", + "total_badness": 1009.5773389 }, { "angles_tet": [ - 13.063, - 161.0 + 11.356, + 162.52 ], "angles_trig": [ 16.741, @@ -2054,9 +2146,9 @@ ], "ne1d": 232, "ne2d": 598, - "ne3d": 1523, - "quality_histogram": "[0, 0, 0, 1, 7, 20, 34, 48, 65, 111, 135, 155, 165, 189, 171, 134, 132, 89, 52, 15]", - "total_badness": 2538.6647915 + "ne3d": 1418, + "quality_histogram": "[0, 0, 0, 2, 9, 14, 27, 47, 66, 97, 109, 150, 161, 159, 147, 133, 119, 96, 66, 16]", + "total_badness": 2344.2576172 }, { "angles_tet": [ @@ -2064,14 +2156,14 @@ 150.16 ], "angles_trig": [ - 19.4, - 134.3 + 19.085, + 134.23 ], "ne1d": 344, "ne2d": 1136, - "ne3d": 3233, - "quality_histogram": "[0, 0, 0, 0, 1, 4, 12, 23, 48, 81, 149, 226, 335, 430, 473, 436, 439, 333, 186, 57]", - "total_badness": 4637.4815537 + "ne3d": 3234, + "quality_histogram": "[0, 0, 0, 0, 2, 4, 12, 25, 48, 85, 148, 219, 353, 420, 468, 466, 428, 322, 182, 52]", + "total_badness": 4649.5043488 }, { "angles_tet": [ @@ -2111,74 +2203,74 @@ 166.97 ], "angles_trig": [ - 1.8776, + 3.5084, 158.16 ], "ne1d": 886, "ne2d": 2592, - "ne3d": 8251, - "quality_histogram": "[4, 9, 37, 43, 43, 52, 43, 48, 107, 147, 263, 410, 617, 958, 1225, 1264, 1209, 1019, 602, 151]", - "total_badness": 12329.19667 + "ne3d": 8292, + "quality_histogram": "[5, 9, 29, 46, 37, 51, 44, 46, 94, 160, 257, 412, 647, 899, 1265, 1378, 1157, 1002, 611, 143]", + "total_badness": 12343.24347 }, { "angles_tet": [ - 1.0836, + 1.0841, 174.05 ], "angles_trig": [ - 2.5669, + 3.4703, 170.0 ], "ne1d": 570, "ne2d": 1202, - "ne3d": 1831, - "quality_histogram": "[2, 29, 38, 60, 65, 77, 105, 141, 152, 176, 189, 161, 156, 135, 118, 80, 71, 48, 24, 4]", - "total_badness": 4620.6249923 + "ne3d": 1795, + "quality_histogram": "[2, 20, 39, 51, 70, 80, 102, 131, 157, 158, 176, 170, 145, 140, 122, 84, 67, 50, 26, 5]", + "total_badness": 4436.3977005 }, { "angles_tet": [ 1.1033, - 172.28 + 171.77 ], "angles_trig": [ 3.728, - 163.66 + 165.5 ], "ne1d": 724, "ne2d": 1730, - "ne3d": 3241, - "quality_histogram": "[3, 15, 33, 52, 50, 37, 49, 80, 128, 163, 207, 267, 344, 390, 425, 377, 308, 193, 92, 28]", - "total_badness": 5992.5764939 + "ne3d": 3290, + "quality_histogram": "[3, 18, 29, 53, 45, 38, 53, 72, 116, 168, 230, 257, 328, 415, 424, 399, 334, 189, 92, 27]", + "total_badness": 6051.57684 }, { "angles_tet": [ - 1.2156, + 1.2134, 169.94 ], "angles_trig": [ - 3.0435, - 165.56 + 2.0839, + 163.08 ], "ne1d": 956, "ne2d": 2828, - "ne3d": 8549, - "quality_histogram": "[3, 9, 39, 50, 44, 51, 59, 59, 82, 133, 195, 336, 560, 830, 1174, 1357, 1462, 1224, 706, 176]", - "total_badness": 12575.367761 + "ne3d": 8481, + "quality_histogram": "[3, 8, 38, 49, 47, 50, 55, 57, 96, 137, 195, 343, 515, 792, 1200, 1366, 1417, 1181, 742, 190]", + "total_badness": 12456.662988 }, { "angles_tet": [ - 1.1519, - 168.31 + 1.1518, + 168.3 ], "angles_trig": [ - 3.4032, - 150.86 + 1.7811, + 158.38 ], "ne1d": 1554, "ne2d": 6372, - "ne3d": 31639, - "quality_histogram": "[2, 8, 13, 8, 24, 53, 51, 73, 92, 189, 305, 634, 1290, 2378, 3824, 5340, 6177, 5876, 4146, 1156]", - "total_badness": 40882.230035 + "ne3d": 31605, + "quality_histogram": "[2, 8, 13, 9, 26, 53, 51, 69, 87, 191, 315, 647, 1221, 2378, 3853, 5328, 6181, 5935, 4068, 1170]", + "total_badness": 40844.172563 }, { "angles_tet": [ @@ -2191,9 +2283,9 @@ ], "ne1d": 2992, "ne2d": 23322, - "ne3d": 281660, - "quality_histogram": "[4, 9, 10, 11, 11, 22, 32, 66, 101, 250, 741, 2131, 5596, 13622, 27762, 44429, 59897, 63856, 48363, 14747]", - "total_badness": 344296.42526 + "ne3d": 281946, + "quality_histogram": "[4, 10, 12, 10, 9, 21, 29, 60, 99, 245, 724, 2133, 5538, 13549, 27663, 44504, 60069, 63936, 48457, 14874]", + "total_badness": 344547.37835 } ], "revolution.geo": [ @@ -2209,38 +2301,38 @@ "ne1d": 320, "ne2d": 3110, "ne3d": 8379, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 3, 27, 91, 210, 453, 679, 983, 1088, 1150, 1165, 1083, 812, 518, 117]", - "total_badness": 11964.310211 + "quality_histogram": "[0, 0, 0, 0, 0, 0, 3, 27, 90, 212, 461, 680, 967, 1096, 1153, 1170, 1080, 802, 520, 118]", + "total_badness": 11967.150699 }, { "angles_tet": [ - 12.622, - 146.3 + 8.7903, + 155.06 ], "angles_trig": [ - 15.111, - 130.65 + 15.518, + 131.14 ], "ne1d": 160, "ne2d": 822, - "ne3d": 1231, - "quality_histogram": "[0, 0, 0, 0, 0, 11, 51, 85, 105, 139, 138, 152, 155, 101, 76, 79, 66, 43, 24, 6]", - "total_badness": 2255.1850071 + "ne3d": 1234, + "quality_histogram": "[0, 0, 0, 1, 0, 10, 62, 86, 121, 132, 145, 139, 147, 99, 95, 67, 61, 38, 23, 8]", + "total_badness": 2290.4515055 }, { "angles_tet": [ - 15.408, + 16.888, 145.04 ], "angles_trig": [ - 14.362, - 134.93 + 17.592, + 134.95 ], "ne1d": 240, "ne2d": 1830, - "ne3d": 3864, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 3, 31, 69, 157, 283, 417, 507, 494, 513, 422, 403, 319, 206, 40]", - "total_badness": 5758.3866216 + "ne3d": 3886, + "quality_histogram": "[0, 0, 0, 0, 0, 1, 3, 27, 70, 177, 288, 405, 522, 502, 496, 407, 393, 336, 210, 49]", + "total_badness": 5799.4091506 }, { "angles_tet": [ @@ -2253,9 +2345,9 @@ ], "ne1d": 320, "ne2d": 3110, - "ne3d": 8253, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 2, 13, 47, 138, 333, 624, 839, 1047, 1151, 1187, 1166, 945, 608, 153]", - "total_badness": 11468.848993 + "ne3d": 8258, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 2, 13, 48, 141, 339, 627, 829, 1041, 1149, 1209, 1156, 945, 605, 154]", + "total_badness": 11481.414846 }, { "angles_tet": [ @@ -2270,43 +2362,43 @@ "ne2d": 6864, "ne3d": 33174, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 2, 2, 52, 185, 571, 1253, 2563, 4038, 5675, 6688, 6259, 4556, 1330]", - "total_badness": 41696.548837 + "total_badness": 41696.548838 }, { "angles_tet": [ - 21.857, + 21.902, 143.83 ], "angles_trig": [ - 19.774, + 19.492, 121.07 ], "ne1d": 800, "ne2d": 17934, - "ne3d": 201342, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 4, 13, 87, 366, 1250, 3570, 9060, 18977, 31022, 42571, 46627, 36250, 11545]", - "total_badness": 244286.77424 + "ne3d": 201307, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 4, 11, 83, 359, 1237, 3580, 9039, 19000, 30994, 42542, 46628, 36256, 11574]", + "total_badness": 244220.94708 } ], "screw.step": [ { "angles_tet": [ - 15.138, - 148.37 + 15.139, + 148.36 ], "angles_trig": [ - 17.355, + 17.363, 140.59 ], "ne1d": 400, "ne2d": 1436, - "ne3d": 2341, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 15, 60, 96, 166, 202, 251, 312, 271, 259, 270, 191, 140, 90, 18]", - "total_badness": 3714.0468775 + "ne3d": 2342, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 16, 61, 92, 165, 209, 246, 307, 280, 262, 272, 182, 148, 84, 18]", + "total_badness": 3718.1755695 }, { "angles_tet": [ - 21.55, + 19.077, 146.38 ], "angles_trig": [ @@ -2315,24 +2407,24 @@ ], "ne1d": 528, "ne2d": 2792, - "ne3d": 8129, - "quality_histogram": "[0, 0, 0, 0, 0, 1, 4, 8, 35, 96, 188, 298, 537, 817, 1057, 1323, 1446, 1284, 798, 237]", - "total_badness": 10753.086327 + "ne3d": 8126, + "quality_histogram": "[0, 0, 0, 0, 0, 1, 4, 11, 33, 98, 190, 299, 553, 796, 1040, 1329, 1446, 1294, 795, 237]", + "total_badness": 10753.750681 }, { "angles_tet": [ 20.515, - 144.02 + 144.03 ], "angles_trig": [ - 20.575, - 124.46 + 24.891, + 120.48 ], "ne1d": 666, "ne2d": 4922, - "ne3d": 31526, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 2, 6, 35, 90, 306, 711, 1685, 3214, 4996, 6794, 6910, 5193, 1584]", - "total_badness": 38669.670326 + "ne3d": 31546, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 5, 29, 94, 294, 701, 1778, 3224, 4988, 6720, 6967, 5142, 1603]", + "total_badness": 38702.936245 } ], "sculpture.geo": [ @@ -2417,76 +2509,76 @@ 147.57 ], "angles_trig": [ - 16.998, + 17.184, 119.06 ], "ne1d": 480, "ne2d": 2394, "ne3d": 6711, - "quality_histogram": "[0, 0, 0, 0, 1, 2, 5, 9, 18, 27, 60, 127, 255, 455, 730, 1074, 1345, 1332, 952, 319]", - "total_badness": 8467.8828851 + "quality_histogram": "[0, 0, 0, 0, 2, 2, 7, 8, 21, 26, 61, 123, 262, 462, 722, 1073, 1348, 1317, 960, 317]", + "total_badness": 8476.8430085 } ], "shaft.geo": [ { "angles_tet": [ - 8.741, - 169.01 - ], - "angles_trig": [ - 9.2536, - 148.93 - ], - "ne1d": 708, - "ne2d": 1722, - "ne3d": 2780, - "quality_histogram": "[0, 0, 1, 10, 28, 29, 48, 46, 95, 147, 260, 397, 330, 277, 253, 288, 248, 181, 109, 33]", - "total_badness": 4601.9915591 - }, - { - "angles_tet": [ - 14.566, - 156.38 - ], - "angles_trig": [ - 17.101, - 120.3 - ], - "ne1d": 410, - "ne2d": 606, - "ne3d": 870, - "quality_histogram": "[0, 0, 0, 0, 0, 3, 2, 5, 22, 36, 42, 53, 91, 101, 127, 131, 94, 85, 47, 31]", - "total_badness": 1263.6238885 - }, - { - "angles_tet": [ - 9.5449, - 167.3 - ], - "angles_trig": [ - 10.216, - 148.27 - ], - "ne1d": 510, - "ne2d": 1004, - "ne3d": 2013, - "quality_histogram": "[0, 0, 2, 11, 38, 79, 90, 131, 106, 114, 113, 172, 155, 218, 217, 207, 190, 98, 58, 14]", - "total_badness": 3713.7502435 - }, - { - "angles_tet": [ - 11.328, + 8.3078, 162.65 ], "angles_trig": [ - 13.746, - 135.37 + 9.3916, + 147.77 ], "ne1d": 708, "ne2d": 1722, - "ne3d": 2720, - "quality_histogram": "[0, 0, 0, 1, 3, 2, 11, 23, 52, 119, 264, 396, 344, 289, 274, 315, 272, 207, 111, 37]", - "total_badness": 4172.9994061 + "ne3d": 2734, + "quality_histogram": "[0, 0, 2, 4, 8, 15, 30, 41, 85, 143, 291, 393, 333, 284, 246, 293, 248, 190, 101, 27]", + "total_badness": 4392.4206713 + }, + { + "angles_tet": [ + 15.154, + 159.78 + ], + "angles_trig": [ + 17.101, + 134.17 + ], + "ne1d": 410, + "ne2d": 606, + "ne3d": 791, + "quality_histogram": "[0, 0, 0, 0, 2, 3, 4, 7, 33, 42, 54, 61, 88, 86, 118, 92, 89, 72, 29, 11]", + "total_badness": 1208.0644901 + }, + { + "angles_tet": [ + 12.907, + 158.51 + ], + "angles_trig": [ + 11.963, + 148.8 + ], + "ne1d": 510, + "ne2d": 1004, + "ne3d": 1859, + "quality_histogram": "[0, 0, 0, 3, 7, 29, 43, 74, 68, 95, 115, 158, 153, 213, 250, 232, 206, 107, 81, 25]", + "total_badness": 3021.4076425 + }, + { + "angles_tet": [ + 14.303, + 162.65 + ], + "angles_trig": [ + 15.512, + 147.17 + ], + "ne1d": 708, + "ne2d": 1722, + "ne3d": 2708, + "quality_histogram": "[0, 0, 0, 1, 3, 1, 11, 22, 50, 108, 258, 412, 344, 299, 275, 311, 271, 203, 108, 31]", + "total_badness": 4149.681437 }, { "angles_tet": [ @@ -2505,18 +2597,18 @@ }, { "angles_tet": [ - 25.341, - 142.09 + 22.61, + 139.73 ], "angles_trig": [ - 22.461, - 120.2 + 22.297, + 118.87 ], "ne1d": 1792, "ne2d": 10600, - "ne3d": 63811, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 2, 22, 127, 439, 1283, 3078, 6205, 10007, 13534, 14326, 11042, 3746]", - "total_badness": 77678.178377 + "ne3d": 63839, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 4, 22, 130, 424, 1292, 3122, 6208, 9992, 13576, 14305, 11041, 3723]", + "total_badness": 77729.577133 } ], "sphere.geo": [ @@ -2644,18 +2736,18 @@ }, { "angles_tet": [ - 8.1657, - 165.53 + 9.1579, + 165.65 ], "angles_trig": [ - 9.2408, - 143.87 + 8.211, + 143.3 ], "ne1d": 30, "ne2d": 116, - "ne3d": 262, - "quality_histogram": "[0, 0, 4, 26, 30, 52, 33, 16, 22, 13, 11, 12, 11, 8, 6, 7, 6, 3, 2, 0]", - "total_badness": 832.73325512 + "ne3d": 264, + "quality_histogram": "[0, 0, 7, 21, 33, 55, 37, 14, 20, 15, 10, 6, 13, 10, 6, 7, 5, 3, 2, 0]", + "total_badness": 850.25370446 }, { "angles_tet": [ @@ -2713,8 +2805,68 @@ 0.0, 0.0 ], - "ne1d": 35, - "ne2d": 121, + "ne1d": 31, + "ne2d": 99, + "ne3d": 0, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + "total_badness": 0.0 + }, + { + "angles_tet": [ + 0.0, + 0.0 + ], + "angles_trig": [ + 0.0, + 0.0 + ], + "ne1d": 27, + "ne2d": 75, + "ne3d": 0, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + "total_badness": 0.0 + }, + { + "angles_tet": [ + 0.0, + 0.0 + ], + "angles_trig": [ + 0.0, + 0.0 + ], + "ne1d": 25, + "ne2d": 85, + "ne3d": 0, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + "total_badness": 0.0 + }, + { + "angles_tet": [ + 0.0, + 0.0 + ], + "angles_trig": [ + 0.0, + 0.0 + ], + "ne1d": 31, + "ne2d": 99, + "ne3d": 0, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + "total_badness": 0.0 + }, + { + "angles_tet": [ + 0.0, + 0.0 + ], + "angles_trig": [ + 0.0, + 0.0 + ], + "ne1d": 41, + "ne2d": 177, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -2729,67 +2881,7 @@ 0.0 ], "ne1d": 30, - "ne2d": 98, - "ne3d": 0, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", - "total_badness": 0.0 - }, - { - "angles_tet": [ - 0.0, - 0.0 - ], - "angles_trig": [ - 0.0, - 0.0 - ], - "ne1d": 30, - "ne2d": 96, - "ne3d": 0, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", - "total_badness": 0.0 - }, - { - "angles_tet": [ - 0.0, - 0.0 - ], - "angles_trig": [ - 0.0, - 0.0 - ], - "ne1d": 35, - "ne2d": 121, - "ne3d": 0, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", - "total_badness": 0.0 - }, - { - "angles_tet": [ - 0.0, - 0.0 - ], - "angles_trig": [ - 0.0, - 0.0 - ], - "ne1d": 44, - "ne2d": 190, - "ne3d": 0, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", - "total_badness": 0.0 - }, - { - "angles_tet": [ - 0.0, - 0.0 - ], - "angles_trig": [ - 0.0, - 0.0 - ], - "ne1d": 58, - "ne2d": 396, + "ne2d": 108, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -2806,22 +2898,7 @@ 0.0 ], "ne1d": 32, - "ne2d": 158, - "ne3d": 0, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", - "total_badness": 0.0 - }, - { - "angles_tet": [ - 0.0, - 0.0 - ], - "angles_trig": [ - 0.0, - 0.0 - ], - "ne1d": 32, - "ne2d": 130, + "ne2d": 148, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -2851,7 +2928,7 @@ 0.0 ], "ne1d": 32, - "ne2d": 158, + "ne2d": 148, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -2865,8 +2942,8 @@ 0.0, 0.0 ], - "ne1d": 45, - "ne2d": 313, + "ne1d": 32, + "ne2d": 148, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -2880,8 +2957,23 @@ 0.0, 0.0 ], - "ne1d": 81, - "ne2d": 843, + "ne1d": 43, + "ne2d": 300, + "ne3d": 0, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + "total_badness": 0.0 + }, + { + "angles_tet": [ + 0.0, + 0.0 + ], + "angles_trig": [ + 0.0, + 0.0 + ], + "ne1d": 79, + "ne2d": 821, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -2898,7 +2990,7 @@ 0.0 ], "ne1d": 32, - "ne2d": 134, + "ne2d": 124, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -2913,7 +3005,7 @@ 0.0 ], "ne1d": 32, - "ne2d": 108, + "ne2d": 116, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -2928,7 +3020,7 @@ 0.0 ], "ne1d": 32, - "ne2d": 118, + "ne2d": 124, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -2943,7 +3035,7 @@ 0.0 ], "ne1d": 32, - "ne2d": 134, + "ne2d": 124, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -2957,8 +3049,8 @@ 0.0, 0.0 ], - "ne1d": 45, - "ne2d": 253, + "ne1d": 43, + "ne2d": 249, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -2972,8 +3064,8 @@ 0.0, 0.0 ], - "ne1d": 81, - "ne2d": 709, + "ne1d": 79, + "ne2d": 687, "ne3d": 0, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", "total_badness": 0.0 @@ -2997,18 +3089,18 @@ }, { "angles_tet": [ - 1.8454, - 174.07 + 1.9786, + 173.68 ], "angles_trig": [ - 3.7635, - 166.86 + 3.8198, + 165.45 ], "ne1d": 0, "ne2d": 692, - "ne3d": 3289, - "quality_histogram": "[22, 357, 521, 490, 397, 337, 276, 212, 158, 125, 86, 85, 59, 35, 37, 25, 30, 23, 11, 3]", - "total_badness": 17806.714921 + "ne3d": 2726, + "quality_histogram": "[19, 190, 366, 339, 352, 304, 237, 182, 157, 143, 110, 86, 53, 46, 34, 43, 30, 24, 10, 1]", + "total_badness": 13096.6735 }, { "angles_tet": [ @@ -3021,9 +3113,9 @@ ], "ne1d": 0, "ne2d": 1446, - "ne3d": 2732, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 2, 1, 14, 50, 134, 229, 351, 416, 425, 383, 313, 197, 166, 51]", - "total_badness": 3892.9500522 + "ne3d": 2739, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 2, 2, 15, 48, 132, 221, 357, 421, 424, 382, 318, 204, 161, 52]", + "total_badness": 3899.5318975 }, { "angles_tet": [ @@ -3083,14 +3175,14 @@ ], "ne1d": 690, "ne2d": 1684, - "ne3d": 5193, - "quality_histogram": "[0, 0, 1, 1, 1, 11, 32, 47, 111, 196, 275, 365, 465, 566, 668, 699, 617, 545, 452, 141]", - "total_badness": 7518.4794095 + "ne3d": 5178, + "quality_histogram": "[0, 0, 1, 0, 1, 8, 29, 37, 107, 193, 284, 368, 461, 566, 670, 689, 621, 536, 462, 145]", + "total_badness": 7465.8398023 }, { "angles_tet": [ - 8.1301, - 160.14 + 8.0938, + 167.14 ], "angles_trig": [ 7.7605, @@ -3098,9 +3190,9 @@ ], "ne1d": 390, "ne2d": 522, - "ne3d": 1353, - "quality_histogram": "[0, 0, 3, 13, 12, 38, 80, 115, 128, 149, 170, 126, 139, 107, 87, 85, 54, 34, 11, 2]", - "total_badness": 2731.8393348 + "ne3d": 1347, + "quality_histogram": "[0, 0, 4, 14, 14, 41, 75, 117, 125, 145, 169, 126, 140, 107, 82, 87, 54, 34, 11, 2]", + "total_badness": 2735.6699238 }, { "angles_tet": [ @@ -3113,9 +3205,9 @@ ], "ne1d": 512, "ne2d": 874, - "ne3d": 2381, - "quality_histogram": "[0, 0, 0, 3, 9, 15, 42, 68, 122, 139, 198, 209, 307, 380, 349, 235, 137, 97, 46, 25]", - "total_badness": 3929.5802554 + "ne3d": 2395, + "quality_histogram": "[0, 0, 0, 3, 9, 13, 42, 68, 129, 146, 191, 209, 315, 390, 343, 236, 134, 94, 49, 24]", + "total_badness": 3955.4970644 }, { "angles_tet": [ @@ -3128,14 +3220,14 @@ ], "ne1d": 690, "ne2d": 1684, - "ne3d": 5099, - "quality_histogram": "[0, 0, 1, 1, 0, 3, 23, 37, 102, 189, 265, 343, 430, 564, 673, 707, 612, 543, 465, 141]", - "total_badness": 7304.8063731 + "ne3d": 5095, + "quality_histogram": "[0, 0, 1, 0, 0, 4, 19, 34, 101, 181, 263, 354, 439, 548, 689, 696, 611, 547, 467, 141]", + "total_badness": 7282.7477811 }, { "angles_tet": [ - 6.8825, - 166.88 + 16.895, + 145.94 ], "angles_trig": [ 17.568, @@ -3143,13 +3235,13 @@ ], "ne1d": 1050, "ne2d": 3812, - "ne3d": 17990, - "quality_histogram": "[0, 0, 1, 0, 0, 0, 3, 15, 34, 64, 183, 570, 1424, 2206, 2299, 2682, 2720, 2713, 2367, 709]", - "total_badness": 23464.671179 + "ne3d": 18028, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 3, 15, 34, 62, 183, 573, 1437, 2190, 2345, 2680, 2713, 2701, 2419, 673]", + "total_badness": 23515.317943 }, { "angles_tet": [ - 14.338, + 15.34, 149.41 ], "angles_trig": [ @@ -3158,26 +3250,26 @@ ], "ne1d": 1722, "ne2d": 10042, - "ne3d": 84837, - "quality_histogram": "[0, 0, 0, 0, 0, 3, 55, 1435, 719, 373, 691, 1186, 2492, 5459, 8935, 13171, 16437, 16966, 12825, 4090]", - "total_badness": 108583.90765 + "ne3d": 84906, + "quality_histogram": "[0, 0, 0, 0, 0, 2, 49, 1422, 720, 370, 704, 1186, 2437, 5535, 8984, 13266, 16409, 16882, 12872, 4068]", + "total_badness": 108652.77514 } ], "twobricks.geo": [ { "angles_tet": [ - 22.934, - 142.89 + 29.453, + 134.56 ], "angles_trig": [ - 18.806, - 142.29 + 26.574, + 91.538 ], "ne1d": 72, "ne2d": 50, - "ne3d": 41, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 8, 2, 16, 3, 4, 0, 0, 0, 0]", - "total_badness": 68.897088924 + "ne3d": 36, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 8, 2, 18, 2, 4, 0, 0, 0, 0]", + "total_badness": 55.618194358 }, { "angles_tet": [ @@ -3211,18 +3303,18 @@ }, { "angles_tet": [ - 22.934, - 142.89 + 29.453, + 134.56 ], "angles_trig": [ - 18.806, - 142.29 + 26.574, + 91.538 ], "ne1d": 72, "ne2d": 50, - "ne3d": 41, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 8, 2, 16, 3, 4, 0, 0, 0, 0]", - "total_badness": 68.897088924 + "ne3d": 36, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 8, 2, 18, 2, 4, 0, 0, 0, 0]", + "total_badness": 55.618194358 }, { "angles_tet": [ @@ -3252,24 +3344,24 @@ "ne2d": 346, "ne3d": 595, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 23, 40, 55, 95, 101, 105, 99, 60, 8]", - "total_badness": 777.63275563 + "total_badness": 777.63275434 } ], "twocubes.geo": [ { "angles_tet": [ - 22.934, - 142.89 + 29.453, + 134.56 ], "angles_trig": [ - 18.806, - 142.29 + 26.574, + 91.538 ], "ne1d": 72, "ne2d": 50, - "ne3d": 41, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 8, 2, 16, 3, 4, 0, 0, 0, 0]", - "total_badness": 68.897088924 + "ne3d": 36, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 8, 2, 18, 2, 4, 0, 0, 0, 0]", + "total_badness": 55.618194358 }, { "angles_tet": [ @@ -3303,18 +3395,18 @@ }, { "angles_tet": [ - 22.934, - 142.89 + 29.453, + 134.56 ], "angles_trig": [ - 18.806, - 142.29 + 26.574, + 91.538 ], "ne1d": 72, "ne2d": 50, - "ne3d": 41, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 8, 2, 16, 3, 4, 0, 0, 0, 0]", - "total_badness": 68.897088924 + "ne3d": 36, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 8, 2, 18, 2, 4, 0, 0, 0, 0]", + "total_badness": 55.618194358 }, { "angles_tet": [ @@ -3344,7 +3436,7 @@ "ne2d": 346, "ne3d": 595, "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 23, 40, 55, 95, 101, 105, 99, 60, 8]", - "total_badness": 777.63275563 + "total_badness": 777.63275434 } ], "twocyl.geo": [ @@ -3365,33 +3457,33 @@ }, { "angles_tet": [ - 18.404, - 153.38 + 20.148, + 153.82 ], "angles_trig": [ - 26.007, - 121.51 + 25.599, + 118.3 ], "ne1d": 68, "ne2d": 100, - "ne3d": 150, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 2, 2, 4, 6, 12, 12, 8, 13, 24, 18, 18, 23, 4, 4]", - "total_badness": 222.6797729 + "ne3d": 135, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 0, 4, 5, 2, 9, 5, 18, 25, 16, 22, 20, 7, 1]", + "total_badness": 190.82756065 }, { "angles_tet": [ - 12.34, - 161.32 + 12.268, + 164.96 ], "angles_trig": [ - 12.084, - 152.05 + 15.698, + 145.1 ], "ne1d": 102, "ne2d": 238, - "ne3d": 497, - "quality_histogram": "[0, 0, 0, 2, 11, 31, 40, 56, 65, 43, 34, 21, 26, 25, 30, 31, 43, 28, 8, 3]", - "total_badness": 1042.5778658 + "ne3d": 471, + "quality_histogram": "[0, 0, 1, 6, 3, 23, 25, 45, 63, 40, 34, 29, 30, 34, 27, 33, 43, 25, 8, 2]", + "total_badness": 950.55701299 }, { "angles_tet": [ @@ -3410,18 +3502,18 @@ }, { "angles_tet": [ - 19.806, - 141.75 + 21.286, + 140.31 ], "angles_trig": [ - 22.382, - 115.67 + 22.446, + 114.89 ], "ne1d": 214, "ne2d": 910, - "ne3d": 1906, - "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 2, 14, 28, 73, 129, 185, 278, 363, 378, 262, 155, 38]", - "total_badness": 2512.811709 + "ne3d": 1889, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 2, 13, 31, 69, 126, 193, 280, 351, 341, 277, 168, 37]", + "total_badness": 2489.1406753 }, { "angles_tet": [ diff --git a/tests/pytest/test_boundarylayer.py b/tests/pytest/test_boundarylayer.py new file mode 100644 index 00000000..798cb1cc --- /dev/null +++ b/tests/pytest/test_boundarylayer.py @@ -0,0 +1,160 @@ + +import pytest +from netgen.csg import * + +def GetNSurfaceElements(mesh, boundaries, adjacent_domain=None): + nse_in_layer = 0 + for el in mesh.Elements2D(): + if mesh.GetBCName(el.index-1) in boundaries: + if adjacent_domain is None: + nse_in_layer += 1 + else: + if (mesh.FaceDescriptor(el.index).domin > 0 and mesh.GetMaterial(mesh.FaceDescriptor(el.index).domin) == adjacent_domain) or (mesh.FaceDescriptor(el.index).domout > 0 and mesh.GetMaterial(mesh.FaceDescriptor(el.index).domout) == adjacent_domain): + nse_in_layer += 1 + return nse_in_layer + +@pytest.mark.parametrize("outside", [True, False]) +def test_boundarylayer(outside, capfd): + mesh = unit_cube.GenerateMesh(maxh=0.3) + ne_before = mesh.ne + layer_surfacenames = ["right", "top", "left", "back", "bottom"] + mesh.BoundaryLayer("|".join(layer_surfacenames), [0.01, 0.01], "layer", outside=outside) + + should_ne = ne_before + 2 * GetNSurfaceElements(mesh, layer_surfacenames) + assert mesh.ne == should_ne + capture = capfd.readouterr() + assert not "elements are not matching" in capture.out + + for side in ["front"]: + mesh.BoundaryLayer(side, [0.001, 0.001], "layer", outside=outside) + should_ne += 2 * GetNSurfaceElements(mesh, [side]) + assert mesh.ne == should_ne + capture = capfd.readouterr() + assert not "elements are not matching" in capture.out + +@pytest.mark.parametrize("outside", [True, False]) +@pytest.mark.parametrize("version", [1, 2]) # version 2 not working yet +def test_boundarylayer2(outside, version, capfd): + geo = CSGeometry() + top = Plane(Pnt(0,0,0.5), Vec(0,0,1)) + bot = Plane(Pnt(0,0,0.1), Vec(0,0,-1)) + bigpart = OrthoBrick(Pnt(-5,-5,0), Pnt(1,1,1)) + part = bigpart* top * bot + outer = ((OrthoBrick(Pnt(-1,-1,-1), Pnt(2,2,3)).bc("outer") * Plane(Pnt(2,2,2), Vec(0,0,1)).bc("outertop"))) + + geo.Add((part * outer).mat("part")) + if version == 1: + geo.Add((outer-part).mat("rest")) + else: + geo.Add((outer-top).mat("rest")) + geo.Add((outer-bot).mat("rest")) + geo.Add((outer*top*bot-bigpart).mat("rest")) + + geo.CloseSurfaces(top, bot, []) + mesh = geo.GenerateMesh() + should_ne = mesh.ne + 2 * GetNSurfaceElements(mesh, ["default"], "part") + layersize = 0.025 + mesh.BoundaryLayer("default", [layersize, layersize], "part", domains="part", outside=outside) + assert mesh.ne == should_ne + assert not "elements are not matching" in capfd.readouterr().out + import netgen.gui + ngs = pytest.importorskip("ngsolve") + ngs.Draw(ngs.Mesh(mesh)) + mesh = ngs.Mesh(mesh) + assert ngs.Integrate(1, mesh.Materials("part")) == pytest.approx(0.5*2.05*2.05 if outside else 0.4*2*2) + assert ngs.Integrate(1, mesh) == pytest.approx(3**3) + + +@pytest.mark.parametrize("outside", [True, False]) +def test_wrong_orientation(outside, capfd): + geo = CSGeometry() + brick = OrthoBrick((-1,0,0),(1,1,1)) - Plane((0,0,0), (1,0,0)) + geo.Add(brick.mat("air")) + + mesh = geo.GenerateMesh() + + mesh.BoundaryLayer(".*", 0.1, "air", domains="air", outside=outside) + ngs = pytest.importorskip("ngsolve") + mesh = ngs.Mesh(mesh) + assert ngs.Integrate(1, mesh) == pytest.approx(1.2**3 if outside else 1) + +def test_splitted_surface(): + geo = CSGeometry() + + brick = OrthoBrick((0,0,0), (1,1,1)) + slots = OrthoBrick((0.2,0,-1), (0.4, 1, 2)) + OrthoBrick((0.6, 0,-1), (0.8, 1,2)) + geo.Add((brick-slots).mat("block")) + geo.Add((brick*slots).mat("slot")) + + mesh = geo.GenerateMesh() + mesh.BoundaryLayer(".*", [0.001, 0.001], "block", "block", outside=False) + ngs = pytest.importorskip("ngsolve") + mesh = ngs.Mesh(mesh) + assert ngs.Integrate(1, mesh) == pytest.approx(1) + assert ngs.Integrate(1, mesh.Materials("slot")) == pytest.approx(0.4) + +@pytest.mark.parametrize("outside", [True, False]) +def test_pyramids(outside): + geo = CSGeometry() + box = OrthoBrick((0,0,0), (1,1,1)) + plate = OrthoBrick((0.3,0.3,0.4),(0.7,0.7,1)) * Plane((0,0,0.6), (0,0,1)).bc("top") + geo.Add((box-plate).mat("air")) + geo.Add(plate.mat("plate")) + mesh = geo.GenerateMesh() + mesh.BoundaryLayer("top", [0.01], "layer", "plate", outside=outside) + ngs = pytest.importorskip("ngsolve") + mesh = ngs.Mesh(mesh) + assert ngs.Integrate(1, mesh.Materials("plate")) == pytest.approx(0.032 if outside else 0.0304) + assert ngs.Integrate(1, mesh.Materials("layer")) == pytest.approx(0.0016) + assert ngs.Integrate(1, mesh.Materials("air")) == pytest.approx(0.9664 if outside else 0.968) + +@pytest.mark.parametrize("outside", [True, False]) +def test_with_inner_corner(outside, capfd): + geo = CSGeometry() + + core_thickness = 0.1 + limb_distance = 0.5 + core_height = 0.5 + coil_r1 = 0.08 + coil_r2 = 0.16 + coil_h = 0.2 + domain_size = 1.2 + domain_size_y = 0.7 + + def CreateCoil(x): + outer = Cylinder((x, 0, -1), (x, 0, 1), coil_r2) + inner = Cylinder((x, 0, -1), (x, 0, 1), coil_r1) + top = Plane((0,0,coil_h/2), (0,0,1)) + bot = Plane((0,0,-coil_h/2), (0,0,-1)) + return ((outer - inner) * top * bot, (outer, inner, top, bot)) + + core_front = Plane((0,-core_thickness/2, 0), (0,-1,0)).bc("core_front") + core_back = Plane((0,core_thickness/2, 0), (0,1,0)).bc("core_front") + core_limb1 = OrthoBrick((-limb_distance/2-core_thickness/2, -core_thickness, -core_height/2),(-limb_distance/2+core_thickness/2, core_thickness, core_height/2)) + core_limb2 = OrthoBrick((limb_distance/2-core_thickness/2, -core_thickness, -core_height/2),(limb_distance/2+core_thickness/2, core_thickness, core_height/2)) + core_top = OrthoBrick((-limb_distance/2-core_thickness/2, -core_thickness, core_height/2-core_thickness/2),(limb_distance/2+core_thickness/2, core_thickness, core_height/2+core_thickness/2)) + core_bot = OrthoBrick((-limb_distance/2-core_thickness/2, -core_thickness, -core_height/2-core_thickness/2),(limb_distance/2+core_thickness/2, core_thickness, -core_height/2+core_thickness/2)) + + core = (core_limb1 + core_limb2 + core_top + core_bot).bc("core_rest") + core = core * core_front * core_back + core.maxh(core_thickness * 0.4) + + coil1, (outer1, inner1, top1, bot1) = CreateCoil(-limb_distance/2) + coil1.mat("coil_1") + coil2, (outer2, inner2, top2, bot2) = CreateCoil(limb_distance/2) + coil2.mat("coil_2") + + oil = OrthoBrick((-domain_size/2, -domain_size_y/2, -domain_size/2), (domain_size/2, domain_size_y/2, domain_size/2)).bc("tank") - core # - coil1 - coil2 + + geo.Add(core.mat("core"), col=(0.4,0.4,0.4)) + geo.Add(coil1, col=(0.72, 0.45, 0.2)) + geo.Add(coil2, col=(0.72, 0.45, 0.2)) + geo.Add(oil.mat("oil"), transparent=True) + mesh = geo.GenerateMesh() + mesh.BoundaryLayer("core_front", [0.001, 0.002], "core", "core", outside=outside) + ngs = pytest.importorskip("ngsolve") + mesh = ngs.Mesh(mesh) + capture = capfd.readouterr() + assert not "elements are not matching" in capture.out + assert ngs.Integrate(1, mesh.Materials("core")) == pytest.approx(0.0212 if outside else 0.02) + assert ngs.Integrate(1, mesh.Materials("oil")) == pytest.approx(0.9868 if outside else 0.988) diff --git a/tests/pytest/test_csg.py b/tests/pytest/test_csg.py new file mode 100644 index 00000000..7cfc9331 --- /dev/null +++ b/tests/pytest/test_csg.py @@ -0,0 +1,26 @@ +from netgen.csg import * + +def test_2_polyhedra(): + geo = CSGeometry() + first = Polyhedron([(0,0,0), (0,1,0), (3,1,0), (3,0,0), + (0,1,1), (3,1,1), (3,0,1), (0,0,1)], + [(0,1,2,3), (1,4,5,2), (2,5,6,3), (3,6,7,0), + (0,7,4,1), (7,6,5,4)]) + # TODO: height = 0.1 not working! + height = 0.3 + second = Polyhedron([(0,0,1), (0,1,1), (3,1,1), (3,0,1), + (0,1,1+height), (3,1,1+height), + (3,0,1+height), (0,0,1+height)], + [(0,1,2,3), (1,4,5,2), (2,5,6,3), (3,6,7,0), + (0,7,4,1), (7,6,5,4)]) + + geo.Add(first) + geo.Add(second) + mesh = geo.GenerateMesh() + return mesh + + +if __name__ == "__main__": + from ngsolve import Mesh, Draw + mesh = Mesh(test_2_polyhedra()) + Draw(mesh) diff --git a/tests/pytest/test_csg2d.py b/tests/pytest/test_csg2d.py new file mode 100644 index 00000000..9e05682b --- /dev/null +++ b/tests/pytest/test_csg2d.py @@ -0,0 +1,115 @@ +from netgen.geom2d import * +import pytest +import math +from pytest import approx +from pytest_check import check + + +def check_area(geo, area): + if isinstance(geo, Solid2d): + g = CSG2d() + g.Add(geo) + geo = g + + m = geo.GenerateMesh(maxh=0.2) + ngs = pytest.importorskip("ngsolve") + mesh = ngs.Mesh(m) + mesh.Curve(5) + with check: assert ngs.Integrate(1.0, mesh) == approx(area) + +def test_two_circles(): + c1 = Circle(center=(0,0), radius=1) + c2 = c1.Rotate(45) + s = c1*c2 + geo = CSG2d() + geo.Add(s) + m = geo.GenerateMesh() + assert len(m.Elements2D()) > 0 + + ngs = pytest.importorskip("ngsolve") + mesh = ngs.Mesh(m) + mesh.Curve(5) + assert ngs.Integrate(1.0, mesh) == approx(math.pi) + ngs.Draw(mesh) + +def test_two_edge(): + s = Solid2d( [(-1,0), cp(0,1), (1,0), cp(0,2)] ) + geo = CSG2d() + geo.Add(s) + m = geo.GenerateMesh() + assert len(m.Elements2D()) > 0 + + ngs = pytest.importorskip("ngsolve") + g = geo.GenerateSplineGeometry() + ngs.Draw(g) + mesh = ngs.Mesh(m) + mesh.Curve(5) + ngs.Draw(mesh) + +def test_trig_and_circle(): + g = CSG2d() + + trig = Solid2d( [(0,0), (1,1), (-1,1) ] ).BC("diamond") + circle = Circle( center=(0,0.101), radius=0.1).BC("circle") # TODO: Failing with center=(0,0.1) + + d = trig-circle + g.Add(d) + g.Add(circle) + + m = g.GenerateMesh(maxh=0.1) + assert len(m.Elements2D()) > 0 + + ngs = pytest.importorskip("ngsolve") + geo = g.GenerateSplineGeometry() + ngs.Draw(geo) + + mesh = ngs.Mesh(m) + mesh.Curve(3) + ngs.Draw(mesh) + + +def test_circle_plus_rect(): + circle = Circle( center=(0,0), radius=1 ) + rect = Rectangle( pmin=(-0.5,0.0), pmax=(0.5,0.5) ) + + geo = CSG2d() + geo.Add(circle+rect) + m = geo.GenerateMesh(maxh=0.2) + + + ngs = pytest.importorskip("ngsolve") + mesh = ngs.Mesh(m) + mesh.Curve(5) + assert ngs.Integrate(1.0, mesh) == approx(math.pi) + +def test_circle_plus_rect1(): + circle = Circle( center=(0,0), radius=1 ) + rect = Rectangle( pmin=(-0.5,-0.5), pmax=(0.5,0.5) ) + + geo = CSG2d() + geo.Add(circle+rect) + m = geo.GenerateMesh(maxh=0.2) + + + ngs = pytest.importorskip("ngsolve") + mesh = ngs.Mesh(m) + mesh.Curve(5) + assert ngs.Integrate(1.0, mesh) == approx(math.pi) + +def test_circle_and_rect(): + c = Circle(center=(0,0),radius=1) + r = Rectangle((0,0),(1,1)) + + pi = math.pi + check_area(c-r, 3/4*pi) + check_area(c*r, 1/4*pi) + check_area(c+r, 3/4*pi+1) + check_area(r*c, 1/4*pi) + check_area(r+c, 3/4*pi+1) + check_area(r-c, 1-1/4*pi) + + +if __name__ == "__main__": + test_two_circles() + test_two_edge() + test_trig_and_circle() diff --git a/tests/pytest/test_geom2d.py b/tests/pytest/test_geom2d.py new file mode 100644 index 00000000..d40fd40a --- /dev/null +++ b/tests/pytest/test_geom2d.py @@ -0,0 +1,13 @@ +from netgen.geom2d import SplineGeometry + + +def test_leftdom_equals_rightdom(): + geo = SplineGeometry() + pnts = [(0,0), (1,0), (2,0), (2,1), (1,1), (0,1)] + gp = [geo.AppendPoint(*p) for p in pnts] + lines = [(0,1,0), (1,2,0), (2,3,0), (3,4,0), (4,5,0), (5,0,0), (1,4,1)] + for p1, p2, rd in lines: + geo.Append(["line", p1, p2], leftdomain=1, rightdomain=rd) + + mesh = geo.GenerateMesh() + diff --git a/tests/pytest/test_mpi4py.py b/tests/pytest/test_mpi4py.py new file mode 100644 index 00000000..b4c85902 --- /dev/null +++ b/tests/pytest/test_mpi4py.py @@ -0,0 +1,21 @@ +import pytest +import netgen.meshing + +mpi4py = pytest.importorskip("mpi4py") + +@pytest.mark.mpi +def test_mpi4py(): + comm = mpi4py.MPI.COMM_WORLD + + if comm.rank==0: + from netgen.csg import unit_cube + m = unit_cube.GenerateMesh(maxh=0.1) + m.Save("mpimesh") + + comm.Barrier() + + mesh = netgen.meshing.Mesh(3, comm) + mesh.Load("mpimesh.vol.gz") + + if comm.rank==0: + assert mesh.ne==0 diff --git a/tests/pytest/test_splinegeo_tensordomainmeshing.py b/tests/pytest/test_splinegeo_tensordomainmeshing.py new file mode 100644 index 00000000..0c00ae7d --- /dev/null +++ b/tests/pytest/test_splinegeo_tensordomainmeshing.py @@ -0,0 +1,22 @@ +from netgen.geom2d import * + +def test_tensordomainmeshing(): + geo = SplineGeometry() + w = 10 + h = 0.01 + + p = [ (0, 0), (w, 0), (w, h), (0, h) ] + p = [geo.AppendPoint(*px) for px in p] + + l0 = geo.Append ( ["line", p[0], p[1]], leftdomain=1, rightdomain=0 ) + l1 = geo.Append ( ["line", p[1], p[2]], leftdomain=1, rightdomain=0) + geo.Append ( ["line", p[3], p[2]], leftdomain=0, rightdomain=1, copy=l0 ) + geo.Append ( ["line", p[0], p[3]], leftdomain=0, rightdomain=1, copy=l1 ) + + geo._SetDomainTensorMeshing(1, True) + + mesh = geo.GenerateMesh(maxh=1) + + for el in mesh.Elements2D(): + print(el.vertices) + assert len(el.vertices) == 4 diff --git a/tests/pytest/test_tutorials.py b/tests/pytest/test_tutorials.py index 90bccc3f..236749da 100644 --- a/tests/pytest/test_tutorials.py +++ b/tests/pytest/test_tutorials.py @@ -63,12 +63,12 @@ def getMeshingparameters(filename): standard = [MeshingParameters()] + [MeshingParameters(ms) for ms in (meshsize.very_coarse, meshsize.coarse, meshsize.moderate, meshsize.fine, meshsize.very_fine)] if filename == "shell.geo": return [] # do not test this example cause it needs so long... - if filename == "extrusion.geo": - return [] # this segfaults right now if filename == "manyholes2.geo": return [standard[1]] # this gets too big for finer meshsizes if filename in ("manyholes.geo", "frame.step"): return standard[:3] # this gets too big for finer meshsizes + if filename == "extrusion.geo": + return standard[:-1] if filename == "screw.step": return standard[3:] # coarser meshes don't work here if filename == "cylsphere.geo": @@ -142,7 +142,11 @@ def generateResultFile(): continue meshdata = [] for mp in mps: - mesh = generateMesh(_file, mp) + try: + mesh = generateMesh(_file, mp) + except Exception as e: + print("Meshingparameters: ", mp) + raise e meshdata.append( getData(mesh, mp) ) data[_file] = meshdata print("needed", time.time() - start, "seconds") diff --git a/windows/.gitignore b/windows/.gitignore new file mode 100644 index 00000000..73a41fa4 --- /dev/null +++ b/windows/.gitignore @@ -0,0 +1 @@ +netgen.rc diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index e69de29b..e1ef0a93 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -0,0 +1,4 @@ +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/netgen.rc.template" + "${CMAKE_CURRENT_SOURCE_DIR}/netgen.rc" + IMMEDIATE @ONLY) diff --git a/windows/netgen.rc b/windows/netgen.rc.template similarity index 79% rename from windows/netgen.rc rename to windows/netgen.rc.template index 047feb6f..bbdab4a7 100644 --- a/windows/netgen.rc +++ b/windows/netgen.rc.template @@ -7,7 +7,7 @@ // // Generated from the TEXTINCLUDE 2 resource. // -#include "afxres.h" +#include ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS @@ -35,8 +35,8 @@ LANGUAGE LANG_GERMAN, SUBLANG_GERMAN // VS_VERSION_INFO VERSIONINFO - FILEVERSION 5,1,0,0 - PRODUCTVERSION 5,1,0,0 + FILEVERSION @NETGEN_VERSION_MAJOR@,@NETGEN_VERSION_MINOR@,@NETGEN_VERSION_PATCH@,@NETGEN_VERSION_TWEAK@ + PRODUCTVERSION @NETGEN_VERSION_MAJOR@,@NETGEN_VERSION_MINOR@,@NETGEN_VERSION_PATCH@,@NETGEN_VERSION_TWEAK@ FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x3L @@ -51,14 +51,14 @@ BEGIN BEGIN BLOCK "040904b0" BEGIN - VALUE "CompanyName", "Vienna UT" + VALUE "CompanyName", "TU Wien" VALUE "FileDescription", "Netgen Meshing Software" - VALUE "FileVersion", "5.1-dev" + VALUE "FileVersion", "@NETGEN_VERSION@" VALUE "InternalName", "Netgen" - VALUE "LegalCopyright", "GNU Public License (GPL)" + VALUE "LegalCopyright", "GNU Lesser General Public License (LGPL)" VALUE "OriginalFilename", "Netgen.exe" VALUE "ProductName", "Netgen" - VALUE "ProductVersion", "5.1-dev" + VALUE "ProductVersion", "@NETGEN_VERSION@" END END BLOCK "VarFileInfo"